Kaynağa Gözat

feat: curated utility/feedback widgets (adjustment, progress, spinner, separator, image)

- src/GtkAdjustment.def: value model (double/REAL) shared by ranges
- src/GtkProgressBar.def, GtkSpinner.def: feedback widgets
- src/GtkSeparator.def, GtkImage.def: decoration widgets
- tests/test_utility.mod: getter/setter round-trips
- examples/feedback.mod: timed progress bar + spinner

18 tests pass.
Eric Streit 3 gün önce
ebeveyn
işleme
5272a51453

+ 2 - 2
Makefile

@@ -18,8 +18,8 @@ OBJS    := $(OBJDIR)/GtkUtils.o $(OBJDIR)/GtkClosures.o
 
 INCLUDES := -I$(SRC_DIR) -I$(LIB_DIR)
 
-TESTS    := test_glib test_version test_widgets test_inputs test_inputs2 test_containers test_containers2 test_actions test_closure test_headerbar test_gio test_gerror test_filedialog test_views test_builder test_application
-EXAMPLES := hello counter inputs containers actions headerbar editor layouts files views builder
+TESTS    := test_glib test_version test_widgets test_inputs test_inputs2 test_containers test_containers2 test_actions test_closure test_headerbar test_gio test_gerror test_filedialog test_views test_builder test_utility test_application
+EXAMPLES := hello counter inputs containers actions headerbar editor layouts files views builder feedback
 
 # Include the libadwaita test/example only when libadwaita is installed.
 ifneq ($(ADWLIBS),)

+ 3 - 1
README.md

@@ -20,7 +20,7 @@ Verified with `gm2` 16.0.1 (experimental), GTK 4.18.6 and libadwaita 1.7.6.
 - **Pure-Modula-2 helpers**: `GtkUtils` (string/number conversion,
   `Connect`, `SetAccel`, ownership and `GError` helpers) and
   `GtkClosures` (per-connection signal contexts with cleanup).
-- **17 tests** and **11 runnable examples**.
+- **18 tests** and **13 runnable examples**.
 - **Generator + verifier**: `tools/gir2def.py` turns a `.gir` file into
   `.def` files; `tools/gir_verify.py` checks every generated identifier
   against the installed library. Verified clean across 8602 identifiers.
@@ -113,6 +113,7 @@ gm2 -fiso -Isrc -Ilib myprog.mod \
 | `files` | native file dialog → `GFile` |
 | `views` | `GtkListView` / `GtkGridView` / `GtkColumnView` from a store |
 | `builder` | load a window from a `.ui` file with `GtkBuilder` |
+| `feedback` | progress bar, spinner, separator, image |
 | `adw` | libadwaita preferences app (toolbar view, rows, toast, about) |
 
 ## Module reference
@@ -148,6 +149,7 @@ gm2 -fiso -Isrc -Ilib myprog.mod \
 | `GtkBox`, `GtkGrid`, `GtkFrame`, `GtkExpander`, `GtkPaned`, `GtkOverlay`, `GtkNotebook`, `GtkStack`, `GtkStackSwitcher`, `GtkScrolledWindow` | layout containers |
 | `GtkLabel`, `GtkButton`, `GtkCheckButton`, `GtkSwitch`, `GtkScale`, `GtkRange`, `GtkSpinButton`, `GtkEntry`, `GtkEditable`, `GtkSearchEntry`, `GtkDropDown` | controls |
 | `GtkTextView`, `GtkTextBuffer`, `GtkTextIter` | multi-line text |
+| `GtkAdjustment`, `GtkProgressBar`, `GtkSpinner`, `GtkSeparator`, `GtkImage` | value model and feedback/decoration widgets |
 | `GtkHeaderBar`, `GtkMenuButton`, `GtkPopoverMenuBar` | window chrome and menus |
 | `GtkBuilder` | load widget trees from `.ui` XML (with `GError`) |
 | `GtkListBox`, `GtkListView`, `GtkGridView`, `GtkColumnView`, `GtkColumnViewColumn`, `GtkColumnViewCell`, `GtkListItem`, `GtkSignalListItemFactory`, `GtkSelectionModel`, `GtkSingleSelection`, `GtkNoSelection` | lists, grids and tables |

