DemosWindows.mod 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. IMPLEMENTATION MODULE DemosWindows ;
  2. (*
  3. m2-GTK4 - window-oriented gtk-demo ports, following the gtk-demo
  4. pattern used by DemosBasic/DemoImpl: each Do* procedure creates its
  5. window once (module VAR), then toggles visibility - showing it, or
  6. destroying it and resetting the handle to NIL - and returns it.
  7. Originals: gtk/demos/gtk-demo/{headerbar,infobar,tabs,assistant}.c
  8. Simplifications forced by the curated bindings under src/:
  9. * GtkInfoBar is not bound, so DoInfoBar lays the messages out as
  10. plain labels plus toggle buttons instead of real info bars.
  11. * Pango tab arrays and gtk_text_view_set_tabs() are not bound, so
  12. DoTabs omits the tab-stop setup (the sample text is unchanged).
  13. * GtkAccessible and g_object_bind_property are not bound; the
  14. accessibility annotations and the toggle/info-bar bindings are
  15. dropped.
  16. * gtk_widget_set_display() / g_object_add_weak_pointer() are not
  17. used, matching the existing demos (display is inherited, and the
  18. module VAR is reset to NIL explicitly).
  19. *)
  20. FROM SYSTEM IMPORT ADDRESS;
  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_child, gtk_window_destroy, gtk_window_set_titlebar;
  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_halign, gtk_widget_set_valign, gtk_widget_set_hexpand,
  28. gtk_widget_add_css_class;
  29. FROM GtkEnums IMPORT GtkHorizontal, GtkVertical, GtkAlignCenter,
  30. GtkAlignFill, GtkWrapWord;
  31. FROM GtkHeaderBar IMPORT gtk_header_bar_new, gtk_header_bar_pack_start,
  32. gtk_header_bar_pack_end;
  33. FROM GtkBox IMPORT gtk_box_new, gtk_box_append;
  34. FROM GtkButton IMPORT gtk_button_new_from_icon_name;
  35. FROM GtkSwitch IMPORT gtk_switch_new;
  36. FROM GtkTextView IMPORT gtk_text_view_new, gtk_text_view_get_buffer,
  37. gtk_text_view_set_wrap_mode, gtk_text_view_set_top_margin,
  38. gtk_text_view_set_bottom_margin, gtk_text_view_set_left_margin,
  39. gtk_text_view_set_right_margin, gtk_text_view_set_tabs;
  40. FROM GtkTextBuffer IMPORT gtk_text_buffer_set_text;
  41. FROM PangoTabArray IMPORT pango_tab_array_new, pango_tab_array_set_tab,
  42. pango_tab_array_free, PangoTabLeft, PangoTabDecimal;
  43. FROM GtkScrolledWindow IMPORT gtk_scrolled_window_new,
  44. gtk_scrolled_window_set_child, gtk_scrolled_window_set_policy,
  45. GtkPolicyNever, GtkPolicyAutomatic;
  46. FROM GtkLabel IMPORT gtk_label_new, gtk_label_set_wrap;
  47. FROM GtkToggleButton IMPORT gtk_toggle_button_new_with_label;
  48. FROM GtkFrame IMPORT gtk_frame_new, gtk_frame_set_child;
  49. FROM GtkAssistant IMPORT gtk_assistant_new, gtk_assistant_append_page,
  50. gtk_assistant_set_page_title, gtk_assistant_set_page_type,
  51. gtk_assistant_set_page_complete, gtk_assistant_get_current_page,
  52. gtk_assistant_get_n_pages, gtk_assistant_get_nth_page,
  53. gtk_assistant_commit, AssistantPageIntro, AssistantPageConfirm,
  54. AssistantPageProgress;
  55. FROM GtkEntry IMPORT gtk_entry_new, gtk_entry_set_activates_default;
  56. FROM GtkEditable IMPORT gtk_editable_get_text;
  57. FROM GtkCheckButton IMPORT gtk_check_button_new_with_label;
  58. FROM GtkProgressBar IMPORT gtk_progress_bar_new,
  59. gtk_progress_bar_get_fraction, gtk_progress_bar_set_fraction;
  60. FROM GtkClosures IMPORT Connect2, Connect3;
  61. FROM GtkUtils IMPORT StrAppend, IntToStr, CStrToM2;
  62. FROM GLib IMPORT g_timeout_add, GSourceContinue, GSourceRemove;
  63. (* ------------------------------------------------------------------ *)
  64. (* Header Bar (headerbar.c) *)
  65. (* ------------------------------------------------------------------ *)
  66. VAR
  67. headerBarWindow: ADDRESS;
  68. (* GtkWidget *gtk_button_new_from_icon_name(const char *icon_name) *)
  69. PROCEDURE IconButton (name: ARRAY OF CHAR) : ADDRESS;
  70. BEGIN
  71. RETURN gtk_button_new_from_icon_name(name)
  72. END IconButton;
  73. PROCEDURE DoHeaderBar (doWidget: ADDRESS) : ADDRESS;
  74. VAR
  75. header, button, box, content: ADDRESS;
  76. BEGIN
  77. IF headerBarWindow = NIL THEN
  78. headerBarWindow := gtk_window_new();
  79. gtk_window_set_title(headerBarWindow, "Welcome to the Hotel California");
  80. gtk_window_set_default_size(headerBarWindow, 600, 400);
  81. header := gtk_header_bar_new();
  82. button := IconButton("mail-send-receive-symbolic");
  83. gtk_header_bar_pack_end(header, button);
  84. box := gtk_box_new(GtkHorizontal, 0);
  85. gtk_widget_add_css_class(box, "linked");
  86. button := IconButton("go-previous-symbolic");
  87. gtk_box_append(box, button);
  88. button := IconButton("go-next-symbolic");
  89. gtk_box_append(box, button);
  90. gtk_header_bar_pack_start(header, box);
  91. button := gtk_switch_new();
  92. gtk_header_bar_pack_start(header, button);
  93. gtk_window_set_titlebar(headerBarWindow, header);
  94. content := gtk_text_view_new();
  95. gtk_window_set_child(headerBarWindow, content)
  96. END;
  97. IF gtk_widget_get_visible(headerBarWindow) = 0 THEN
  98. gtk_widget_set_visible(headerBarWindow, 1)
  99. ELSE
  100. gtk_window_destroy(headerBarWindow);
  101. headerBarWindow := NIL
  102. END;
  103. RETURN headerBarWindow
  104. END DoHeaderBar;
  105. (* ------------------------------------------------------------------ *)
  106. (* Info Bars (infobar.c) *)
  107. (* ------------------------------------------------------------------ *)
  108. VAR
  109. infoBarWindow: ADDRESS;
  110. (* GtkInfoBar is not bound by m2-GTK4, so this simplified port shows the
  111. five message types as wrapped labels, with a linked row of toggle
  112. buttons underneath, all inside a titled frame. *)
  113. PROCEDURE DoInfoBar (doWidget: ADDRESS) : ADDRESS;
  114. VAR
  115. vbox, frame, actions, label, button: ADDRESS;
  116. BEGIN
  117. IF infoBarWindow = NIL THEN
  118. actions := gtk_box_new(GtkHorizontal, 0);
  119. gtk_widget_add_css_class(actions, "linked");
  120. infoBarWindow := gtk_window_new();
  121. gtk_window_set_title(infoBarWindow, "Info Bars");
  122. gtk_window_set_resizable(infoBarWindow, 0);
  123. vbox := gtk_box_new(GtkVertical, 0);
  124. gtk_widget_set_margin_start(vbox, 8);
  125. gtk_widget_set_margin_end(vbox, 8);
  126. gtk_widget_set_margin_top(vbox, 8);
  127. gtk_widget_set_margin_bottom(vbox, 8);
  128. gtk_window_set_child(infoBarWindow, vbox);
  129. label := gtk_label_new("This is an info bar with message type GTK_MESSAGE_INFO");
  130. gtk_label_set_wrap(label, 1);
  131. gtk_box_append(vbox, label);
  132. button := gtk_toggle_button_new_with_label("Message");
  133. gtk_box_append(actions, button);
  134. label := gtk_label_new("This is an info bar with message type GTK_MESSAGE_WARNING");
  135. gtk_label_set_wrap(label, 1);
  136. gtk_box_append(vbox, label);
  137. button := gtk_toggle_button_new_with_label("Warning");
  138. gtk_box_append(actions, button);
  139. label := gtk_label_new("This is an info bar with message type GTK_MESSAGE_QUESTION");
  140. gtk_label_set_wrap(label, 1);
  141. gtk_box_append(vbox, label);
  142. button := gtk_toggle_button_new_with_label("Question");
  143. gtk_box_append(actions, button);
  144. label := gtk_label_new("This is an info bar with message type GTK_MESSAGE_ERROR");
  145. gtk_label_set_wrap(label, 1);
  146. gtk_box_append(vbox, label);
  147. button := gtk_toggle_button_new_with_label("Error");
  148. gtk_box_append(actions, button);
  149. label := gtk_label_new("This is an info bar with message type GTK_MESSAGE_OTHER");
  150. gtk_label_set_wrap(label, 1);
  151. gtk_box_append(vbox, label);
  152. button := gtk_toggle_button_new_with_label("Other");
  153. gtk_box_append(actions, button);
  154. frame := gtk_frame_new("An example of different info bars");
  155. gtk_widget_set_margin_top(frame, 8);
  156. gtk_widget_set_margin_bottom(frame, 8);
  157. gtk_box_append(vbox, frame);
  158. gtk_widget_set_halign(actions, GtkAlignCenter);
  159. gtk_widget_set_margin_start(actions, 8);
  160. gtk_widget_set_margin_end(actions, 8);
  161. gtk_widget_set_margin_top(actions, 8);
  162. gtk_widget_set_margin_bottom(actions, 8);
  163. gtk_frame_set_child(frame, actions)
  164. END;
  165. IF gtk_widget_get_visible(infoBarWindow) = 0 THEN
  166. gtk_widget_set_visible(infoBarWindow, 1)
  167. ELSE
  168. gtk_window_destroy(infoBarWindow);
  169. infoBarWindow := NIL
  170. END;
  171. RETURN infoBarWindow
  172. END DoInfoBar;
  173. (* ------------------------------------------------------------------ *)
  174. (* Text View/Tabs (tabs.c) *)
  175. (* ------------------------------------------------------------------ *)
  176. VAR
  177. tabsWindow: ADDRESS;
  178. (* Pango tab arrays and gtk_text_view_set_tabs() are not bound; the tab
  179. stops are therefore omitted, but the sample text is preserved (built
  180. with explicit TAB/LF characters so no escape sequences are needed). *)
  181. PROCEDURE SetTabsText (buffer: ADDRESS);
  182. VAR
  183. text: ARRAY [0..127] OF CHAR;
  184. tab, nl: ARRAY [0..1] OF CHAR;
  185. BEGIN
  186. tab[0] := CHR(9);
  187. tab[1] := 0C;
  188. nl[0] := CHR(10);
  189. nl[1] := 0C;
  190. text[0] := 0C;
  191. StrAppend(text, "one"); StrAppend(text, tab);
  192. StrAppend(text, "2.0"); StrAppend(text, tab);
  193. StrAppend(text, "three"); StrAppend(text, nl);
  194. StrAppend(text, "four"); StrAppend(text, tab);
  195. StrAppend(text, "5.555"); StrAppend(text, tab);
  196. StrAppend(text, "six"); StrAppend(text, nl);
  197. StrAppend(text, "seven"); StrAppend(text, tab);
  198. StrAppend(text, "88.88"); StrAppend(text, tab);
  199. StrAppend(text, "nine");
  200. gtk_text_buffer_set_text(buffer, text, -1)
  201. END SetTabsText;
  202. PROCEDURE DoTabs (doWidget: ADDRESS) : ADDRESS;
  203. VAR
  204. view, sw, buffer, tabs: ADDRESS;
  205. BEGIN
  206. IF tabsWindow = NIL THEN
  207. tabsWindow := gtk_window_new();
  208. gtk_window_set_title(tabsWindow, "Tabs");
  209. gtk_window_set_default_size(tabsWindow, 330, 130);
  210. gtk_window_set_resizable(tabsWindow, 0);
  211. view := gtk_text_view_new();
  212. gtk_text_view_set_wrap_mode(view, GtkWrapWord);
  213. gtk_text_view_set_top_margin(view, 20);
  214. gtk_text_view_set_bottom_margin(view, 20);
  215. gtk_text_view_set_left_margin(view, 20);
  216. gtk_text_view_set_right_margin(view, 20);
  217. tabs := pango_tab_array_new(2, 1);
  218. pango_tab_array_set_tab(tabs, 0, PangoTabLeft, 50);
  219. pango_tab_array_set_tab(tabs, 1, PangoTabDecimal, 100);
  220. gtk_text_view_set_tabs(view, tabs);
  221. pango_tab_array_free(tabs);
  222. buffer := gtk_text_view_get_buffer(view);
  223. SetTabsText(buffer);
  224. sw := gtk_scrolled_window_new();
  225. gtk_scrolled_window_set_policy(sw, GtkPolicyNever, GtkPolicyAutomatic);
  226. gtk_window_set_child(tabsWindow, sw);
  227. gtk_scrolled_window_set_child(sw, view)
  228. END;
  229. IF gtk_widget_get_visible(tabsWindow) = 0 THEN
  230. gtk_widget_set_visible(tabsWindow, 1)
  231. ELSE
  232. gtk_window_destroy(tabsWindow);
  233. tabsWindow := NIL
  234. END;
  235. RETURN tabsWindow
  236. END DoTabs;
  237. (* ------------------------------------------------------------------ *)
  238. (* Assistant (assistant.c) *)
  239. (* ------------------------------------------------------------------ *)
  240. VAR
  241. assistantWindow, progressBar: ADDRESS;
  242. (* gboolean apply_changes_gradually(gpointer data) *)
  243. PROCEDURE ApplyChangesGradually (data: ADDRESS) : INTEGER;
  244. VAR
  245. fraction: REAL;
  246. BEGIN
  247. fraction := gtk_progress_bar_get_fraction(progressBar);
  248. fraction := fraction + 0.05;
  249. IF fraction < 1.0 THEN
  250. gtk_progress_bar_set_fraction(progressBar, fraction);
  251. RETURN GSourceContinue
  252. ELSE
  253. gtk_window_destroy(data);
  254. RETURN GSourceRemove
  255. END
  256. END ApplyChangesGradually;
  257. (* void on_assistant_apply(GtkWidget *widget, gpointer data) *)
  258. PROCEDURE OnAssistantApply (widget: ADDRESS; data: ADDRESS);
  259. BEGIN
  260. g_timeout_add(100, ApplyChangesGradually, widget)
  261. END OnAssistantApply;
  262. (* void on_assistant_close_cancel(GtkWidget *widget, gpointer data) *)
  263. PROCEDURE OnAssistantCloseCancel (widget: ADDRESS; data: ADDRESS);
  264. BEGIN
  265. gtk_window_destroy(widget)
  266. END OnAssistantCloseCancel;
  267. (* gint format "Sample assistant (%d of %d)" without g_strdup_printf. *)
  268. PROCEDURE MakeAssistantTitle (current, total: INTEGER;
  269. VAR title: ARRAY OF CHAR);
  270. VAR
  271. num: ARRAY [0..15] OF CHAR;
  272. BEGIN
  273. title[0] := 0C;
  274. StrAppend(title, "Sample assistant (");
  275. IntToStr(current, num);
  276. StrAppend(title, num);
  277. StrAppend(title, " of ");
  278. IntToStr(total, num);
  279. StrAppend(title, num);
  280. StrAppend(title, ")")
  281. END MakeAssistantTitle;
  282. (* void on_assistant_prepare(GtkWidget *widget, GtkWidget *page, gpointer data) *)
  283. PROCEDURE OnAssistantPrepare (widget: ADDRESS; page: ADDRESS; data: ADDRESS);
  284. VAR
  285. currentPage, nPages: INTEGER;
  286. title: ARRAY [0..63] OF CHAR;
  287. BEGIN
  288. currentPage := gtk_assistant_get_current_page(widget);
  289. nPages := gtk_assistant_get_n_pages(widget);
  290. MakeAssistantTitle(currentPage + 1, nPages, title);
  291. gtk_window_set_title(widget, title);
  292. (* The fourth page (zero-based) is the progress page: commit once the
  293. user has clicked Apply to reach it. *)
  294. IF currentPage = 3 THEN
  295. gtk_assistant_commit(widget)
  296. END
  297. END OnAssistantPrepare;
  298. (* void on_entry_changed(GtkWidget *widget, gpointer data) *)
  299. PROCEDURE OnEntryChanged (widget: ADDRESS; data: ADDRESS);
  300. VAR
  301. currentPage: ADDRESS;
  302. pageNumber: INTEGER;
  303. text: ARRAY [0..1] OF CHAR;
  304. BEGIN
  305. pageNumber := gtk_assistant_get_current_page(data);
  306. currentPage := gtk_assistant_get_nth_page(data, pageNumber);
  307. CStrToM2(gtk_editable_get_text(widget), text);
  308. IF text[0] # 0C THEN
  309. gtk_assistant_set_page_complete(data, currentPage, 1)
  310. ELSE
  311. gtk_assistant_set_page_complete(data, currentPage, 0)
  312. END
  313. END OnEntryChanged;
  314. PROCEDURE CreatePage1 (assistant: ADDRESS);
  315. VAR
  316. box, label, entry: ADDRESS;
  317. BEGIN
  318. box := gtk_box_new(GtkHorizontal, 12);
  319. gtk_widget_set_margin_start(box, 12);
  320. gtk_widget_set_margin_end(box, 12);
  321. gtk_widget_set_margin_top(box, 12);
  322. gtk_widget_set_margin_bottom(box, 12);
  323. label := gtk_label_new("You must fill out this entry to continue:");
  324. gtk_box_append(box, label);
  325. entry := gtk_entry_new();
  326. gtk_entry_set_activates_default(entry, 1);
  327. gtk_widget_set_valign(entry, GtkAlignCenter);
  328. gtk_box_append(box, entry);
  329. Connect2(entry, "changed", OnEntryChanged, assistant);
  330. gtk_assistant_append_page(assistant, box);
  331. gtk_assistant_set_page_title(assistant, box, "Page 1");
  332. gtk_assistant_set_page_type(assistant, box, AssistantPageIntro)
  333. END CreatePage1;
  334. PROCEDURE CreatePage2 (assistant: ADDRESS);
  335. VAR
  336. box, checkbutton: ADDRESS;
  337. BEGIN
  338. box := gtk_box_new(GtkHorizontal, 12);
  339. gtk_widget_set_margin_start(box, 12);
  340. gtk_widget_set_margin_end(box, 12);
  341. gtk_widget_set_margin_top(box, 12);
  342. gtk_widget_set_margin_bottom(box, 12);
  343. checkbutton := gtk_check_button_new_with_label
  344. ("This is optional data, you may continue even if you do not check this");
  345. gtk_widget_set_valign(checkbutton, GtkAlignCenter);
  346. gtk_box_append(box, checkbutton);
  347. gtk_assistant_append_page(assistant, box);
  348. gtk_assistant_set_page_complete(assistant, box, 1);
  349. gtk_assistant_set_page_title(assistant, box, "Page 2")
  350. END CreatePage2;
  351. PROCEDURE CreatePage3 (assistant: ADDRESS);
  352. VAR
  353. label: ADDRESS;
  354. BEGIN
  355. label := gtk_label_new("This is a confirmation page, press 'Apply' to apply changes");
  356. gtk_assistant_append_page(assistant, label);
  357. gtk_assistant_set_page_type(assistant, label, AssistantPageConfirm);
  358. gtk_assistant_set_page_complete(assistant, label, 1);
  359. gtk_assistant_set_page_title(assistant, label, "Confirmation")
  360. END CreatePage3;
  361. PROCEDURE CreatePage4 (assistant: ADDRESS);
  362. BEGIN
  363. progressBar := gtk_progress_bar_new();
  364. gtk_widget_set_halign(progressBar, GtkAlignFill);
  365. gtk_widget_set_valign(progressBar, GtkAlignCenter);
  366. gtk_widget_set_hexpand(progressBar, 1);
  367. gtk_widget_set_margin_start(progressBar, 40);
  368. gtk_widget_set_margin_end(progressBar, 40);
  369. gtk_assistant_append_page(assistant, progressBar);
  370. gtk_assistant_set_page_type(assistant, progressBar, AssistantPageProgress);
  371. gtk_assistant_set_page_title(assistant, progressBar, "Applying changes");
  372. (* Prevents the assistant from being closed while we are "busy". *)
  373. gtk_assistant_set_page_complete(assistant, progressBar, 0)
  374. END CreatePage4;
  375. PROCEDURE DoAssistant (doWidget: ADDRESS) : ADDRESS;
  376. BEGIN
  377. IF assistantWindow = NIL THEN
  378. assistantWindow := gtk_assistant_new();
  379. gtk_window_set_default_size(assistantWindow, -1, 300);
  380. CreatePage1(assistantWindow);
  381. CreatePage2(assistantWindow);
  382. CreatePage3(assistantWindow);
  383. CreatePage4(assistantWindow);
  384. Connect2(assistantWindow, "cancel", OnAssistantCloseCancel, NIL);
  385. Connect2(assistantWindow, "close", OnAssistantCloseCancel, NIL);
  386. Connect2(assistantWindow, "apply", OnAssistantApply, NIL);
  387. Connect3(assistantWindow, "prepare", OnAssistantPrepare, NIL)
  388. END;
  389. IF gtk_widget_get_visible(assistantWindow) = 0 THEN
  390. gtk_widget_set_visible(assistantWindow, 1)
  391. ELSE
  392. gtk_window_destroy(assistantWindow);
  393. assistantWindow := NIL
  394. END;
  395. RETURN assistantWindow
  396. END DoAssistant;
  397. END DemosWindows.