DemosInput.mod 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. IMPLEMENTATION MODULE DemosInput ;
  2. (*
  3. See the GTK C originals in gtk/demos/gtk-demo:
  4. password_entry.c, search_entry.c, tagged_entry.c, shortcut_triggers.c
  5. Only the bindings under src/ are used, so this module builds with just
  6. -Isrc -Idemos (see also demos/DemosText.mod). A few C facilities used
  7. by the originals are not bound; those spots are simplified and called
  8. out in comments:
  9. * GtkPasswordEntry is not bound, so the two password fields use a
  10. GtkEntry with visibility switched off (still read via GtkEditable).
  11. * gtk_search_bar_set_key_capture_widget / g_object_bind_property are
  12. not bound; the header toggle drives the search bar through its
  13. "toggled" signal instead.
  14. * the demo-private DemoTaggedEntry composite is not a GTK widget, so
  15. the tagged entry is approximated with a GtkBox holding an entry,
  16. tag labels and a spinner.
  17. * gtk_callback_action_new is not bound; the shortcut rows are
  18. GtkButtons and the parsed "signal(clicked)" action activates them.
  19. *)
  20. FROM SYSTEM IMPORT ADDRESS, ADR;
  21. FROM GtkWindow IMPORT gtk_window_new, gtk_window_set_title,
  22. gtk_window_set_default_size, gtk_window_set_resizable,
  23. gtk_window_set_titlebar, gtk_window_set_child, gtk_window_destroy;
  24. FROM GtkWidget IMPORT gtk_widget_set_visible, gtk_widget_get_visible,
  25. gtk_widget_set_margin_start, gtk_widget_set_margin_end,
  26. gtk_widget_set_margin_top, gtk_widget_set_margin_bottom,
  27. gtk_widget_set_sensitive, gtk_widget_set_halign,
  28. gtk_widget_set_size_request, gtk_widget_add_css_class,
  29. gtk_widget_add_controller;
  30. FROM GtkEnums IMPORT GtkVertical, GtkHorizontal, GtkAlignCenter, GtkAlignEnd;
  31. FROM GtkBox IMPORT gtk_box_new, gtk_box_append, gtk_box_remove;
  32. FROM GtkHeaderBar IMPORT gtk_header_bar_new,
  33. gtk_header_bar_set_show_title_buttons, gtk_header_bar_pack_end;
  34. FROM GtkEntry IMPORT gtk_entry_new, gtk_entry_set_placeholder_text;
  35. FROM GtkPasswordEntry IMPORT gtk_password_entry_new;
  36. FROM GtkEditable IMPORT gtk_editable_get_text;
  37. FROM GtkSearchEntry IMPORT gtk_search_entry_new;
  38. FROM GtkSearchBar IMPORT gtk_search_bar_new, gtk_search_bar_connect_entry,
  39. gtk_search_bar_set_show_close_button, gtk_search_bar_set_child,
  40. gtk_search_bar_set_search_mode;
  41. FROM GtkToggleButton IMPORT gtk_toggle_button_new_with_label,
  42. gtk_toggle_button_get_active;
  43. FROM GtkButton IMPORT gtk_button_new_with_label,
  44. gtk_button_new_with_mnemonic, gtk_button_get_label;
  45. FROM GtkLabel IMPORT gtk_label_new, gtk_label_set_text;
  46. FROM GtkCheckButton IMPORT gtk_check_button_new_with_mnemonic;
  47. FROM GtkSpinner IMPORT gtk_spinner_new, gtk_spinner_start;
  48. FROM GtkListBox IMPORT gtk_list_box_new, gtk_list_box_insert;
  49. FROM GtkShortcut IMPORT gtk_shortcut_new,
  50. gtk_shortcut_trigger_parse_string, gtk_shortcut_action_parse_string;
  51. FROM GtkShortcutController IMPORT gtk_shortcut_controller_new,
  52. gtk_shortcut_controller_set_scope, gtk_shortcut_controller_add_shortcut,
  53. GtkShortcutScopeGlobal;
  54. FROM GObject IMPORT g_signal_connect_data, GConnectDefault;
  55. FROM GLib IMPORT g_strcmp0;
  56. FROM libc IMPORT printf, strncpy;
  57. (* local stand-in for GtkUtils.CStrToM2 (lib/ is not on the include path
  58. for these demos): copy a borrowed C string into a Modula-2 buffer. *)
  59. PROCEDURE CopyCStr (src: ADDRESS; VAR dst: ARRAY OF CHAR);
  60. BEGIN
  61. IF src = NIL THEN
  62. dst[0] := 0C
  63. ELSE
  64. strncpy(ADR(dst), src, HIGH(dst) + 1);
  65. dst[HIGH(dst)] := 0C
  66. END
  67. END CopyCStr;
  68. (* ------------------------------------------------------------------ *)
  69. (* Password Entry *)
  70. (* ------------------------------------------------------------------ *)
  71. VAR
  72. pwWindow, pwEntry, pwEntry2, pwButton: ADDRESS;
  73. (* void update_button(GObject *object, GParamSpec *pspec, gpointer data) *)
  74. PROCEDURE OnUpdateButton (object: ADDRESS; pspec: ADDRESS; data: ADDRESS);
  75. VAR
  76. text, text2: ADDRESS;
  77. buf: ARRAY [0..255] OF CHAR;
  78. BEGIN
  79. text := gtk_editable_get_text(pwEntry);
  80. text2 := gtk_editable_get_text(pwEntry2);
  81. CopyCStr(text, buf);
  82. IF (buf[0] # 0C) AND (g_strcmp0(text, text2) = 0) THEN
  83. gtk_widget_set_sensitive(pwButton, 1)
  84. ELSE
  85. gtk_widget_set_sensitive(pwButton, 0)
  86. END
  87. END OnUpdateButton;
  88. (* void button_pressed(GtkButton *widget, GtkWidget *window) *)
  89. PROCEDURE OnDone (button: ADDRESS; data: ADDRESS);
  90. BEGIN
  91. gtk_window_destroy(pwWindow);
  92. pwWindow := NIL
  93. END OnDone;
  94. PROCEDURE PasswordField (placeholder: ARRAY OF CHAR) : ADDRESS;
  95. VAR
  96. entry: ADDRESS;
  97. BEGIN
  98. entry := gtk_password_entry_new();
  99. g_signal_connect_data(entry, "notify::text", ADR(OnUpdateButton),
  100. NIL, NIL, GConnectDefault);
  101. RETURN entry
  102. END PasswordField;
  103. PROCEDURE DoPasswordEntry (doWidget: ADDRESS) : ADDRESS;
  104. VAR
  105. box, header: ADDRESS;
  106. BEGIN
  107. IF pwWindow = NIL THEN
  108. pwWindow := gtk_window_new();
  109. gtk_window_set_title(pwWindow, "Choose a Password");
  110. gtk_window_set_resizable(pwWindow, 0);
  111. header := gtk_header_bar_new();
  112. gtk_header_bar_set_show_title_buttons(header, 0);
  113. gtk_window_set_titlebar(pwWindow, header);
  114. box := gtk_box_new(GtkVertical, 6);
  115. gtk_widget_set_margin_start(box, 18);
  116. gtk_widget_set_margin_end(box, 18);
  117. gtk_widget_set_margin_top(box, 18);
  118. gtk_widget_set_margin_bottom(box, 18);
  119. gtk_window_set_child(pwWindow, box);
  120. pwEntry := PasswordField("Password");
  121. gtk_box_append(box, pwEntry);
  122. pwEntry2 := PasswordField("Confirm");
  123. gtk_box_append(box, pwEntry2);
  124. pwButton := gtk_button_new_with_mnemonic("_Done");
  125. gtk_widget_add_css_class(pwButton, "suggested-action");
  126. g_signal_connect_data(pwButton, "clicked", ADR(OnDone),
  127. NIL, NIL, GConnectDefault);
  128. gtk_widget_set_sensitive(pwButton, 0);
  129. gtk_header_bar_pack_end(header, pwButton)
  130. END;
  131. IF gtk_widget_get_visible(pwWindow) = 0 THEN
  132. gtk_widget_set_visible(pwWindow, 1)
  133. ELSE
  134. gtk_window_destroy(pwWindow);
  135. pwWindow := NIL
  136. END;
  137. RETURN pwWindow
  138. END DoPasswordEntry;
  139. (* ------------------------------------------------------------------ *)
  140. (* Search Entry *)
  141. (* ------------------------------------------------------------------ *)
  142. VAR
  143. searchWindow, searchBar: ADDRESS;
  144. (* void search_changed_cb(GtkSearchEntry *entry, GtkLabel *result_label) *)
  145. PROCEDURE OnSearchChanged (entry: ADDRESS; resultLabel: ADDRESS);
  146. VAR
  147. text: ADDRESS;
  148. buf: ARRAY [0..255] OF CHAR;
  149. BEGIN
  150. text := gtk_editable_get_text(entry);
  151. CopyCStr(text, buf);
  152. gtk_label_set_text(resultLabel, buf)
  153. END OnSearchChanged;
  154. (* The C demo binds the header toggle's "active" to the search bar's
  155. "search-mode-enabled"; g_object_bind_property is not bound here, so
  156. the same effect is wired through the "toggled" signal. *)
  157. PROCEDURE OnSearchToggled (button: ADDRESS; data: ADDRESS);
  158. BEGIN
  159. IF gtk_toggle_button_get_active(button) # 0 THEN
  160. gtk_search_bar_set_search_mode(searchBar, 1)
  161. ELSE
  162. gtk_search_bar_set_search_mode(searchBar, 0)
  163. END
  164. END OnSearchToggled;
  165. PROCEDURE DoSearchEntry (doWidget: ADDRESS) : ADDRESS;
  166. VAR
  167. vbox, hbox, box, label, entry, button, header: ADDRESS;
  168. BEGIN
  169. IF searchWindow = NIL THEN
  170. searchWindow := gtk_window_new();
  171. gtk_window_set_title(searchWindow, "Type to Search");
  172. gtk_window_set_resizable(searchWindow, 0);
  173. gtk_widget_set_size_request(searchWindow, 200, -1);
  174. header := gtk_header_bar_new();
  175. gtk_window_set_titlebar(searchWindow, header);
  176. vbox := gtk_box_new(GtkVertical, 0);
  177. gtk_window_set_child(searchWindow, vbox);
  178. entry := gtk_search_entry_new();
  179. gtk_widget_set_halign(entry, GtkAlignCenter);
  180. searchBar := gtk_search_bar_new();
  181. gtk_search_bar_connect_entry(searchBar, entry);
  182. gtk_search_bar_set_show_close_button(searchBar, 0);
  183. gtk_search_bar_set_child(searchBar, entry);
  184. gtk_box_append(vbox, searchBar);
  185. (* gtk_search_bar_set_key_capture_widget is not bound; the search
  186. bar is opened via the header toggle below instead. *)
  187. box := gtk_box_new(GtkVertical, 18);
  188. gtk_widget_set_margin_start(box, 18);
  189. gtk_widget_set_margin_end(box, 18);
  190. gtk_widget_set_margin_top(box, 18);
  191. gtk_widget_set_margin_bottom(box, 18);
  192. gtk_box_append(vbox, box);
  193. button := gtk_toggle_button_new_with_label("Search");
  194. g_signal_connect_data(button, "toggled", ADR(OnSearchToggled),
  195. NIL, NIL, GConnectDefault);
  196. gtk_header_bar_pack_end(header, button);
  197. hbox := gtk_box_new(GtkHorizontal, 10);
  198. gtk_box_append(box, hbox);
  199. label := gtk_label_new("Searching for:");
  200. gtk_box_append(hbox, label);
  201. label := gtk_label_new("");
  202. gtk_box_append(hbox, label);
  203. g_signal_connect_data(entry, "search-changed", ADR(OnSearchChanged),
  204. label, NIL, GConnectDefault)
  205. END;
  206. IF gtk_widget_get_visible(searchWindow) = 0 THEN
  207. gtk_widget_set_visible(searchWindow, 1)
  208. ELSE
  209. gtk_window_destroy(searchWindow);
  210. searchWindow := NIL
  211. END;
  212. RETURN searchWindow
  213. END DoSearchEntry;
  214. (* ------------------------------------------------------------------ *)
  215. (* Tagged Entry *)
  216. (* ------------------------------------------------------------------ *)
  217. (* The demo's DemoTaggedEntry composite is not bound; this approximates
  218. it with a horizontal GtkBox that holds the entry, tag labels and an
  219. optional spinner. *)
  220. VAR
  221. taggedWindow, tagBox, tagSpinner: ADDRESS;
  222. (* void add_tag(GtkButton *button, DemoTaggedEntry *entry) *)
  223. PROCEDURE OnAddTag (button: ADDRESS; data: ADDRESS);
  224. VAR
  225. tag: ADDRESS;
  226. BEGIN
  227. tag := gtk_label_new("Blue");
  228. gtk_widget_add_css_class(tag, "blue");
  229. gtk_box_append(tagBox, tag)
  230. END OnAddTag;
  231. (* void toggle_spinner(GtkCheckButton *button, DemoTaggedEntry *entry) *)
  232. PROCEDURE OnToggleSpinner (button: ADDRESS; data: ADDRESS);
  233. BEGIN
  234. IF tagSpinner = NIL THEN
  235. tagSpinner := gtk_spinner_new();
  236. gtk_spinner_start(tagSpinner);
  237. gtk_box_append(tagBox, tagSpinner)
  238. ELSE
  239. gtk_box_remove(tagBox, tagSpinner);
  240. tagSpinner := NIL
  241. END
  242. END OnToggleSpinner;
  243. PROCEDURE DoTaggedEntry (doWidget: ADDRESS) : ADDRESS;
  244. VAR
  245. box, box2, entry, button: ADDRESS;
  246. BEGIN
  247. IF taggedWindow = NIL THEN
  248. taggedWindow := gtk_window_new();
  249. gtk_window_set_title(taggedWindow, "Tagged Entry");
  250. gtk_window_set_default_size(taggedWindow, 260, -1);
  251. gtk_window_set_resizable(taggedWindow, 0);
  252. box := gtk_box_new(GtkVertical, 6);
  253. gtk_widget_set_margin_start(box, 18);
  254. gtk_widget_set_margin_end(box, 18);
  255. gtk_widget_set_margin_top(box, 18);
  256. gtk_widget_set_margin_bottom(box, 18);
  257. gtk_window_set_child(taggedWindow, box);
  258. tagBox := gtk_box_new(GtkHorizontal, 6);
  259. gtk_box_append(box, tagBox);
  260. entry := gtk_entry_new();
  261. gtk_box_append(tagBox, entry);
  262. box2 := gtk_box_new(GtkHorizontal, 6);
  263. gtk_widget_set_halign(box2, GtkAlignEnd);
  264. gtk_box_append(box, box2);
  265. button := gtk_button_new_with_mnemonic("Add _Tag");
  266. g_signal_connect_data(button, "clicked", ADR(OnAddTag),
  267. entry, NIL, GConnectDefault);
  268. gtk_box_append(box2, button);
  269. button := gtk_check_button_new_with_mnemonic("_Spinner");
  270. g_signal_connect_data(button, "toggled", ADR(OnToggleSpinner),
  271. entry, NIL, GConnectDefault);
  272. gtk_box_append(box2, button)
  273. END;
  274. IF gtk_widget_get_visible(taggedWindow) = 0 THEN
  275. gtk_widget_set_visible(taggedWindow, 1)
  276. ELSE
  277. gtk_window_destroy(taggedWindow);
  278. taggedWindow := NIL;
  279. tagBox := NIL;
  280. tagSpinner := NIL
  281. END;
  282. RETURN taggedWindow
  283. END DoTaggedEntry;
  284. (* ------------------------------------------------------------------ *)
  285. (* Shortcut Triggers *)
  286. (* ------------------------------------------------------------------ *)
  287. VAR
  288. shortcutWindow: ADDRESS;
  289. shortcutDesc: ARRAY [0..1] OF ARRAY [0..31] OF CHAR;
  290. shortcutTrig: ARRAY [0..1] OF ARRAY [0..31] OF CHAR;
  291. (* gboolean shortcut_activated(GtkWidget *widget, GVariant *unused, gpointer row) *)
  292. PROCEDURE OnShortcutActivated (button: ADDRESS; data: ADDRESS);
  293. VAR
  294. label: ADDRESS;
  295. buf: ARRAY [0..127] OF CHAR;
  296. BEGIN
  297. label := gtk_button_get_label(button);
  298. CopyCStr(label, buf);
  299. printf("activated %s\n", buf)
  300. END OnShortcutActivated;
  301. PROCEDURE DoShortcutTriggers (doWidget: ADDRESS) : ADDRESS;
  302. VAR
  303. list, row, controller, trigger, action, shortcut: ADDRESS;
  304. i: INTEGER;
  305. triggerAction: ARRAY [0..31] OF CHAR;
  306. BEGIN
  307. IF shortcutWindow = NIL THEN
  308. shortcutWindow := gtk_window_new();
  309. gtk_window_set_title(shortcutWindow, "Shortcuts");
  310. gtk_window_set_default_size(shortcutWindow, 200, -1);
  311. gtk_window_set_resizable(shortcutWindow, 0);
  312. shortcutDesc[0] := "Press Ctrl-G";
  313. shortcutDesc[1] := "Press X";
  314. shortcutTrig[0] := "<Control>g";
  315. shortcutTrig[1] := "x";
  316. triggerAction := "signal(clicked)";
  317. list := gtk_list_box_new();
  318. gtk_widget_set_margin_top(list, 6);
  319. gtk_widget_set_margin_bottom(list, 6);
  320. gtk_widget_set_margin_start(list, 6);
  321. gtk_widget_set_margin_end(list, 6);
  322. gtk_window_set_child(shortcutWindow, list);
  323. FOR i := 0 TO 1 DO
  324. (* The C row is a GtkLabel whose shortcut uses
  325. gtk_callback_action_new (not bound); a GtkButton row plus the
  326. parsed "signal(clicked)" action gives the same activation. *)
  327. row := gtk_button_new_with_label(shortcutDesc[i]);
  328. gtk_list_box_insert(list, row, -1);
  329. controller := gtk_shortcut_controller_new();
  330. gtk_shortcut_controller_set_scope(controller, GtkShortcutScopeGlobal);
  331. gtk_widget_add_controller(row, controller);
  332. trigger := gtk_shortcut_trigger_parse_string(shortcutTrig[i]);
  333. action := gtk_shortcut_action_parse_string(triggerAction);
  334. shortcut := gtk_shortcut_new(trigger, action);
  335. gtk_shortcut_controller_add_shortcut(controller, shortcut);
  336. g_signal_connect_data(row, "clicked", ADR(OnShortcutActivated),
  337. NIL, NIL, GConnectDefault)
  338. END
  339. END;
  340. IF gtk_widget_get_visible(shortcutWindow) = 0 THEN
  341. gtk_widget_set_visible(shortcutWindow, 1)
  342. ELSE
  343. gtk_window_destroy(shortcutWindow);
  344. shortcutWindow := NIL
  345. END;
  346. RETURN shortcutWindow
  347. END DoShortcutTriggers;
  348. END DemosInput.