+ 96 - 0
examples/feedback.mod

@@ -0,0 +1,96 @@
+MODULE feedback ;
+
+(*
+   m2-GTK4 example - progress, spinner, separator and image.
+
+   A progress bar fills from 0 to 1 on a timer while a spinner runs;
+   when it completes the spinner stops and the bar reads "Done".
+
+   Build:  make examples
+   Run:    ./build/examples/feedback
+*)
+
+FROM Gio IMPORT g_application_run;
+FROM GtkApplication IMPORT gtk_application_new;
+FROM GtkWindow IMPORT gtk_application_window_new, gtk_window_set_title,
+   gtk_window_set_default_size, gtk_window_set_child, gtk_window_present;
+FROM GtkBox IMPORT gtk_box_new, gtk_box_append, gtk_box_set_spacing;
+FROM GtkEnums IMPORT GtkVertical, GtkHorizontal;
+FROM GtkWidget IMPORT gtk_widget_set_margin_top,
+   gtk_widget_set_margin_bottom, gtk_widget_set_margin_start,
+   gtk_widget_set_margin_end;
+FROM GtkProgressBar IMPORT gtk_progress_bar_new, gtk_progress_bar_set_fraction,
+   gtk_progress_bar_set_show_text, gtk_progress_bar_set_text;
+FROM GtkSpinner IMPORT gtk_spinner_new, gtk_spinner_start, gtk_spinner_stop;
+FROM GtkSeparator IMPORT gtk_separator_new;
+FROM GtkImage IMPORT gtk_image_new_from_icon_name;
+FROM GLib IMPORT g_timeout_add, GSourceRemove, GSourceContinue;
+FROM GtkClosures IMPORT Connect2;
+FROM SYSTEM IMPORT ADDRESS;
+FROM libc IMPORT printf;
+
+CONST
+   AppId = "org.example.m2gtk4.Feedback";
+
+VAR
+   progress, spinner: ADDRESS;
+   fraction: REAL;
+
+PROCEDURE OnTick (data: ADDRESS) : INTEGER;
+BEGIN
+   fraction := fraction + 0.05;
+   IF fraction >= 1.0 THEN
+      gtk_progress_bar_set_fraction(progress, 1.0);
+      gtk_progress_bar_set_text(progress, "Done");
+      gtk_spinner_stop(spinner);
+      RETURN GSourceRemove
+   ELSE
+      gtk_progress_bar_set_fraction(progress, fraction);
+      RETURN GSourceContinue
+   END
+END OnTick;
+
+PROCEDURE OnActivate (application: ADDRESS; data: ADDRESS);
+VAR
+   window, box: ADDRESS;
+BEGIN
+   fraction := 0.0;
+
+   progress := gtk_progress_bar_new();
+   gtk_progress_bar_set_show_text(progress, 1);
+   gtk_progress_bar_set_fraction(progress, 0.0);
+
+   spinner := gtk_spinner_new();
+   gtk_spinner_start(spinner);
+
+   box := gtk_box_new(GtkVertical, 12);
+   gtk_box_set_spacing(box, 12);
+   gtk_widget_set_margin_top(box, 14);
+   gtk_widget_set_margin_bottom(box, 14);
+   gtk_widget_set_margin_start(box, 14);
+   gtk_widget_set_margin_end(box, 14);
+   gtk_box_append(box, gtk_image_new_from_icon_name("application-x-executable"));
+   gtk_box_append(box, progress);
+   gtk_box_append(box, gtk_separator_new(GtkHorizontal));
+   gtk_box_append(box, spinner);
+
+   window := gtk_application_window_new(application);
+   gtk_window_set_title(window, "m2-GTK4 Feedback");
+   gtk_window_set_default_size(window, 360, 200);
+   gtk_window_set_child(window, box);
+   gtk_window_present(window);
+
+   g_timeout_add(100, OnTick, NIL)
+END OnActivate;
+
+VAR
+   app: ADDRESS;
+BEGIN
+   app := gtk_application_new(AppId, 0);
+   IF app = NIL THEN
+      printf("gtk_application_new failed\n");
+      HALT(1)
+   END;
+   Connect2(app, "activate", OnActivate, NIL);
+   g_application_run(app, 0, NIL)
+END feedback.

