demo.mod 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. MODULE demo ;
  2. (*
  3. m2-GTK4 example - a small "gtk-demo"-style browser.
  4. A searchable sidebar (GtkListView over a GtkFilterListModel with a
  5. Modula-2 GtkCustomFilter) drives a GtkStack of demo pages. Shows the
  6. infrastructure a full gtk-demo port would need.
  7. Build: make examples
  8. Run: ./build/examples/demo
  9. *)
  10. FROM Gio IMPORT g_application_run;
  11. FROM GtkApplication IMPORT gtk_application_new;
  12. FROM GtkWindow IMPORT gtk_application_window_new, gtk_window_set_title,
  13. gtk_window_set_default_size, gtk_window_set_child, gtk_window_present;
  14. FROM GtkBox IMPORT gtk_box_new, gtk_box_append, gtk_box_set_spacing;
  15. FROM GtkEnums IMPORT GtkVertical, GtkHorizontal;
  16. FROM GtkLabel IMPORT gtk_label_new, gtk_label_set_text;
  17. FROM GtkButton IMPORT gtk_button_new_with_label;
  18. FROM GtkListBox IMPORT gtk_list_box_new, gtk_list_box_append,
  19. gtk_list_box_row_new, gtk_list_box_row_set_child;
  20. FROM GtkSearchEntry IMPORT gtk_search_entry_new,
  21. gtk_search_entry_set_placeholder_text;
  22. FROM GtkEditable IMPORT gtk_editable_get_text;
  23. FROM GtkWidget IMPORT gtk_widget_set_size_request, gtk_widget_set_vexpand,
  24. gtk_widget_set_hexpand, gtk_widget_add_css_class,
  25. gtk_widget_set_margin_start, gtk_widget_set_margin_end,
  26. gtk_widget_set_margin_top, gtk_widget_set_margin_bottom;
  27. FROM GtkListView IMPORT gtk_list_view_new;
  28. FROM GtkListItem IMPORT gtk_list_item_get_item, gtk_list_item_get_child,
  29. gtk_list_item_set_child;
  30. FROM GtkSignalListItemFactory IMPORT gtk_signal_list_item_factory_new;
  31. FROM GtkSingleSelection IMPORT gtk_single_selection_new,
  32. gtk_single_selection_get_selected;
  33. FROM GtkFilterListModel IMPORT gtk_filter_list_model_new;
  34. FROM GtkCustomFilter IMPORT gtk_custom_filter_new,
  35. gtk_custom_filter_set_filter_func;
  36. FROM GtkStack IMPORT gtk_stack_new, gtk_stack_add_titled,
  37. gtk_stack_set_visible_child_name;
  38. FROM GtkDrawingArea IMPORT gtk_drawing_area_new,
  39. gtk_drawing_area_set_content_width, gtk_drawing_area_set_content_height,
  40. gtk_drawing_area_set_draw_func;
  41. FROM GtkCssProvider IMPORT gtk_css_provider_new,
  42. gtk_css_provider_load_from_string;
  43. FROM GtkStyleContext IMPORT gtk_style_context_add_provider_for_display,
  44. GtkStyleProviderPriorityApplication;
  45. FROM Gdk IMPORT gdk_display_get_default;
  46. FROM GListStore IMPORT g_list_store_new, g_list_store_append;
  47. FROM GListModel IMPORT g_list_model_get_item;
  48. FROM GtkStringObject IMPORT gtk_string_object_new,
  49. gtk_string_object_get_string, gtk_string_object_get_type;
  50. FROM Cairo IMPORT cairo_set_source_rgb, cairo_arc, cairo_fill,
  51. cairo_set_line_width, cairo_move_to, cairo_line_to, cairo_stroke;
  52. FROM GObject IMPORT g_object_unref;
  53. FROM GtkClosures IMPORT Connect2, Connect3;
  54. FROM GtkUtils IMPORT CStrToM2;
  55. FROM SYSTEM IMPORT ADDRESS, ADR;
  56. FROM libc IMPORT printf;
  57. CONST
  58. AppId = "org.example.m2gtk4.Demo";
  59. InvalidPosition = 0FFFFFFFFH;
  60. Css = ".demo-accent { color: #e01b24; font-weight: bold; }";
  61. TYPE
  62. DemoProc = PROCEDURE () : ADDRESS;
  63. Demo = RECORD
  64. title: ARRAY [0..31] OF CHAR;
  65. build: DemoProc;
  66. END;
  67. VAR
  68. stack, filter, filterModel, selection: ADDRESS;
  69. searchText: ARRAY [0..63] OF CHAR;
  70. (* --- a couple of demo pages --- *)
  71. PROCEDURE BuildWelcome () : ADDRESS;
  72. BEGIN
  73. RETURN gtk_label_new("Welcome to the m2-GTK4 demo browser.")
  74. END BuildWelcome;
  75. PROCEDURE BuildButtons () : ADDRESS;
  76. VAR box: ADDRESS;
  77. BEGIN
  78. box := gtk_box_new(GtkVertical, 6);
  79. gtk_box_append(box, gtk_button_new_with_label("Click me"));
  80. gtk_box_append(box, gtk_button_new_with_label("Me too"));
  81. RETURN box
  82. END BuildButtons;
  83. PROCEDURE BuildList () : ADDRESS;
  84. VAR box, row: ADDRESS; i: INTEGER;
  85. BEGIN
  86. box := gtk_list_box_new();
  87. FOR i := 1 TO 3 DO
  88. row := gtk_list_box_row_new();
  89. gtk_list_box_row_set_child(row, gtk_label_new("A list row"));
  90. gtk_list_box_append(box, row)
  91. END;
  92. RETURN box
  93. END BuildList;
  94. PROCEDURE BuildCSS () : ADDRESS;
  95. VAR label: ADDRESS;
  96. BEGIN
  97. label := gtk_label_new("Styled by CSS");
  98. gtk_widget_add_css_class(label, "demo-accent");
  99. RETURN label
  100. END BuildCSS;
  101. PROCEDURE DrawDemo (area: ADDRESS; cr: ADDRESS; width, height: INTEGER;
  102. data: ADDRESS);
  103. BEGIN
  104. cairo_set_source_rgb(cr, 0.20, 0.50, 0.90);
  105. cairo_arc(cr, 80.0, 80.0, 50.0, 0.0, 6.2831853);
  106. cairo_fill(cr);
  107. cairo_set_source_rgb(cr, 0.90, 0.20, 0.20);
  108. cairo_set_line_width(cr, 6.0);
  109. cairo_move_to(cr, 150.0, 40.0);
  110. cairo_line_to(cr, 280.0, 120.0);
  111. cairo_stroke(cr)
  112. END DrawDemo;
  113. PROCEDURE BuildDrawing () : ADDRESS;
  114. VAR area: ADDRESS;
  115. BEGIN
  116. area := gtk_drawing_area_new();
  117. gtk_drawing_area_set_content_width(area, 300);
  118. gtk_drawing_area_set_content_height(area, 160);
  119. gtk_drawing_area_set_draw_func(area, ADR(DrawDemo), NIL, NIL);
  120. RETURN area
  121. END BuildDrawing;
  122. (* --- search filter --- *)
  123. PROCEDURE Contains (hay, needle: ARRAY OF CHAR) : BOOLEAN;
  124. VAR
  125. i, j, hl, nl: CARDINAL;
  126. found: BOOLEAN;
  127. BEGIN
  128. hl := 0;
  129. WHILE (hl <= HIGH(hay)) AND (hay[hl] # 0C) DO INC(hl) END;
  130. nl := 0;
  131. WHILE (nl <= HIGH(needle)) AND (needle[nl] # 0C) DO INC(nl) END;
  132. IF nl = 0 THEN RETURN TRUE END;
  133. IF nl > hl THEN RETURN FALSE END;
  134. i := 0;
  135. WHILE i + nl <= hl DO
  136. found := TRUE;
  137. j := 0;
  138. WHILE j < nl DO
  139. IF hay[i + j] # needle[j] THEN found := FALSE END;
  140. INC(j)
  141. END;
  142. IF found THEN RETURN TRUE END;
  143. INC(i)
  144. END;
  145. RETURN FALSE
  146. END Contains;
  147. PROCEDURE Match (item: ADDRESS; user: ADDRESS) : INTEGER;
  148. VAR
  149. buf: ARRAY [0..63] OF CHAR;
  150. BEGIN
  151. CStrToM2(gtk_string_object_get_string(item), buf);
  152. IF Contains(buf, searchText) THEN RETURN 1 ELSE RETURN 0 END
  153. END Match;
  154. PROCEDURE OnSearchChanged (entry: ADDRESS; user: ADDRESS);
  155. BEGIN
  156. CStrToM2(gtk_editable_get_text(entry), searchText);
  157. gtk_custom_filter_set_filter_func(filter, ADR(Match), NIL, NIL)
  158. END OnSearchChanged;
  159. PROCEDURE OnSelected (model: ADDRESS; pspec: ADDRESS; user: ADDRESS);
  160. VAR
  161. index: CARDINAL;
  162. item: ADDRESS;
  163. name: ARRAY [0..31] OF CHAR;
  164. BEGIN
  165. index := gtk_single_selection_get_selected(selection);
  166. IF index = InvalidPosition THEN RETURN END;
  167. item := g_list_model_get_item(filterModel, index);
  168. CStrToM2(gtk_string_object_get_string(item), name);
  169. g_object_unref(item);
  170. gtk_stack_set_visible_child_name(stack, name)
  171. END OnSelected;
  172. (* --- list factory --- *)
  173. PROCEDURE RowSetup (factory: ADDRESS; item: ADDRESS; user: ADDRESS);
  174. BEGIN
  175. gtk_list_item_set_child(item, gtk_label_new(""))
  176. END RowSetup;
  177. PROCEDURE RowBind (factory: ADDRESS; item: ADDRESS; user: ADDRESS);
  178. VAR
  179. obj, child: ADDRESS;
  180. buf: ARRAY [0..63] OF CHAR;
  181. BEGIN
  182. obj := gtk_list_item_get_item(item);
  183. CStrToM2(gtk_string_object_get_string(obj), buf);
  184. child := gtk_list_item_get_child(item);
  185. gtk_label_set_text(child, buf)
  186. END RowBind;
  187. PROCEDURE OnActivate (application: ADDRESS; data: ADDRESS);
  188. VAR
  189. window, root, content, search, listView, factory, store, provider: ADDRESS;
  190. demos: ARRAY [0..4] OF Demo;
  191. i: INTEGER;
  192. BEGIN
  193. searchText[0] := 0C;
  194. provider := gtk_css_provider_new();
  195. gtk_css_provider_load_from_string(provider, Css);
  196. gtk_style_context_add_provider_for_display(gdk_display_get_default(),
  197. provider, GtkStyleProviderPriorityApplication);
  198. demos[0].title := "Welcome"; demos[0].build := BuildWelcome;
  199. demos[1].title := "Buttons"; demos[1].build := BuildButtons;
  200. demos[2].title := "List"; demos[2].build := BuildList;
  201. demos[3].title := "CSS"; demos[3].build := BuildCSS;
  202. demos[4].title := "Drawing"; demos[4].build := BuildDrawing;
  203. store := g_list_store_new(gtk_string_object_get_type());
  204. stack := gtk_stack_new();
  205. FOR i := 0 TO 4 DO
  206. gtk_stack_add_titled(stack, demos[i].build(),
  207. demos[i].title, demos[i].title);
  208. g_list_store_append(store, gtk_string_object_new(demos[i].title))
  209. END;
  210. filter := gtk_custom_filter_new(ADR(Match), NIL, NIL);
  211. filterModel := gtk_filter_list_model_new(store, filter);
  212. selection := gtk_single_selection_new(filterModel);
  213. Connect3(selection, "notify::selected", OnSelected, NIL);
  214. factory := gtk_signal_list_item_factory_new();
  215. Connect3(factory, "setup", RowSetup, NIL);
  216. Connect3(factory, "bind", RowBind, NIL);
  217. listView := gtk_list_view_new(selection, factory);
  218. gtk_widget_set_size_request(listView, 180, -1);
  219. gtk_widget_set_vexpand(listView, 1);
  220. search := gtk_search_entry_new();
  221. gtk_search_entry_set_placeholder_text(search, "Filter demos");
  222. Connect2(search, "search-changed", OnSearchChanged, NIL);
  223. content := gtk_box_new(GtkHorizontal, 0);
  224. gtk_widget_set_vexpand(content, 1);
  225. gtk_box_append(content, listView);
  226. gtk_box_append(content, stack);
  227. gtk_widget_set_hexpand(stack, 1);
  228. root := gtk_box_new(GtkVertical, 6);
  229. gtk_box_set_spacing(root, 6);
  230. gtk_widget_set_margin_top(root, 8);
  231. gtk_widget_set_margin_bottom(root, 8);
  232. gtk_widget_set_margin_start(root, 8);
  233. gtk_widget_set_margin_end(root, 8);
  234. gtk_box_append(root, search);
  235. gtk_box_append(root, content);
  236. window := gtk_application_window_new(application);
  237. gtk_window_set_title(window, "m2-GTK4 Demo");
  238. gtk_window_set_default_size(window, 640, 420);
  239. gtk_window_set_child(window, root);
  240. gtk_stack_set_visible_child_name(stack, "Welcome");
  241. gtk_window_present(window)
  242. END OnActivate;
  243. VAR
  244. app: ADDRESS;
  245. BEGIN
  246. app := gtk_application_new(AppId, 0);
  247. IF app = NIL THEN
  248. printf("gtk_application_new failed\n");
  249. HALT(1)
  250. END;
  251. Connect2(app, "activate", OnActivate, NIL);
  252. g_application_run(app, 0, NIL)
  253. END demo.