DemosInput.mod 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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. gtk_entry_set_visibility, gtk_entry_set_activates_default;
  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. (* GtkPasswordEntry is not bound; a GtkEntry with the text hidden
  99. behaves the same for this demo and is read via GtkEditable. *)
  100. entry := gtk_entry_new();
  101. gtk_entry_set_visibility(entry, 0);
  102. gtk_entry_set_placeholder_text(entry, placeholder);
  103. gtk_entry_set_activates_default(entry, 1);
  104. g_signal_connect_data(entry, "notify::text", ADR(OnUpdateButton),
  105. NIL, NIL, GConnectDefault);
  106. RETURN entry
  107. END PasswordField;
  108. PROCEDURE DoPasswordEntry (doWidget: ADDRESS) : ADDRESS;
  109. VAR
  110. box, header: ADDRESS;
  111. BEGIN
  112. IF pwWindow = NIL THEN
  113. pwWindow := gtk_window_new();
  114. gtk_window_set_title(pwWindow, "Choose a Password");
  115. gtk_window_set_resizable(pwWindow, 0);
  116. header := gtk_header_bar_new();
  117. gtk_header_bar_set_show_title_buttons(header, 0);
  118. gtk_window_set_titlebar(pwWindow, header);
  119. box := gtk_box_new(GtkVertical, 6);
  120. gtk_widget_set_margin_start(box, 18);
  121. gtk_widget_set_margin_end(box, 18);
  122. gtk_widget_set_margin_top(box, 18);
  123. gtk_widget_set_margin_bottom(box, 18);
  124. gtk_window_set_child(pwWindow, box);
  125. pwEntry := PasswordField("Password");
  126. gtk_box_append(box, pwEntry);
  127. pwEntry2 := PasswordField("Confirm");
  128. gtk_box_append(box, pwEntry2);
  129. pwButton := gtk_button_new_with_mnemonic("_Done");
  130. gtk_widget_add_css_class(pwButton, "suggested-action");
  131. g_signal_connect_data(pwButton, "clicked", ADR(OnDone),
  132. NIL, NIL, GConnectDefault);
  133. gtk_widget_set_sensitive(pwButton, 0);
  134. gtk_header_bar_pack_end(header, pwButton)
  135. END;
  136. IF gtk_widget_get_visible(pwWindow) = 0 THEN
  137. gtk_widget_set_visible(pwWindow, 1)
  138. ELSE
  139. gtk_window_destroy(pwWindow);
  140. pwWindow := NIL
  141. END;
  142. RETURN pwWindow
  143. END DoPasswordEntry;
  144. (* ------------------------------------------------------------------ *)
  145. (* Search Entry *)
  146. (* ------------------------------------------------------------------ *)
  147. VAR
  148. searchWindow, searchBar: ADDRESS;
  149. (* void search_changed_cb(GtkSearchEntry *entry, GtkLabel *result_label) *)
  150. PROCEDURE OnSearchChanged (entry: ADDRESS; resultLabel: ADDRESS);
  151. VAR
  152. text: ADDRESS;
  153. buf: ARRAY [0..255] OF CHAR;
  154. BEGIN
  155. text := gtk_editable_get_text(entry);
  156. CopyCStr(text, buf);
  157. gtk_label_set_text(resultLabel, buf)
  158. END OnSearchChanged;
  159. (* The C demo binds the header toggle's "active" to the search bar's
  160. "search-mode-enabled"; g_object_bind_property is not bound here, so
  161. the same effect is wired through the "toggled" signal. *)
  162. PROCEDURE OnSearchToggled (button: ADDRESS; data: ADDRESS);
  163. BEGIN
  164. IF gtk_toggle_button_get_active(button) # 0 THEN
  165. gtk_search_bar_set_search_mode(searchBar, 1)
  166. ELSE
  167. gtk_search_bar_set_search_mode(searchBar, 0)
  168. END
  169. END OnSearchToggled;
  170. PROCEDURE DoSearchEntry (doWidget: ADDRESS) : ADDRESS;
  171. VAR
  172. vbox, hbox, box, label, entry, button, header: ADDRESS;
  173. BEGIN
  174. IF searchWindow = NIL THEN
  175. searchWindow := gtk_window_new();
  176. gtk_window_set_title(searchWindow, "Type to Search");
  177. gtk_window_set_resizable(searchWindow, 0);
  178. gtk_widget_set_size_request(searchWindow, 200, -1);
  179. header := gtk_header_bar_new();
  180. gtk_window_set_titlebar(searchWindow, header);
  181. vbox := gtk_box_new(GtkVertical, 0);
  182. gtk_window_set_child(searchWindow, vbox);
  183. entry := gtk_search_entry_new();
  184. gtk_widget_set_halign(entry, GtkAlignCenter);
  185. searchBar := gtk_search_bar_new();
  186. gtk_search_bar_connect_entry(searchBar, entry);
  187. gtk_search_bar_set_show_close_button(searchBar, 0);
  188. gtk_search_bar_set_child(searchBar, entry);
  189. gtk_box_append(vbox, searchBar);
  190. (* gtk_search_bar_set_key_capture_widget is not bound; the search
  191. bar is opened via the header toggle below instead. *)
  192. box := gtk_box_new(GtkVertical, 18);
  193. gtk_widget_set_margin_start(box, 18);
  194. gtk_widget_set_margin_end(box, 18);
  195. gtk_widget_set_margin_top(box, 18);
  196. gtk_widget_set_margin_bottom(box, 18);
  197. gtk_box_append(vbox, box);
  198. button := gtk_toggle_button_new_with_label("Search");
  199. g_signal_connect_data(button, "toggled", ADR(OnSearchToggled),
  200. NIL, NIL, GConnectDefault);
  201. gtk_header_bar_pack_end(header, button);
  202. hbox := gtk_box_new(GtkHorizontal, 10);
  203. gtk_box_append(box, hbox);
  204. label := gtk_label_new("Searching for:");
  205. gtk_box_append(hbox, label);
  206. label := gtk_label_new("");
  207. gtk_box_append(hbox, label);
  208. g_signal_connect_data(entry, "search-changed", ADR(OnSearchChanged),
  209. label, NIL, GConnectDefault)
  210. END;
  211. IF gtk_widget_get_visible(searchWindow) = 0 THEN
  212. gtk_widget_set_visible(searchWindow, 1)
  213. ELSE
  214. gtk_window_destroy(searchWindow);
  215. searchWindow := NIL
  216. END;
  217. RETURN searchWindow
  218. END DoSearchEntry;
  219. (* ------------------------------------------------------------------ *)
  220. (* Tagged Entry *)
  221. (* ------------------------------------------------------------------ *)
  222. (* The demo's DemoTaggedEntry composite is not bound; this approximates
  223. it with a horizontal GtkBox that holds the entry, tag labels and an
  224. optional spinner. *)
  225. VAR
  226. taggedWindow, tagBox, tagSpinner: ADDRESS;
  227. (* void add_tag(GtkButton *button, DemoTaggedEntry *entry) *)
  228. PROCEDURE OnAddTag (button: ADDRESS; data: ADDRESS);
  229. VAR
  230. tag: ADDRESS;
  231. BEGIN
  232. tag := gtk_label_new("Blue");
  233. gtk_widget_add_css_class(tag, "blue");
  234. gtk_box_append(tagBox, tag)
  235. END OnAddTag;
  236. (* void toggle_spinner(GtkCheckButton *button, DemoTaggedEntry *entry) *)
  237. PROCEDURE OnToggleSpinner (button: ADDRESS; data: ADDRESS);
  238. BEGIN
  239. IF tagSpinner = NIL THEN
  240. tagSpinner := gtk_spinner_new();
  241. gtk_spinner_start(tagSpinner);
  242. gtk_box_append(tagBox, tagSpinner)
  243. ELSE
  244. gtk_box_remove(tagBox, tagSpinner);
  245. tagSpinner := NIL
  246. END
  247. END OnToggleSpinner;
  248. PROCEDURE DoTaggedEntry (doWidget: ADDRESS) : ADDRESS;
  249. VAR
  250. box, box2, entry, button: ADDRESS;
  251. BEGIN
  252. IF taggedWindow = NIL THEN
  253. taggedWindow := gtk_window_new();
  254. gtk_window_set_title(taggedWindow, "Tagged Entry");
  255. gtk_window_set_default_size(taggedWindow, 260, -1);
  256. gtk_window_set_resizable(taggedWindow, 0);
  257. box := gtk_box_new(GtkVertical, 6);
  258. gtk_widget_set_margin_start(box, 18);
  259. gtk_widget_set_margin_end(box, 18);
  260. gtk_widget_set_margin_top(box, 18);
  261. gtk_widget_set_margin_bottom(box, 18);
  262. gtk_window_set_child(taggedWindow, box);
  263. tagBox := gtk_box_new(GtkHorizontal, 6);
  264. gtk_box_append(box, tagBox);
  265. entry := gtk_entry_new();
  266. gtk_box_append(tagBox, entry);
  267. box2 := gtk_box_new(GtkHorizontal, 6);
  268. gtk_widget_set_halign(box2, GtkAlignEnd);
  269. gtk_box_append(box, box2);
  270. button := gtk_button_new_with_mnemonic("Add _Tag");
  271. g_signal_connect_data(button, "clicked", ADR(OnAddTag),
  272. entry, NIL, GConnectDefault);
  273. gtk_box_append(box2, button);
  274. button := gtk_check_button_new_with_mnemonic("_Spinner");
  275. g_signal_connect_data(button, "toggled", ADR(OnToggleSpinner),
  276. entry, NIL, GConnectDefault);
  277. gtk_box_append(box2, button)
  278. END;
  279. IF gtk_widget_get_visible(taggedWindow) = 0 THEN
  280. gtk_widget_set_visible(taggedWindow, 1)
  281. ELSE
  282. gtk_window_destroy(taggedWindow);
  283. taggedWindow := NIL;
  284. tagBox := NIL;
  285. tagSpinner := NIL
  286. END;
  287. RETURN taggedWindow
  288. END DoTaggedEntry;
  289. (* ------------------------------------------------------------------ *)
  290. (* Shortcut Triggers *)
  291. (* ------------------------------------------------------------------ *)
  292. VAR
  293. shortcutWindow: ADDRESS;
  294. shortcutDesc: ARRAY [0..1] OF ARRAY [0..31] OF CHAR;
  295. shortcutTrig: ARRAY [0..1] OF ARRAY [0..31] OF CHAR;
  296. (* gboolean shortcut_activated(GtkWidget *widget, GVariant *unused, gpointer row) *)
  297. PROCEDURE OnShortcutActivated (button: ADDRESS; data: ADDRESS);
  298. VAR
  299. label: ADDRESS;
  300. buf: ARRAY [0..127] OF CHAR;
  301. BEGIN
  302. label := gtk_button_get_label(button);
  303. CopyCStr(label, buf);
  304. printf("activated %s\n", buf)
  305. END OnShortcutActivated;
  306. PROCEDURE DoShortcutTriggers (doWidget: ADDRESS) : ADDRESS;
  307. VAR
  308. list, row, controller, trigger, action, shortcut: ADDRESS;
  309. i: INTEGER;
  310. triggerAction: ARRAY [0..31] OF CHAR;
  311. BEGIN
  312. IF shortcutWindow = NIL THEN
  313. shortcutWindow := gtk_window_new();
  314. gtk_window_set_title(shortcutWindow, "Shortcuts");
  315. gtk_window_set_default_size(shortcutWindow, 200, -1);
  316. gtk_window_set_resizable(shortcutWindow, 0);
  317. shortcutDesc[0] := "Press Ctrl-G";
  318. shortcutDesc[1] := "Press X";
  319. shortcutTrig[0] := "<Control>g";
  320. shortcutTrig[1] := "x";
  321. triggerAction := "signal(clicked)";
  322. list := gtk_list_box_new();
  323. gtk_widget_set_margin_top(list, 6);
  324. gtk_widget_set_margin_bottom(list, 6);
  325. gtk_widget_set_margin_start(list, 6);
  326. gtk_widget_set_margin_end(list, 6);
  327. gtk_window_set_child(shortcutWindow, list);
  328. FOR i := 0 TO 1 DO
  329. (* The C row is a GtkLabel whose shortcut uses
  330. gtk_callback_action_new (not bound); a GtkButton row plus the
  331. parsed "signal(clicked)" action gives the same activation. *)
  332. row := gtk_button_new_with_label(shortcutDesc[i]);
  333. gtk_list_box_insert(list, row, -1);
  334. controller := gtk_shortcut_controller_new();
  335. gtk_shortcut_controller_set_scope(controller, GtkShortcutScopeGlobal);
  336. gtk_widget_add_controller(row, controller);
  337. trigger := gtk_shortcut_trigger_parse_string(shortcutTrig[i]);
  338. action := gtk_shortcut_action_parse_string(triggerAction);
  339. shortcut := gtk_shortcut_new(trigger, action);
  340. gtk_shortcut_controller_add_shortcut(controller, shortcut);
  341. g_signal_connect_data(row, "clicked", ADR(OnShortcutActivated),
  342. NIL, NIL, GConnectDefault)
  343. END
  344. END;
  345. IF gtk_widget_get_visible(shortcutWindow) = 0 THEN
  346. gtk_widget_set_visible(shortcutWindow, 1)
  347. ELSE
  348. gtk_window_destroy(shortcutWindow);
  349. shortcutWindow := NIL
  350. END;
  351. RETURN shortcutWindow
  352. END DoShortcutTriggers;
  353. END DemosInput.