+ 81 - 0
src/GtkAdjustment.def

@@ -0,0 +1,81 @@
+DEFINITION MODULE FOR "C" GtkAdjustment ;
+
+(*
+   m2-GTK4 - GtkAdjustment: the value model shared by ranges, spin
+   buttons and scrollbars.  Mapped onto <gtk/gtkadjustment.h>.
+
+   Values are C `double` (REAL).  Signal: "value-changed".
+*)
+
+FROM SYSTEM IMPORT ADDRESS;
+
+EXPORT UNQUALIFIED
+   GtkAdjustment,
+   gtk_adjustment_new,
+   gtk_adjustment_get_value, gtk_adjustment_set_value,
+   gtk_adjustment_get_lower, gtk_adjustment_set_lower,
+   gtk_adjustment_get_upper, gtk_adjustment_set_upper,
+   gtk_adjustment_get_step_increment, gtk_adjustment_set_step_increment,
+   gtk_adjustment_get_page_increment, gtk_adjustment_set_page_increment,
+   gtk_adjustment_get_page_size, gtk_adjustment_set_page_size,
+   gtk_adjustment_clamp_page;
+
+
+TYPE
+   GtkAdjustment = ADDRESS;
+
+(* GtkAdjustment *gtk_adjustment_new(double value, double lower,
+                                      double upper, double step_increment,
+                                      double page_increment, double page_size) *)
+PROCEDURE gtk_adjustment_new (value, lower, upper,
+                              step_increment, page_increment,
+                              page_size: REAL) : GtkAdjustment;
+
+(* double gtk_adjustment_get_value(GtkAdjustment *adjustment) *)
+PROCEDURE gtk_adjustment_get_value (adjustment: GtkAdjustment) : REAL;
+
+(* void gtk_adjustment_set_value(GtkAdjustment *adjustment, double value) *)
+PROCEDURE gtk_adjustment_set_value (adjustment: GtkAdjustment; value: REAL);
+
+(* double gtk_adjustment_get_lower(GtkAdjustment *adjustment) *)
+PROCEDURE gtk_adjustment_get_lower (adjustment: GtkAdjustment) : REAL;
+
+(* void gtk_adjustment_set_lower(GtkAdjustment *adjustment, double lower) *)
+PROCEDURE gtk_adjustment_set_lower (adjustment: GtkAdjustment; lower: REAL);
+
+(* double gtk_adjustment_get_upper(GtkAdjustment *adjustment) *)
+PROCEDURE gtk_adjustment_get_upper (adjustment: GtkAdjustment) : REAL;
+
+(* void gtk_adjustment_set_upper(GtkAdjustment *adjustment, double upper) *)
+PROCEDURE gtk_adjustment_set_upper (adjustment: GtkAdjustment; upper: REAL);
+
+(* double gtk_adjustment_get_step_increment(GtkAdjustment *adjustment) *)
+PROCEDURE gtk_adjustment_get_step_increment (adjustment: GtkAdjustment) : REAL;
+
+(* void gtk_adjustment_set_step_increment(GtkAdjustment *adjustment,
+                                           double step_increment) *)
+PROCEDURE gtk_adjustment_set_step_increment (adjustment: GtkAdjustment;
+                                             step_increment: REAL);
+
+(* double gtk_adjustment_get_page_increment(GtkAdjustment *adjustment) *)
+PROCEDURE gtk_adjustment_get_page_increment (adjustment: GtkAdjustment) : REAL;
+
+(* void gtk_adjustment_set_page_increment(GtkAdjustment *adjustment,
+                                           double page_increment) *)
+PROCEDURE gtk_adjustment_set_page_increment (adjustment: GtkAdjustment;
+                                             page_increment: REAL);
+
+(* double gtk_adjustment_get_page_size(GtkAdjustment *adjustment) *)
+PROCEDURE gtk_adjustment_get_page_size (adjustment: GtkAdjustment) : REAL;
+
+(* void gtk_adjustment_set_page_size(GtkAdjustment *adjustment,
+                                      double page_size) *)
+PROCEDURE gtk_adjustment_set_page_size (adjustment: GtkAdjustment;
+                                        page_size: REAL);
+
+(* void gtk_adjustment_clamp_page(GtkAdjustment *adjustment,
+                                   double lower, double upper) *)
+PROCEDURE gtk_adjustment_clamp_page (adjustment: GtkAdjustment;
+                                     lower, upper: REAL);
+
+END GtkAdjustment.

