m2gtkdemo.mod 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. MODULE m2gtkdemo ;
  2. (*
  3. m2-GTK4 - an m2 gtk-demo.
  4. A browser in the spirit of GTK's own gtk4-demo: a searchable sidebar
  5. listing every demo in DemoCatalog, and an info pane describing the
  6. selected demo. Activating a demo (double-click / Enter) or pressing
  7. "Run" opens it in its own window; demos that have not been ported yet
  8. are shown as such.
  9. Build: make demos
  10. Run: ./build/demos/m2gtkdemo
  11. *)
  12. FROM Gio IMPORT g_application_run;
  13. FROM GtkApplication IMPORT gtk_application_new;
  14. FROM GtkApplicationWindow IMPORT gtk_application_window_new;
  15. FROM GtkWindow IMPORT gtk_window_set_title, gtk_window_set_default_size,
  16. gtk_window_set_child, gtk_window_present;
  17. FROM GtkBox IMPORT gtk_box_new, gtk_box_append, gtk_box_set_spacing;
  18. FROM GtkPaned IMPORT gtk_paned_new, gtk_paned_set_start_child,
  19. gtk_paned_set_end_child, gtk_paned_set_position;
  20. FROM GtkEnums IMPORT GtkVertical, GtkHorizontal;
  21. FROM GtkWidget IMPORT gtk_widget_set_size_request, gtk_widget_set_vexpand,
  22. gtk_widget_set_hexpand, gtk_widget_set_margin_start,
  23. gtk_widget_set_margin_end, gtk_widget_set_margin_top,
  24. gtk_widget_set_margin_bottom;
  25. FROM GtkLabel IMPORT gtk_label_new, gtk_label_set_text,
  26. gtk_label_set_wrap, gtk_label_set_max_width_chars;
  27. FROM GtkButton IMPORT gtk_button_new_with_label;
  28. FROM GtkSearchEntry IMPORT gtk_search_entry_new,
  29. gtk_search_entry_set_placeholder_text;
  30. FROM GtkEditable IMPORT gtk_editable_get_text;
  31. FROM GtkScrolledWindow IMPORT gtk_scrolled_window_new,
  32. gtk_scrolled_window_set_child;
  33. FROM GtkListView IMPORT gtk_list_view_new;
  34. FROM GtkListItem IMPORT gtk_list_item_get_item, gtk_list_item_get_child,
  35. gtk_list_item_set_child;
  36. FROM GtkSignalListItemFactory IMPORT gtk_signal_list_item_factory_new;
  37. FROM GtkSingleSelection IMPORT gtk_single_selection_new,
  38. gtk_single_selection_get_selected;
  39. FROM GtkFilterListModel IMPORT gtk_filter_list_model_new;
  40. FROM GtkCustomFilter IMPORT gtk_custom_filter_new,
  41. gtk_custom_filter_set_filter_func;
  42. FROM GtkStringList IMPORT gtk_string_list_new, gtk_string_list_append;
  43. FROM GtkStringObject IMPORT gtk_string_object_get_string;
  44. FROM GListModel IMPORT g_list_model_get_item;
  45. FROM GObject IMPORT g_signal_connect_data, g_object_unref, GConnectDefault;
  46. FROM GtkClosures IMPORT Connect2, Connect3;
  47. FROM GtkUtils IMPORT Connect, CStrToM2;
  48. FROM GtkDemo IMPORT BuildByName, IsPorted;
  49. FROM DemoCatalog IMPORT DemoCatalogCount, DemoCatalogGet;
  50. FROM SYSTEM IMPORT ADDRESS, ADR;
  51. FROM libc IMPORT printf;
  52. CONST
  53. AppId = "org.example.m2gtk4.Demo4";
  54. Invalid = 0FFFFFFFFH;
  55. VAR
  56. window, filter, filterModel, selection, infoTitle, infoBody, infoStatus,
  57. searchEntry: ADDRESS;
  58. query: ARRAY [0..63] OF CHAR;
  59. PROCEDURE Contains (hay, needle: ARRAY OF CHAR) : BOOLEAN;
  60. VAR
  61. i, j, hl, nl: CARDINAL;
  62. found: BOOLEAN;
  63. BEGIN
  64. hl := 0;
  65. WHILE (hl <= HIGH(hay)) AND (hay[hl] # 0C) DO INC(hl) END;
  66. nl := 0;
  67. WHILE (nl <= HIGH(needle)) AND (needle[nl] # 0C) DO INC(nl) END;
  68. IF nl = 0 THEN RETURN TRUE END;
  69. IF nl > hl THEN RETURN FALSE END;
  70. i := 0;
  71. WHILE i + nl <= hl DO
  72. found := TRUE;
  73. j := 0;
  74. WHILE j < nl DO
  75. IF hay[i + j] # needle[j] THEN found := FALSE END;
  76. INC(j)
  77. END;
  78. IF found THEN RETURN TRUE END;
  79. INC(i)
  80. END;
  81. RETURN FALSE
  82. END Contains;
  83. PROCEDURE CopyStr (VAR dst: ARRAY OF CHAR; src: ARRAY OF CHAR);
  84. VAR
  85. i: CARDINAL;
  86. BEGIN
  87. i := 0;
  88. WHILE (i <= HIGH(dst)) AND (i <= HIGH(src)) AND (src[i] # 0C) DO
  89. dst[i] := src[i];
  90. INC(i)
  91. END;
  92. IF i <= HIGH(dst) THEN dst[i] := 0C END
  93. END CopyStr;
  94. PROCEDURE LookupByName (name: ARRAY OF CHAR;
  95. VAR title, keywords, description: ARRAY OF CHAR);
  96. VAR
  97. i: CARDINAL;
  98. n, t, k, d: ARRAY [0..511] OF CHAR;
  99. BEGIN
  100. i := 0;
  101. WHILE i < DemoCatalogCount() DO
  102. DemoCatalogGet(i, n, t, k, d);
  103. IF Contains(n, name) AND Contains(name, n) THEN
  104. CopyStr(title, t); CopyStr(keywords, k); CopyStr(description, d);
  105. RETURN
  106. END;
  107. INC(i)
  108. END;
  109. title[0] := 0C; keywords[0] := 0C; description[0] := 0C
  110. END LookupByName;
  111. (* gboolean match(gpointer item, gpointer user) - item is a demo name *)
  112. PROCEDURE Match (item: ADDRESS; user: ADDRESS) : INTEGER;
  113. VAR
  114. name, title, keywords, description: ARRAY [0..511] OF CHAR;
  115. BEGIN
  116. CStrToM2(gtk_string_object_get_string(item), name);
  117. LookupByName(name, title, keywords, description);
  118. IF Contains(title, query) OR Contains(keywords, query) THEN
  119. RETURN 1
  120. ELSE
  121. RETURN 0
  122. END
  123. END Match;
  124. PROCEDURE UpdateInfo (name: ARRAY OF CHAR);
  125. VAR
  126. title, keywords, description: ARRAY [0..1023] OF CHAR;
  127. BEGIN
  128. LookupByName(name, title, keywords, description);
  129. gtk_label_set_text(infoTitle, title);
  130. gtk_label_set_text(infoBody, description);
  131. IF IsPorted(name) THEN
  132. gtk_label_set_text(infoStatus, "Ported to Modula-2. Press Run.")
  133. ELSE
  134. gtk_label_set_text(infoStatus, "Not ported yet.")
  135. END
  136. END UpdateInfo;
  137. PROCEDURE NameAtSelection (VAR name: ARRAY OF CHAR);
  138. VAR
  139. index: CARDINAL;
  140. item: ADDRESS;
  141. BEGIN
  142. name[0] := 0C;
  143. index := gtk_single_selection_get_selected(selection);
  144. IF index # Invalid THEN
  145. item := g_list_model_get_item(filterModel, index);
  146. CStrToM2(gtk_string_object_get_string(item), name);
  147. g_object_unref(item)
  148. END
  149. END NameAtSelection;
  150. PROCEDURE RunSelected;
  151. VAR
  152. name: ARRAY [0..511] OF CHAR;
  153. demoWindow: ADDRESS;
  154. BEGIN
  155. NameAtSelection(name);
  156. IF name[0] # 0C THEN
  157. UpdateInfo(name);
  158. IF IsPorted(name) THEN
  159. demoWindow := BuildByName(name, window);
  160. IF demoWindow = NIL THEN
  161. printf("demo %s could not be built\n", name)
  162. END
  163. END
  164. END
  165. END RunSelected;
  166. (* void activate(GtkListView *list, guint position, gpointer user) *)
  167. PROCEDURE OnActivate (list: ADDRESS; position: CARDINAL; user: ADDRESS);
  168. VAR
  169. item, demoWindow: ADDRESS;
  170. name: ARRAY [0..511] OF CHAR;
  171. BEGIN
  172. item := g_list_model_get_item(filterModel, position);
  173. CStrToM2(gtk_string_object_get_string(item), name);
  174. g_object_unref(item);
  175. UpdateInfo(name);
  176. IF IsPorted(name) THEN
  177. demoWindow := BuildByName(name, window)
  178. END
  179. END OnActivate;
  180. PROCEDURE OnRun (button: ADDRESS; user: ADDRESS);
  181. BEGIN
  182. RunSelected
  183. END OnRun;
  184. PROCEDURE OnSelectionChanged (sel: ADDRESS; user: ADDRESS);
  185. BEGIN
  186. RunSelected
  187. END OnSelectionChanged;
  188. PROCEDURE OnSearchChanged (entry: ADDRESS; user: ADDRESS);
  189. BEGIN
  190. CStrToM2(gtk_editable_get_text(entry), query);
  191. gtk_custom_filter_set_filter_func(filter, ADR(Match), NIL, NIL)
  192. END OnSearchChanged;
  193. PROCEDURE RowSetup (factory: ADDRESS; item: ADDRESS; user: ADDRESS);
  194. BEGIN
  195. gtk_list_item_set_child(item, gtk_label_new(""))
  196. END RowSetup;
  197. PROCEDURE RowBind (factory: ADDRESS; item: ADDRESS; user: ADDRESS);
  198. VAR
  199. obj, child: ADDRESS;
  200. name, title, keywords, description: ARRAY [0..511] OF CHAR;
  201. BEGIN
  202. obj := gtk_list_item_get_item(item);
  203. CStrToM2(gtk_string_object_get_string(obj), name);
  204. LookupByName(name, title, keywords, description);
  205. child := gtk_list_item_get_child(item);
  206. gtk_label_set_text(child, title)
  207. END RowBind;
  208. PROCEDURE OnActivateApp (application: ADDRESS; data: ADDRESS);
  209. VAR
  210. root, paned, sidebar, listView, factory, store, runButton, info: ADDRESS;
  211. name, title, keywords, description: ARRAY [0..511] OF CHAR;
  212. i: CARDINAL;
  213. BEGIN
  214. query[0] := 0C;
  215. store := gtk_string_list_new(NIL);
  216. i := 0;
  217. WHILE i < DemoCatalogCount() DO
  218. DemoCatalogGet(i, name, title, keywords, description);
  219. gtk_string_list_append(store, name);
  220. INC(i)
  221. END;
  222. filter := gtk_custom_filter_new(ADR(Match), NIL, NIL);
  223. filterModel := gtk_filter_list_model_new(store, filter);
  224. selection := gtk_single_selection_new(filterModel);
  225. factory := gtk_signal_list_item_factory_new();
  226. Connect3(factory, "setup", RowSetup, NIL);
  227. Connect3(factory, "bind", RowBind, NIL);
  228. listView := gtk_list_view_new(selection, factory);
  229. gtk_widget_set_vexpand(listView, 1);
  230. g_signal_connect_data(listView, "activate", ADR(OnActivate), NIL, NIL,
  231. GConnectDefault);
  232. Connect2(selection, "notify::selected", OnSelectionChanged, NIL);
  233. searchEntry := gtk_search_entry_new();
  234. gtk_search_entry_set_placeholder_text(searchEntry, "Search demos");
  235. Connect2(searchEntry, "search-changed", OnSearchChanged, NIL);
  236. sidebar := gtk_box_new(GtkVertical, 6);
  237. gtk_box_set_spacing(sidebar, 6);
  238. gtk_box_append(sidebar, searchEntry);
  239. gtk_box_append(sidebar, listView);
  240. gtk_widget_set_size_request(sidebar, 240, -1);
  241. infoTitle := gtk_label_new("");
  242. gtk_label_set_wrap(infoTitle, 1);
  243. gtk_label_set_max_width_chars(infoTitle, 40);
  244. gtk_widget_set_margin_start(infoTitle, 12);
  245. gtk_widget_set_margin_end(infoTitle, 12);
  246. gtk_widget_set_margin_top(infoTitle, 12);
  247. infoBody := gtk_label_new("");
  248. gtk_label_set_wrap(infoBody, 1);
  249. gtk_label_set_max_width_chars(infoBody, 50);
  250. gtk_widget_set_margin_start(infoBody, 12);
  251. gtk_widget_set_margin_end(infoBody, 12);
  252. infoStatus := gtk_label_new("");
  253. gtk_label_set_wrap(infoStatus, 1);
  254. gtk_widget_set_margin_start(infoStatus, 12);
  255. gtk_widget_set_margin_end(infoStatus, 12);
  256. runButton := gtk_button_new_with_label("Run selected demo");
  257. gtk_widget_set_margin_start(runButton, 12);
  258. gtk_widget_set_margin_end(runButton, 12);
  259. gtk_widget_set_margin_bottom(runButton, 12);
  260. Connect2(runButton, "clicked", OnRun, NIL);
  261. info := gtk_box_new(GtkVertical, 10);
  262. gtk_box_set_spacing(info, 10);
  263. gtk_box_append(info, infoTitle);
  264. gtk_box_append(info, infoBody);
  265. gtk_box_append(info, infoStatus);
  266. gtk_box_append(info, runButton);
  267. paned := gtk_paned_new(GtkHorizontal);
  268. gtk_paned_set_start_child(paned, sidebar);
  269. gtk_paned_set_end_child(paned, info);
  270. gtk_paned_set_position(paned, 260);
  271. root := gtk_box_new(GtkVertical, 0);
  272. gtk_box_append(root, paned);
  273. window := gtk_application_window_new(application);
  274. gtk_window_set_title(window, "m2-GTK4 Demo");
  275. gtk_window_set_default_size(window, 760, 520);
  276. gtk_window_set_child(window, root);
  277. gtk_window_present(window)
  278. END OnActivateApp;
  279. VAR
  280. app: ADDRESS;
  281. BEGIN
  282. app := gtk_application_new(AppId, 0);
  283. IF app = NIL THEN
  284. printf("gtk_application_new failed\n");
  285. HALT(1)
  286. END;
  287. Connect2(app, "activate", OnActivateApp, NIL);
  288. g_application_run(app, 0, NIL)
  289. END m2gtkdemo.