IMPLEMENTATION MODULE DemoImpl ; (* Five demos ported from the GTK source tree (gtk/demos/gtk-demo), simplified to the features bound by m2-GTK4: Links (links.c) - markup with hyperlinks + activate-link List Box (listbox.c) - rows in a GtkListBox Flow Box (flowbox.c) - reflowing color swatches Expander (expander.c) - collapsible scrolled text CSS Basics (css_basics.c) - live CSS editing *) FROM SYSTEM IMPORT ADDRESS, ADR; FROM GtkWindow IMPORT gtk_window_new, gtk_window_set_title, gtk_window_set_default_size, gtk_window_set_resizable, gtk_window_set_child, gtk_window_destroy; FROM GtkWidget IMPORT gtk_widget_set_visible, gtk_widget_get_visible, gtk_widget_set_margin_start, gtk_widget_set_margin_end, gtk_widget_set_margin_top, gtk_widget_set_margin_bottom, gtk_widget_set_vexpand, gtk_widget_add_css_class; FROM GtkEnums IMPORT GtkVertical, GtkSelectionNone; FROM GtkLabel IMPORT gtk_label_new, gtk_label_set_use_markup, gtk_label_set_wrap, gtk_label_set_wrap_mode, gtk_label_set_max_width_chars; FROM GtkBox IMPORT gtk_box_new, gtk_box_append, gtk_box_set_spacing; FROM GtkScrolledWindow IMPORT gtk_scrolled_window_new, gtk_scrolled_window_set_child, gtk_scrolled_window_set_min_content_height, gtk_scrolled_window_set_has_frame; FROM GtkListBox IMPORT gtk_list_box_new, gtk_list_box_append, gtk_list_box_row_new, gtk_list_box_row_set_child; FROM GtkFlowBox IMPORT gtk_flow_box_new, gtk_flow_box_append, gtk_flow_box_set_selection_mode, gtk_flow_box_set_max_children_per_line; FROM GtkDrawingArea IMPORT gtk_drawing_area_new, gtk_drawing_area_set_content_width, gtk_drawing_area_set_content_height, gtk_drawing_area_set_draw_func; FROM GtkButton IMPORT gtk_button_new, gtk_button_set_child; FROM GtkExpander IMPORT gtk_expander_new, gtk_expander_set_child; FROM GtkTextView IMPORT gtk_text_view_new, gtk_text_view_new_with_buffer; FROM GtkTextBuffer IMPORT gtk_text_buffer_new, gtk_text_buffer_set_text, gtk_text_buffer_get_text, gtk_text_buffer_get_start_iter, gtk_text_buffer_get_end_iter; FROM GtkTextIter IMPORT GtkTextIter; FROM GtkCssProvider IMPORT gtk_css_provider_new, gtk_css_provider_load_from_string; FROM GtkStyleContext IMPORT gtk_style_context_add_provider_for_display, GtkStyleProviderPriorityApplication; FROM GtkAlertDialog IMPORT gtk_alert_dialog_new, gtk_alert_dialog_set_detail, gtk_alert_dialog_show; FROM Gdk IMPORT gdk_display_get_default, GdkRGBA, gdk_rgba_parse, gdk_cairo_set_source_rgba; FROM Cairo IMPORT cairo_paint; FROM GObject IMPORT g_signal_connect_data, g_object_unref, GConnectDefault; FROM GtkClosures IMPORT Connect2; FROM GtkUtils IMPORT IntToStr, Free, CStrToM2; FROM GLib IMPORT g_strcmp0; FROM libc IMPORT printf; (* ------------------------------------------------------------------ *) (* Links *) (* ------------------------------------------------------------------ *) VAR linksWindow: ADDRESS; CONST LinksMarkup = "Some text may be marked up as hyperlinks, which can be clicked or activated via keynav, working fine alongside other markup such as Flathub."; (* gboolean activate_link(GtkWidget *label, const char *uri, gpointer data) *) PROCEDURE OnActivateLink (label: ADDRESS; uri: ADDRESS; data: ADDRESS) : INTEGER; VAR dialog: ADDRESS; key: ARRAY [0..7] OF CHAR; BEGIN key := "keynav"; IF g_strcmp0(uri, ADR(key)) = 0 THEN dialog := gtk_alert_dialog_new("Keyboard navigation"); gtk_alert_dialog_set_detail(dialog, "keynav is a shorthand for keyboard navigation."); gtk_alert_dialog_show(dialog, NIL); g_object_unref(dialog); RETURN 1 END; RETURN 0 END OnActivateLink; PROCEDURE DoLinks (doWidget: ADDRESS) : ADDRESS; VAR label: ADDRESS; BEGIN IF linksWindow = NIL THEN linksWindow := gtk_window_new(); gtk_window_set_title(linksWindow, "Links"); gtk_window_set_resizable(linksWindow, 0); label := gtk_label_new(LinksMarkup); gtk_label_set_use_markup(label, 1); gtk_label_set_max_width_chars(label, 40); gtk_label_set_wrap(label, 1); gtk_label_set_wrap_mode(label, 0); (* PANGO_WRAP_WORD *) g_signal_connect_data(label, "activate-link", ADR(OnActivateLink), NIL, NIL, GConnectDefault); gtk_widget_set_margin_start(label, 20); gtk_widget_set_margin_end(label, 20); gtk_widget_set_margin_top(label, 20); gtk_widget_set_margin_bottom(label, 20); gtk_window_set_child(linksWindow, label) END; IF gtk_widget_get_visible(linksWindow) = 0 THEN gtk_widget_set_visible(linksWindow, 1) ELSE gtk_window_destroy(linksWindow); linksWindow := NIL END; RETURN linksWindow END DoLinks; (* ------------------------------------------------------------------ *) (* List Box *) (* ------------------------------------------------------------------ *) VAR listBoxWindow: ADDRESS; PROCEDURE DoListBox (doWidget: ADDRESS) : ADDRESS; VAR box, row, sw: ADDRESS; i: INTEGER; text: ARRAY [0..15] OF CHAR; BEGIN IF listBoxWindow = NIL THEN listBoxWindow := gtk_window_new(); gtk_window_set_title(listBoxWindow, "List Box"); gtk_window_set_default_size(listBoxWindow, 260, 220); box := gtk_list_box_new(); FOR i := 1 TO 5 DO row := gtk_list_box_row_new(); IntToStr(i, text); gtk_list_box_row_set_child(row, gtk_label_new(text)); gtk_list_box_append(box, row) END; sw := gtk_scrolled_window_new(); gtk_scrolled_window_set_child(sw, box); gtk_window_set_child(listBoxWindow, sw) END; IF gtk_widget_get_visible(listBoxWindow) = 0 THEN gtk_widget_set_visible(listBoxWindow, 1) ELSE gtk_window_destroy(listBoxWindow); listBoxWindow := NIL END; RETURN listBoxWindow END DoListBox; (* ------------------------------------------------------------------ *) (* Flow Box *) (* ------------------------------------------------------------------ *) VAR flowBoxWindow: ADDRESS; colors: ARRAY [0..11] OF ARRAY [0..31] OF CHAR; (* void draw_color(GtkDrawingArea *area, cairo_t *cr, int w, int h, gpointer d) *) PROCEDURE DrawColor (area: ADDRESS; cr: ADDRESS; width, height: INTEGER; data: ADDRESS); VAR spec: ARRAY [0..31] OF CHAR; rgba: GdkRGBA; BEGIN CStrToM2(data, spec); IF gdk_rgba_parse(rgba, spec) # 0 THEN gdk_cairo_set_source_rgba(cr, rgba); cairo_paint(cr) END END DrawColor; PROCEDURE ColorSwatch (color: ADDRESS) : ADDRESS; VAR button, area: ADDRESS; BEGIN button := gtk_button_new(); area := gtk_drawing_area_new(); gtk_drawing_area_set_content_width(area, 24); gtk_drawing_area_set_content_height(area, 24); gtk_drawing_area_set_draw_func(area, ADR(DrawColor), color, NIL); gtk_button_set_child(button, area); RETURN button END ColorSwatch; PROCEDURE DoFlowBox (doWidget: ADDRESS) : ADDRESS; VAR sw, flowbox: ADDRESS; i: INTEGER; BEGIN IF flowBoxWindow = NIL THEN flowBoxWindow := gtk_window_new(); gtk_window_set_title(flowBoxWindow, "Flow Box"); gtk_window_set_default_size(flowBoxWindow, 320, 260); colors[0] := "AliceBlue"; colors[1] := "AntiqueWhite"; colors[2] := "aqua"; colors[3] := "blue"; colors[4] := "chartreuse"; colors[5] := "coral"; colors[6] := "crimson"; colors[7] := "DarkOrange"; colors[8] := "gold"; colors[9] := "ForestGreen"; colors[10] := "Orchid"; colors[11] := "SteelBlue"; flowbox := gtk_flow_box_new(); gtk_flow_box_set_selection_mode(flowbox, GtkSelectionNone); gtk_flow_box_set_max_children_per_line(flowbox, 8); FOR i := 0 TO 11 DO gtk_flow_box_append(flowbox, ColorSwatch(ADR(colors[i]))) END; sw := gtk_scrolled_window_new(); gtk_scrolled_window_set_child(sw, flowbox); gtk_window_set_child(flowBoxWindow, sw) END; IF gtk_widget_get_visible(flowBoxWindow) = 0 THEN gtk_widget_set_visible(flowBoxWindow, 1) ELSE gtk_window_destroy(flowBoxWindow); flowBoxWindow := NIL END; RETURN flowBoxWindow END DoFlowBox; (* ------------------------------------------------------------------ *) (* Expander *) (* ------------------------------------------------------------------ *) VAR expanderWindow: ADDRESS; PROCEDURE DoExpander (doWidget: ADDRESS) : ADDRESS; VAR box, expander, sw, tv: ADDRESS; BEGIN IF expanderWindow = NIL THEN expanderWindow := gtk_window_new(); gtk_window_set_title(expanderWindow, "Expander"); gtk_window_set_default_size(expanderWindow, 320, 260); box := gtk_box_new(GtkVertical, 10); gtk_box_set_spacing(box, 10); gtk_widget_set_margin_start(box, 10); gtk_widget_set_margin_end(box, 10); gtk_widget_set_margin_top(box, 10); gtk_widget_set_margin_bottom(box, 10); gtk_box_append(box, gtk_label_new("Here are some more details:")); expander := gtk_expander_new("Details:"); gtk_widget_set_vexpand(expander, 1); tv := gtk_text_view_new(); sw := gtk_scrolled_window_new(); gtk_scrolled_window_set_min_content_height(sw, 100); gtk_scrolled_window_set_has_frame(sw, 1); gtk_scrolled_window_set_child(sw, tv); gtk_widget_set_vexpand(sw, 1); gtk_expander_set_child(expander, sw); gtk_box_append(box, expander); gtk_window_set_child(expanderWindow, box) END; IF gtk_widget_get_visible(expanderWindow) = 0 THEN gtk_widget_set_visible(expanderWindow, 1) ELSE gtk_window_destroy(expanderWindow); expanderWindow := NIL END; RETURN expanderWindow END DoExpander; (* ------------------------------------------------------------------ *) (* CSS Basics *) (* ------------------------------------------------------------------ *) VAR cssWindow, cssBuffer, cssProvider: ADDRESS; CONST InitialCss = ".sample { color: #1c71d8; font-weight: bold; font-size: 20px; }"; PROCEDURE ApplyCss; VAR start, stop: GtkTextIter; text: ADDRESS; buf: ARRAY [0..1023] OF CHAR; BEGIN gtk_text_buffer_get_start_iter(cssBuffer, start); gtk_text_buffer_get_end_iter(cssBuffer, stop); text := gtk_text_buffer_get_text(cssBuffer, start, stop, 0); IF text # NIL THEN CStrToM2(text, buf); gtk_css_provider_load_from_string(cssProvider, buf); Free(text) END END ApplyCss; PROCEDURE OnCssChanged (buffer: ADDRESS; user: ADDRESS); BEGIN ApplyCss END OnCssChanged; PROCEDURE DoCss (doWidget: ADDRESS) : ADDRESS; VAR box, view, sw, sample: ADDRESS; BEGIN IF cssWindow = NIL THEN cssWindow := gtk_window_new(); gtk_window_set_title(cssWindow, "CSS Basics"); gtk_window_set_default_size(cssWindow, 400, 300); cssProvider := gtk_css_provider_new(); gtk_style_context_add_provider_for_display(gdk_display_get_default(), cssProvider, GtkStyleProviderPriorityApplication); sample := gtk_label_new("Sample label (styled by the CSS below)"); gtk_widget_add_css_class(sample, "sample"); cssBuffer := gtk_text_buffer_new(NIL); gtk_text_buffer_set_text(cssBuffer, InitialCss, -1); view := gtk_text_view_new_with_buffer(cssBuffer); sw := gtk_scrolled_window_new(); gtk_scrolled_window_set_child(sw, view); gtk_scrolled_window_set_min_content_height(sw, 120); gtk_widget_set_vexpand(sw, 1); box := gtk_box_new(GtkVertical, 10); gtk_box_set_spacing(box, 10); gtk_widget_set_margin_start(box, 10); gtk_widget_set_margin_end(box, 10); gtk_widget_set_margin_top(box, 10); gtk_widget_set_margin_bottom(box, 10); gtk_box_append(box, sample); gtk_box_append(box, sw); Connect2(cssBuffer, "changed", OnCssChanged, NIL); gtk_window_set_child(cssWindow, box); ApplyCss END; IF gtk_widget_get_visible(cssWindow) = 0 THEN gtk_widget_set_visible(cssWindow, 1) ELSE gtk_window_destroy(cssWindow); cssWindow := NIL END; RETURN cssWindow END DoCss; END DemoImpl.