+ 39 - 0
src/GtkImage.def

@@ -0,0 +1,39 @@
+DEFINITION MODULE FOR "C" GtkImage ;
+
+(*
+   m2-GTK4 - GtkImage: displays an icon or a file image.  Mapped onto
+   <gtk/gtkimage.h>.
+
+   gtk_image_get_icon_name() returns a borrowed string (the icon name of
+   an icon-name image, or NIL otherwise).
+*)
+
+FROM SYSTEM IMPORT ADDRESS;
+
+EXPORT UNQUALIFIED
+   GtkImage,
+   gtk_image_new,
+   gtk_image_new_from_icon_name, gtk_image_new_from_file,
+   gtk_image_set_from_icon_name, gtk_image_get_icon_name;
+
+
+TYPE
+   GtkImage = ADDRESS;
+
+(* GtkWidget *gtk_image_new(void) *)
+PROCEDURE gtk_image_new () : GtkImage;
+
+(* GtkWidget *gtk_image_new_from_icon_name(const char *icon_name) *)
+PROCEDURE gtk_image_new_from_icon_name (icon_name: ARRAY OF CHAR) : GtkImage;
+
+(* GtkWidget *gtk_image_new_from_file(const char *filename) *)
+PROCEDURE gtk_image_new_from_file (filename: ARRAY OF CHAR) : GtkImage;
+
+(* void gtk_image_set_from_icon_name(GtkImage *image, const char *icon_name) *)
+PROCEDURE gtk_image_set_from_icon_name (image: GtkImage;
+                                        icon_name: ARRAY OF CHAR);
+
+(* const char *gtk_image_get_icon_name(GtkImage *image) *)
+PROCEDURE gtk_image_get_icon_name (image: GtkImage) : ADDRESS;
+
+END GtkImage.

+ 62 - 0
src/GtkProgressBar.def

@@ -0,0 +1,62 @@
+DEFINITION MODULE FOR "C" GtkProgressBar ;
+
+(*
+   m2-GTK4 - GtkProgressBar: a progress indicator.  Mapped onto
+   <gtk/gtkprogressbar.h>.
+
+   Either set a fraction (0.0 .. 1.0) for determinate progress, or call
+   pulse() repeatedly (with a pulse step) for indeterminate "busy"
+   progress.  The default text shows a percentage unless set_text() is
+   used.
+*)
+
+FROM SYSTEM IMPORT ADDRESS;
+
+EXPORT UNQUALIFIED
+   GtkProgressBar,
+   gtk_progress_bar_new,
+   gtk_progress_bar_set_fraction, gtk_progress_bar_get_fraction,
+   gtk_progress_bar_set_pulse_step, gtk_progress_bar_pulse,
+   gtk_progress_bar_set_show_text, gtk_progress_bar_get_show_text,
+   gtk_progress_bar_set_text, gtk_progress_bar_get_text,
+   gtk_progress_bar_set_inverted;
+
+
+TYPE
+   GtkProgressBar = ADDRESS;
+
+(* GtkWidget *gtk_progress_bar_new(void) *)
+PROCEDURE gtk_progress_bar_new () : GtkProgressBar;
+
+(* void gtk_progress_bar_set_fraction(GtkProgressBar *pbar, double fraction) *)
+PROCEDURE gtk_progress_bar_set_fraction (pbar: GtkProgressBar; fraction: REAL);
+
+(* double gtk_progress_bar_get_fraction(GtkProgressBar *pbar) *)
+PROCEDURE gtk_progress_bar_get_fraction (pbar: GtkProgressBar) : REAL;
+
+(* void gtk_progress_bar_set_pulse_step(GtkProgressBar *pbar, double fraction) *)
+PROCEDURE gtk_progress_bar_set_pulse_step (pbar: GtkProgressBar;
+                                           fraction: REAL);
+
+(* void gtk_progress_bar_pulse(GtkProgressBar *pbar) *)
+PROCEDURE gtk_progress_bar_pulse (pbar: GtkProgressBar);
+
+(* void gtk_progress_bar_set_show_text(GtkProgressBar *pbar, gboolean show_text) *)
+PROCEDURE gtk_progress_bar_set_show_text (pbar: GtkProgressBar;
+                                          show_text: INTEGER);
+
+(* gboolean gtk_progress_bar_get_show_text(GtkProgressBar *pbar) *)
+PROCEDURE gtk_progress_bar_get_show_text (pbar: GtkProgressBar) : [ INTEGER ];
+
+(* void gtk_progress_bar_set_text(GtkProgressBar *pbar, const char *text) *)
+PROCEDURE gtk_progress_bar_set_text (pbar: GtkProgressBar;
+                                     text: ARRAY OF CHAR);
+
+(* const char *gtk_progress_bar_get_text(GtkProgressBar *pbar) *)
+PROCEDURE gtk_progress_bar_get_text (pbar: GtkProgressBar) : ADDRESS;
+
+(* void gtk_progress_bar_set_inverted(GtkProgressBar *pbar, gboolean inverted) *)
+PROCEDURE gtk_progress_bar_set_inverted (pbar: GtkProgressBar;
+                                         inverted: INTEGER);
+
+END GtkProgressBar.

+ 22 - 0
src/GtkSeparator.def

@@ -0,0 +1,22 @@
+DEFINITION MODULE FOR "C" GtkSeparator ;
+
+(*
+   m2-GTK4 - GtkSeparator: a horizontal or vertical dividing line.
+   Mapped onto <gtk/gtkseparator.h>.
+*)
+
+FROM SYSTEM IMPORT ADDRESS;
+FROM GtkEnums IMPORT GtkOrientation;
+
+EXPORT UNQUALIFIED
+   GtkSeparator,
+   gtk_separator_new;
+
+
+TYPE
+   GtkSeparator = ADDRESS;
+
+(* GtkWidget *gtk_separator_new(GtkOrientation orientation) *)
+PROCEDURE gtk_separator_new (orientation: GtkOrientation) : GtkSeparator;
+
+END GtkSeparator.

+ 35 - 0
src/GtkSpinner.def

@@ -0,0 +1,35 @@
+DEFINITION MODULE FOR "C" GtkSpinner ;
+
+(*
+   m2-GTK4 - GtkSpinner: an animated busy indicator.  Mapped onto
+   <gtk/gtkspinner.h>.
+*)
+
+FROM SYSTEM IMPORT ADDRESS;
+
+EXPORT UNQUALIFIED
+   GtkSpinner,
+   gtk_spinner_new,
+   gtk_spinner_start, gtk_spinner_stop,
+   gtk_spinner_set_spinning, gtk_spinner_get_spinning;
+
+
+TYPE
+   GtkSpinner = ADDRESS;
+
+(* GtkWidget *gtk_spinner_new(void) *)
+PROCEDURE gtk_spinner_new () : GtkSpinner;
+
+(* void gtk_spinner_start(GtkSpinner *spinner) *)
+PROCEDURE gtk_spinner_start (spinner: GtkSpinner);
+
+(* void gtk_spinner_stop(GtkSpinner *spinner) *)
+PROCEDURE gtk_spinner_stop (spinner: GtkSpinner);
+
+(* void gtk_spinner_set_spinning(GtkSpinner *spinner, gboolean spinning) *)
+PROCEDURE gtk_spinner_set_spinning (spinner: GtkSpinner; spinning: INTEGER);
+
+(* gboolean gtk_spinner_get_spinning(GtkSpinner *spinner) *)
+PROCEDURE gtk_spinner_get_spinning (spinner: GtkSpinner) : [ INTEGER ];
+
+END GtkSpinner.

+ 2 - 2
tests/run_tests.sh

@@ -13,7 +13,7 @@ CFLAGS=$(pkg-config --cflags gtk4)
 LIBS=$(pkg-config --libs gtk4)
 
 # libadwaita is optional: add its flags and test when available.
-TESTS="test_glib test_version test_widgets test_inputs test_inputs2 test_containers test_containers2 test_actions test_closure test_headerbar test_gio test_gerror test_filedialog test_views test_builder test_application"
+TESTS="test_glib test_version test_widgets test_inputs test_inputs2 test_containers test_containers2 test_actions test_closure test_headerbar test_gio test_gerror test_filedialog test_views test_builder test_utility test_application"
 if pkg-config --exists libadwaita-1 2>/dev/null; then
   CFLAGS="$CFLAGS $(pkg-config --cflags libadwaita-1)"
   LIBS="$LIBS $(pkg-config --libs libadwaita-1)"
@@ -45,7 +45,7 @@ for t in $TESTS; do
   $GM2 -I"$ROOT/src" -I"$ROOT/lib" $GM2FLAGS $CFLAGS \
        "$ROOT/tests/$t.mod" "$OBJ/GtkUtils.o" "$OBJ/GtkClosures.o" -o "$BIN/$t" $LIBS
   case "$t" in
-    test_widgets|test_inputs|test_inputs2|test_containers|test_containers2|test_actions|test_closure|test_headerbar|test_filedialog|test_views|test_builder|test_adw|test_application) needs_display=1 ;;
+    test_widgets|test_inputs|test_inputs2|test_containers|test_containers2|test_actions|test_closure|test_headerbar|test_filedialog|test_views|test_builder|test_utility|test_adw|test_application) needs_display=1 ;;
     *) needs_display=0 ;;
   esac
   if [ "$needs_display" = 1 ] && [ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ]; then

+ 126 - 0
tests/test_utility.mod

@@ -0,0 +1,126 @@
+MODULE test_utility ;
+
+(*
+   m2-GTK4 test - small utility/feedback widgets: GtkAdjustment,
+   GtkProgressBar, GtkSpinner, GtkSeparator and GtkImage.
+   Requires a display.
+*)
+
+FROM Gio IMPORT g_application_run, g_application_quit;
+FROM GtkApplication IMPORT gtk_application_new;
+FROM GtkWindow IMPORT gtk_application_window_new, gtk_window_set_child;
+FROM GtkBox IMPORT gtk_box_new, gtk_box_append;
+FROM GtkEnums IMPORT GtkVertical, GtkHorizontal;
+FROM GtkAdjustment IMPORT gtk_adjustment_new, gtk_adjustment_get_value,
+   gtk_adjustment_set_value, gtk_adjustment_get_lower, gtk_adjustment_get_upper,
+   gtk_adjustment_set_upper, gtk_adjustment_get_step_increment,
+   gtk_adjustment_get_page_increment, gtk_adjustment_get_page_size,
+   gtk_adjustment_clamp_page;
+FROM GtkProgressBar IMPORT gtk_progress_bar_new, gtk_progress_bar_set_fraction,
+   gtk_progress_bar_get_fraction, gtk_progress_bar_set_show_text,
+   gtk_progress_bar_get_show_text, gtk_progress_bar_set_text,
+   gtk_progress_bar_get_text, gtk_progress_bar_pulse;
+FROM GtkSpinner IMPORT gtk_spinner_new, gtk_spinner_start, gtk_spinner_stop,
+   gtk_spinner_set_spinning, gtk_spinner_get_spinning;
+FROM GtkSeparator IMPORT gtk_separator_new;
+FROM GtkImage IMPORT gtk_image_new_from_icon_name, gtk_image_get_icon_name;
+FROM GtkUtils IMPORT Connect, CStrToM2;
+FROM SYSTEM IMPORT ADDRESS, ADR;
+FROM libc IMPORT printf;
+
+CONST
+   AppId = "org.example.m2gtk4.TestUtility";
+
+VAR
+   app: ADDRESS;
+   passed: BOOLEAN;
+
+PROCEDURE OnActivate (application: ADDRESS; data: ADDRESS);
+VAR
+   window, box, adj, pbar, spinner, sep, image: ADDRESS;
+   buf: ARRAY [0..63] OF CHAR;
+   ok: BOOLEAN;
+BEGIN
+   ok := TRUE;
+
+   (* --- GtkAdjustment --- *)
+   adj := gtk_adjustment_new(5.0, 0.0, 100.0, 1.0, 10.0, 20.0);
+   IF ABS(gtk_adjustment_get_value(adj) - 5.0) > 0.001 THEN ok := FALSE END;
+   IF ABS(gtk_adjustment_get_lower(adj) - 0.0) > 0.001 THEN ok := FALSE END;
+   IF ABS(gtk_adjustment_get_upper(adj) - 100.0) > 0.001 THEN ok := FALSE END;
+   IF ABS(gtk_adjustment_get_step_increment(adj) - 1.0) > 0.001 THEN ok := FALSE END;
+   IF ABS(gtk_adjustment_get_page_increment(adj) - 10.0) > 0.001 THEN ok := FALSE END;
+   IF ABS(gtk_adjustment_get_page_size(adj) - 20.0) > 0.001 THEN ok := FALSE END;
+   gtk_adjustment_set_value(adj, 50.0);
+   IF ABS(gtk_adjustment_get_value(adj) - 50.0) > 0.001 THEN
+      printf("adjustment value wrong\n"); ok := FALSE
+   END;
+   gtk_adjustment_set_upper(adj, 200.0);
+   IF ABS(gtk_adjustment_get_upper(adj) - 200.0) > 0.001 THEN ok := FALSE END;
+   gtk_adjustment_clamp_page(adj, 0.0, 30.0);
+
+   (* --- GtkProgressBar --- *)
+   pbar := gtk_progress_bar_new();
+   gtk_progress_bar_set_fraction(pbar, 0.25);
+   IF ABS(gtk_progress_bar_get_fraction(pbar) - 0.25) > 0.001 THEN
+      printf("progress fraction wrong\n"); ok := FALSE
+   END;
+   gtk_progress_bar_set_show_text(pbar, 1);
+   IF gtk_progress_bar_get_show_text(pbar) # 1 THEN ok := FALSE END;
+   gtk_progress_bar_set_text(pbar, "25 percent");
+   CStrToM2(gtk_progress_bar_get_text(pbar), buf);
+   IF (buf[0] # '2') OR (buf[10] # 0C) THEN
+      printf("progress text wrong: [%s]\n", buf); ok := FALSE
+   END;
+   gtk_progress_bar_pulse(pbar);
+
+   (* --- GtkSpinner --- *)
+   spinner := gtk_spinner_new();
+   gtk_spinner_start(spinner);
+   IF gtk_spinner_get_spinning(spinner) # 1 THEN ok := FALSE END;
+   gtk_spinner_stop(spinner);
+   IF gtk_spinner_get_spinning(spinner) # 0 THEN ok := FALSE END;
+   gtk_spinner_set_spinning(spinner, 1);
+   IF gtk_spinner_get_spinning(spinner) # 1 THEN ok := FALSE END;
+
+   (* --- GtkSeparator / GtkImage --- *)
+   sep := gtk_separator_new(GtkHorizontal);
+   IF sep = NIL THEN printf("separator NIL\n"); ok := FALSE END;
+   image := gtk_image_new_from_icon_name("open-menu-symbolic");
+   CStrToM2(gtk_image_get_icon_name(image), buf);
+   IF (buf[0] # 'o') OR (buf[18] # 0C) THEN
+      printf("image icon name wrong: [%s]\n", buf); ok := FALSE
+   END;
+
+   box := gtk_box_new(GtkVertical, 6);
+   gtk_box_append(box, pbar);
+   gtk_box_append(box, spinner);
+   gtk_box_append(box, sep);
+   gtk_box_append(box, image);
+   window := gtk_application_window_new(application);
+   gtk_window_set_child(window, box);
+
+   passed := ok;
+   g_application_quit(application)
+END OnActivate;
+
+VAR
+   rc: INTEGER;
+BEGIN
+   passed := FALSE;
+   app := gtk_application_new(AppId, 0);
+   IF app = NIL THEN
+      printf("test_utility: FAIL (no app)\n");
+      HALT(1)
+   END;
+   Connect(app, "activate", ADR(OnActivate), NIL);
+   rc := g_application_run(app, 0, NIL);
+
+   IF passed THEN
+      printf("test_utility: PASS\n");
+      HALT(0)
+   ELSE
+      printf("test_utility: FAIL\n");
+      HALT(1)
+   END
+END test_utility.