From 286b7bbbfcf6745d08e8e211cac70ad4865af689 Mon Sep 17 00:00:00 2001 From: Philipp Doerner Date: Sat, 14 Oct 2023 18:32:01 +0100 Subject: [PATCH 1/8] Move gtk.nim into bindings dir --- book/internals/hooks/after_build_hook.nim | 2 +- book/internals/hooks/before_build_hook.nim | 2 +- book/internals/hooks/build_hook.nim | 2 +- book/internals/hooks/event_hooks.nim | 4 +++- book/internals/hooks/read_hook.nim | 3 ++- owlkettle.nim | 3 ++- owlkettle/adw.nim | 3 ++- owlkettle/{ => bindings}/gtk.nim | 2 +- owlkettle/mainloop.nim | 3 ++- owlkettle/widgetdef.nim | 3 ++- owlkettle/widgets.nim | 3 ++- owlkettle/widgetutils.nim | 3 ++- 12 files changed, 21 insertions(+), 12 deletions(-) rename owlkettle/{ => bindings}/gtk.nim (99%) diff --git a/book/internals/hooks/after_build_hook.nim b/book/internals/hooks/after_build_hook.nim index bfb93441..5422e943 100644 --- a/book/internals/hooks/after_build_hook.nim +++ b/book/internals/hooks/after_build_hook.nim @@ -15,7 +15,7 @@ Let's return to our earlier renderable example and write it so that the parent-w nbCode: import owlkettle - import owlkettle/gtk + import owlkettle/bindings/gtk import std/json renderable MyRenderable: diff --git a/book/internals/hooks/before_build_hook.nim b/book/internals/hooks/before_build_hook.nim index fb5e227a..e484e2cc 100644 --- a/book/internals/hooks/before_build_hook.nim +++ b/book/internals/hooks/before_build_hook.nim @@ -14,7 +14,7 @@ Here a simple code-example for writing a `beforeBuild` hook: nbCode: import owlkettle - import owlkettle/gtk + import owlkettle/bindings/gtk import std/json renderable MyRenderable: diff --git a/book/internals/hooks/build_hook.nim b/book/internals/hooks/build_hook.nim index 27043151..1e830854 100644 --- a/book/internals/hooks/build_hook.nim +++ b/book/internals/hooks/build_hook.nim @@ -27,7 +27,7 @@ nbCode: # main.nim import std/json import owlkettle - import owlkettle/gtk + import owlkettle/bindings/gtk type Config = object name: string diff --git a/book/internals/hooks/event_hooks.nim b/book/internals/hooks/event_hooks.nim index b7dd2f69..b73adbf3 100644 --- a/book/internals/hooks/event_hooks.nim +++ b/book/internals/hooks/event_hooks.nim @@ -14,7 +14,9 @@ Here a minimal example of a custom button widget that provides a `clicked` event nbCode: import owlkettle import std/tables - import owlkettle/[widgetutils, gtk] + import owlkettle/widgetutils + import owlkettle/bindings/gtk + renderable MyButton of BaseWidget: proc clicked() diff --git a/book/internals/hooks/read_hook.nim b/book/internals/hooks/read_hook.nim index 2a674e5d..07b130b9 100644 --- a/book/internals/hooks/read_hook.nim +++ b/book/internals/hooks/read_hook.nim @@ -12,7 +12,8 @@ Let's look at a minimal example from the `ColorChooserDialog` widget: nbCode: import owlkettle - import owlkettle/[gtk, widgetdef] + import owlkettle/widgetdef + import owlkettle/bindings/gtk # The custom widget renderable MyColorChooserDialog of BuiltinDialog: diff --git a/owlkettle.nim b/owlkettle.nim index 27fd82d3..725f76b2 100644 --- a/owlkettle.nim +++ b/owlkettle.nim @@ -20,7 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import owlkettle/[gtk, widgetutils, widgetdef, widgets, guidsl, mainloop] +import owlkettle/[widgetutils, widgetdef, widgets, guidsl, mainloop] +import owlkettle/bindings/gtk export widgetdef except build_bin, update_bin export widgets, guidsl export Align diff --git a/owlkettle/adw.nim b/owlkettle/adw.nim index c951b5b2..0b0c53a4 100644 --- a/owlkettle/adw.nim +++ b/owlkettle/adw.nim @@ -24,7 +24,8 @@ when defined(nimPreviewSlimSystem): import std/assertions -import gtk, widgetdef, widgets, mainloop, widgetutils +import widgetdef, widgets, mainloop, widgetutils +import bindings/gtk when defined(owlkettleDocs) and isMainModule: echo "# Libadwaita Widgets\n\n" diff --git a/owlkettle/gtk.nim b/owlkettle/bindings/gtk.nim similarity index 99% rename from owlkettle/gtk.nim rename to owlkettle/bindings/gtk.nim index 9d1c58e8..70428368 100644 --- a/owlkettle/gtk.nim +++ b/owlkettle/bindings/gtk.nim @@ -23,7 +23,7 @@ # Bindings for GTK 4 import std/[os] -import ./common +import ../common import std/strutils as strutils diff --git a/owlkettle/mainloop.nim b/owlkettle/mainloop.nim index 21947994..bb286650 100644 --- a/owlkettle/mainloop.nim +++ b/owlkettle/mainloop.nim @@ -20,7 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import gtk, widgetdef, common +import widgetdef, common +import bindings/gtk type StylesheetObj = object diff --git a/owlkettle/widgetdef.nim b/owlkettle/widgetdef.nim index 6c230f8e..e9245371 100644 --- a/owlkettle/widgetdef.nim +++ b/owlkettle/widgetdef.nim @@ -25,7 +25,8 @@ import std/[macros, strutils, tables] when defined(nimPreviewSlimSystem): import std/assertions -import gtk, common +import common +import bindings/gtk type Widget* = ref object of RootObj diff --git a/owlkettle/widgets.nim b/owlkettle/widgets.nim index 99376747..2edef4cf 100644 --- a/owlkettle/widgets.nim +++ b/owlkettle/widgets.nim @@ -25,7 +25,8 @@ import std/[unicode, sets, tables, options, asyncfutures, hashes, times] when defined(nimPreviewSlimSystem): import std/assertions -import gtk, widgetdef, cairo, widgetutils, common +import widgetdef, cairo, widgetutils, common +import bindings/gtk customPragmas() when defined(owlkettleDocs) and isMainModule: diff --git a/owlkettle/widgetutils.nim b/owlkettle/widgetutils.nim index 48839e2b..68b2874c 100644 --- a/owlkettle/widgetutils.nim +++ b/owlkettle/widgetutils.nim @@ -23,7 +23,8 @@ # Utilities for wrapping widgets import std/[sets] -import gtk, widgetdef, common +import widgetdef, common +import bindings/gtk customPragmas() From 95d7138061598312b1510e24663d0a8fdd7d6e16 Mon Sep 17 00:00:00 2001 From: Philipp Doerner Date: Sat, 14 Oct 2023 18:39:26 +0100 Subject: [PATCH 2/8] Refactor adwaita bindings into a binding module --- owlkettle/adw.nim | 144 +------------------------------------ owlkettle/bindings/adw.nim | 141 ++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 143 deletions(-) create mode 100644 owlkettle/bindings/adw.nim diff --git a/owlkettle/adw.nim b/owlkettle/adw.nim index 0b0c53a4..e7120b29 100644 --- a/owlkettle/adw.nim +++ b/owlkettle/adw.nim @@ -25,156 +25,14 @@ when defined(nimPreviewSlimSystem): import std/assertions import widgetdef, widgets, mainloop, widgetutils -import bindings/gtk +import ./bindings/[adw, gtk] when defined(owlkettleDocs) and isMainModule: echo "# Libadwaita Widgets\n\n" -const AdwMajor {.intdefine: "adwmajor".}: int = 1 ## Specifies the minimum Adwaita major version required to run an application. Overwriteable via `-d:adwmajor=X`. Defaults to 1. -const AdwMinor {.intdefine: "adwminor".}: int = 0 ## Specifies the minimum Adwaita minor version required to run an application. Overwriteable via `-d:adwinor=X`. Defaults to 0. -const AdwVersion = (AdwMajor, AdwMinor) - -{.passl: "-ladwaita-1".} - -type - StyleManager = distinct pointer - - ColorScheme* = enum - ColorSchemeDefault, - ColorSchemeForceLight, - ColorSchemeForceDark, - ColorSchemePreferDark, - ColorSchemePreferLight - - FlapFoldPolicy* = enum - FlapFoldNever, - FlapFoldAlways, - FlapFoldAuto - - FoldThresholdPolicy* = enum - FoldThresholdMinimum, - FoldThresholdNatural - - FlapTransitionType* = enum - FlapTransitionOver - FlapTransitionUnder - FlapTransitionSlide - -{.push importc, cdecl.} -# Adw -proc adw_init() - -# Adw.StyleManager -proc adw_style_manager_get_default(): StyleManager -proc adw_style_manager_set_color_scheme(manager: StyleManager, colorScheme: ColorScheme) - -# Adw.Window -proc adw_window_new(): GtkWidget -proc adw_window_set_content(window, content: GtkWidget) - -# Adw.WindowTitle -proc adw_window_title_new(title, subtitle: cstring): GtkWidget -proc adw_window_title_set_title(widget: GtkWidget, title: cstring) -proc adw_window_title_set_subtitle(widget: GtkWidget, subtitle: cstring) - -# Adw.Avatar -proc adw_avatar_new(size: cint, text: cstring, showInitials: cbool): GtkWidget -proc adw_avatar_set_show_initials(avatar: GtkWidget, value: cbool) -proc adw_avatar_set_size(avatar: GtkWidget, size: cint) -proc adw_avatar_set_text(avatar: GtkWidget, text: cstring) -proc adw_avatar_set_icon_name(avatar: GtkWidget, iconName: cstring) - -# Adw.Clamp -proc adw_clamp_new(): GtkWidget -proc adw_clamp_set_child(clamp, child: GtkWidget) -proc adw_clamp_set_maximum_size(clamp: GtkWidget, size: cint) - -# Adw.PreferencesGroup -proc adw_preferences_group_new(): GtkWidget -proc adw_preferences_group_add(group, child: GtkWidget) -proc adw_preferences_group_remove(group, child: GtkWidget) -proc adw_preferences_group_set_header_suffix(group, child: GtkWidget) -proc adw_preferences_group_set_description(group: GtkWidget, descr: cstring) -proc adw_preferences_group_set_title(group: GtkWidget, title: cstring) - -# Adw.PreferencesRow -proc adw_preferences_row_new(): GtkWidget -proc adw_preferences_row_set_title(row: GtkWidget, title: cstring) - -# Adw.ActionRow -proc adw_action_row_new(): GtkWidget -proc adw_action_row_set_subtitle(row: GtkWidget, subtitle: cstring) -proc adw_action_row_add_prefix(row, child: GtkWidget) -proc adw_action_row_add_suffix(row, child: GtkWidget) -proc adw_action_row_remove(row, child: GtkWidget) -proc adw_action_row_set_activatable_widget(row, child: GtkWidget) - -# Adw.ExpanderRow -proc adw_expander_row_new(): GtkWidget -proc adw_expander_row_set_subtitle(row: GtkWidget, subtitle: cstring) -proc adw_expander_row_add_action(row, child: GtkWidget) -proc adw_expander_row_add_prefix(row, child: GtkWidget) -proc adw_expander_row_add_row(expanderRow, row: GtkWidget) -proc adw_expander_row_remove(row, child: GtkWidget) - -# Adw.ComboRow -proc adw_combo_row_new(): GtkWidget -proc adw_combo_row_set_model*(comboRow: GtkWidget, model: GListModel) -proc adw_combo_row_set_selected*(comboRow: GtkWidget, selected: cuint) -proc adw_combo_row_get_selected*(comboRow: GtkWidget): cuint - -when AdwVersion >= (1, 2): - # Adw.EntryRow - proc adw_entry_row_new(): GtkWidget - proc adw_entry_row_add_suffix(row, child: GtkWidget) - proc adw_entry_row_remove(row, child: GtkWidget) - -# Adw.Flap -proc adw_flap_new(): GtkWidget -proc adw_flap_set_content(flap, content: GtkWidget) -proc adw_flap_set_flap(flap, child: GtkWidget) -proc adw_flap_set_separator(flap, child: GtkWidget) -proc adw_flap_set_fold_policy(flap: GtkWidget, foldPolicy: FlapFoldPolicy) -proc adw_flap_set_fold_threshold_policy(flap: GtkWidget, foldThresholdPolicy: FoldThresholdPolicy) -proc adw_flap_set_transition_type(flap: GtkWidget, transitionType: FlapTransitionType) -proc adw_flap_set_reveal_flap(flap: GtkWidget, revealed: cbool) -proc adw_flap_set_modal(flap: GtkWidget, modal: cbool) -proc adw_flap_set_locked(flap: GtkWidget, locked: cbool) -proc adw_flap_set_swipe_to_open(flap: GtkWidget, swipe: cbool) -proc adw_flap_set_swipe_to_close(flap: GtkWidget, swipe: cbool) -proc adw_flap_get_reveal_flap(flap: GtkWidget): cbool -proc adw_flap_get_folded(flap: GtkWidget): cbool - -# Adw.SplitButton -proc adw_split_button_new(): GtkWidget -proc adw_split_button_set_child(button, child: GtkWidget) -proc adw_split_button_set_popover(button, child: GtkWidget) - -# Adw.StatusPage -proc adw_status_page_new(): GtkWidget -proc adw_status_page_set_child(self: GtkWidget, child: GtkWidget) -proc adw_status_page_set_description(self: GtkWidget, description: cstring) -proc adw_status_page_set_icon_name(self: GtkWidget, icon_name: cstring) -proc adw_status_page_set_paintable(self: GtkWidget, paintable: GtkWidget) -proc adw_status_page_set_title(self: GtkWidget, title: cstring) - -when AdwVersion >= (1, 2): - # Adw.AboutWindow - proc adw_about_window_new(): GtkWidget - proc adw_about_window_set_application_name(window: GtkWidget, value: cstring) - proc adw_about_window_set_developer_name(window: GtkWidget, value: cstring) - proc adw_about_window_set_version(window: GtkWidget, value: cstring) - proc adw_about_window_set_support_url(window: GtkWidget, value: cstring) - proc adw_about_window_set_issue_url(window: GtkWidget, value: cstring) - proc adw_about_window_set_website(window: GtkWidget, value: cstring) - proc adw_about_window_set_copyright(window: GtkWidget, value: cstring) - proc adw_about_window_set_license(window: GtkWidget, value: cstring) -{.pop.} - renderable WindowSurface of BaseWindow: ## A Window that does not have a title bar. ## A WindowSurface is equivalent to an `Adw.Window`. - content: Widget hooks: diff --git a/owlkettle/bindings/adw.nim b/owlkettle/bindings/adw.nim new file mode 100644 index 00000000..6cac1224 --- /dev/null +++ b/owlkettle/bindings/adw.nim @@ -0,0 +1,141 @@ +import ./gtk + +const AdwMajor {.intdefine: "adwmajor".}: int = 1 ## Specifies the minimum Adwaita major version required to run an application. Overwriteable via `-d:adwmajor=X`. Defaults to 1. +const AdwMinor {.intdefine: "adwminor".}: int = 0 ## Specifies the minimum Adwaita minor version required to run an application. Overwriteable via `-d:adwinor=X`. Defaults to 0. +const AdwVersion* = (AdwMajor, AdwMinor) + +{.passl: "-ladwaita-1".} + +type + StyleManager* = distinct pointer + + ColorScheme* = enum + ColorSchemeDefault, + ColorSchemeForceLight, + ColorSchemeForceDark, + ColorSchemePreferDark, + ColorSchemePreferLight + + FlapFoldPolicy* = enum + FlapFoldNever, + FlapFoldAlways, + FlapFoldAuto + + FoldThresholdPolicy* = enum + FoldThresholdMinimum, + FoldThresholdNatural + + FlapTransitionType* = enum + FlapTransitionOver + FlapTransitionUnder + FlapTransitionSlide + +{.push importc, cdecl.} +# Adw +proc adw_init*() + +# Adw.StyleManager +proc adw_style_manager_get_default*(): StyleManager +proc adw_style_manager_set_color_scheme*(manager: StyleManager, colorScheme: ColorScheme) + +# Adw.Window +proc adw_window_new*(): GtkWidget +proc adw_window_set_content*(window, content: GtkWidget) + +# Adw.WindowTitle +proc adw_window_title_new*(title, subtitle: cstring): GtkWidget +proc adw_window_title_set_title*(widget: GtkWidget, title: cstring) +proc adw_window_title_set_subtitle*(widget: GtkWidget, subtitle: cstring) + +# Adw.Avatar +proc adw_avatar_new*(size: cint, text: cstring, showInitials: cbool): GtkWidget +proc adw_avatar_set_show_initials*(avatar: GtkWidget, value: cbool) +proc adw_avatar_set_size*(avatar: GtkWidget, size: cint) +proc adw_avatar_set_text*(avatar: GtkWidget, text: cstring) +proc adw_avatar_set_icon_name*(avatar: GtkWidget, iconName: cstring) + +# Adw.Clamp +proc adw_clamp_new*(): GtkWidget +proc adw_clamp_set_child*(clamp, child: GtkWidget) +proc adw_clamp_set_maximum_size*(clamp: GtkWidget, size: cint) + +# Adw.PreferencesGroup +proc adw_preferences_group_new*(): GtkWidget +proc adw_preferences_group_add*(group, child: GtkWidget) +proc adw_preferences_group_remove*(group, child: GtkWidget) +proc adw_preferences_group_set_header_suffix*(group, child: GtkWidget) +proc adw_preferences_group_set_description*(group: GtkWidget, descr: cstring) +proc adw_preferences_group_set_title*(group: GtkWidget, title: cstring) + +# Adw.PreferencesRow +proc adw_preferences_row_new*(): GtkWidget +proc adw_preferences_row_set_title*(row: GtkWidget, title: cstring) + +# Adw.ActionRow +proc adw_action_row_new*(): GtkWidget +proc adw_action_row_set_subtitle*(row: GtkWidget, subtitle: cstring) +proc adw_action_row_add_prefix*(row, child: GtkWidget) +proc adw_action_row_add_suffix*(row, child: GtkWidget) +proc adw_action_row_remove*(row, child: GtkWidget) +proc adw_action_row_set_activatable_widget*(row, child: GtkWidget) + +# Adw.ExpanderRow +proc adw_expander_row_new*(): GtkWidget +proc adw_expander_row_set_subtitle*(row: GtkWidget, subtitle: cstring) +proc adw_expander_row_add_action*(row, child: GtkWidget) +proc adw_expander_row_add_prefix*(row, child: GtkWidget) +proc adw_expander_row_add_row*(expanderRow, row: GtkWidget) +proc adw_expander_row_remove*(row, child: GtkWidget) + +# Adw.ComboRow +proc adw_combo_row_new*(): GtkWidget +proc adw_combo_row_set_model*(comboRow: GtkWidget, model: GListModel) +proc adw_combo_row_set_selected*(comboRow: GtkWidget, selected: cuint) +proc adw_combo_row_get_selected*(comboRow: GtkWidget): cuint + +when AdwVersion >= (1, 2): + # Adw.EntryRow + proc adw_entry_row_new*(): GtkWidget + proc adw_entry_row_add_suffix*(row, child: GtkWidget) + proc adw_entry_row_remove*(row, child: GtkWidget) + +# Adw.Flap +proc adw_flap_new*(): GtkWidget +proc adw_flap_set_content*(flap, content: GtkWidget) +proc adw_flap_set_flap*(flap, child: GtkWidget) +proc adw_flap_set_separator*(flap, child: GtkWidget) +proc adw_flap_set_fold_policy*(flap: GtkWidget, foldPolicy: FlapFoldPolicy) +proc adw_flap_set_fold_threshold_policy*(flap: GtkWidget, foldThresholdPolicy: FoldThresholdPolicy) +proc adw_flap_set_transition_type*(flap: GtkWidget, transitionType: FlapTransitionType) +proc adw_flap_set_reveal_flap*(flap: GtkWidget, revealed: cbool) +proc adw_flap_set_modal*(flap: GtkWidget, modal: cbool) +proc adw_flap_set_locked*(flap: GtkWidget, locked: cbool) +proc adw_flap_set_swipe_to_open*(flap: GtkWidget, swipe: cbool) +proc adw_flap_set_swipe_to_close*(flap: GtkWidget, swipe: cbool) +proc adw_flap_get_reveal_flap*(flap: GtkWidget): cbool +proc adw_flap_get_folded*(flap: GtkWidget): cbool + +# Adw.SplitButton +proc adw_split_button_new*(): GtkWidget +proc adw_split_button_set_child*(button, child: GtkWidget) +proc adw_split_button_set_popover*(button, child: GtkWidget) + +# Adw.StatusPage +proc adw_status_page_new*(): GtkWidget +proc adw_status_page_set_child*(self: GtkWidget, child: GtkWidget) +proc adw_status_page_set_description*(self: GtkWidget, description: cstring) +proc adw_status_page_set_icon_name*(self: GtkWidget, icon_name: cstring) +proc adw_status_page_set_paintable*(self: GtkWidget, paintable: GtkWidget) +proc adw_status_page_set_title*(self: GtkWidget, title: cstring) + +when AdwVersion >= (1, 2): + # Adw.AboutWindow + proc adw_about_window_new*(): GtkWidget + proc adw_about_window_set_application_name*(window: GtkWidget, value: cstring) + proc adw_about_window_set_developer_name*(window: GtkWidget, value: cstring) + proc adw_about_window_set_version*(window: GtkWidget, value: cstring) + proc adw_about_window_set_support_url*(window: GtkWidget, value: cstring) + proc adw_about_window_set_issue_url*(window: GtkWidget, value: cstring) + proc adw_about_window_set_website*(window: GtkWidget, value: cstring) + proc adw_about_window_set_copyright*(window: GtkWidget, value: cstring) + proc adw_about_window_set_license*(window: GtkWidget, value: cstring) From b260c079f3831d3c6449813109c3f7fad3886b9f Mon Sep 17 00:00:00 2001 From: Philipp Doerner Date: Sat, 14 Oct 2023 18:46:55 +0100 Subject: [PATCH 3/8] Re export bound enums --- owlkettle/adw.nim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/owlkettle/adw.nim b/owlkettle/adw.nim index e7120b29..3cd444a3 100644 --- a/owlkettle/adw.nim +++ b/owlkettle/adw.nim @@ -27,6 +27,12 @@ when defined(nimPreviewSlimSystem): import widgetdef, widgets, mainloop, widgetutils import ./bindings/[adw, gtk] +export adw.StyleManager +export adw.ColorScheme +export adw.FlapFoldPolicy +export adw.FoldThresholdPolicy +export adw.FlapTransitionType + when defined(owlkettleDocs) and isMainModule: echo "# Libadwaita Widgets\n\n" From 43c3cd20644eefe0967748d82ae5a143ea15742d Mon Sep 17 00:00:00 2001 From: Philipp Doerner Date: Sun, 15 Oct 2023 23:44:45 +0100 Subject: [PATCH 4/8] Provides a scrollbar widget --- examples/widgets/scrollbar.nim | 53 ++++++++++++++++++++++++++++++++++ owlkettle/bindings/gtk.nim | 4 +++ owlkettle/widgets.nim | 11 +++++++ 3 files changed, 68 insertions(+) create mode 100644 examples/widgets/scrollbar.nim diff --git a/examples/widgets/scrollbar.nim b/examples/widgets/scrollbar.nim new file mode 100644 index 00000000..dbece41b --- /dev/null +++ b/examples/widgets/scrollbar.nim @@ -0,0 +1,53 @@ +# MIT License +# +# Copyright (c) 2022 Can Joshua Lehmann +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import std/[sequtils] +import owlkettle, owlkettle/[dataentries, playground, adw] + +viewable App: + items: seq[string] = (1..100).mapIt("Entry " & $it) + orient: Orient = OrientY + sensitive: bool = true + tooltip: string = "" + sizeRequest: tuple[x, y: int] = (-1, -1) + +method view(app: AppState): Widget = + result = gui: + Window(): + title = "Orient Example" + defaultSize = (800, 600) + HeaderBar() {.addTitlebar.}: + insert(app.toAutoFormMenu()) {.addRight.} + + ScrolledWindow: + Box(orient = OrientX): + ListBox(): + for str in app.items: + Label(text = str) + + ScrollBar {.expand: true.}: + orient = app.orient + sensitive = app.sensitive + tooltip = app.tooltip + sizeRequest = app.sizeRequest + +adw.brew(gui(App())) diff --git a/owlkettle/bindings/gtk.nim b/owlkettle/bindings/gtk.nim index af753e33..10c1a830 100644 --- a/owlkettle/bindings/gtk.nim +++ b/owlkettle/bindings/gtk.nim @@ -732,6 +732,10 @@ proc gtk_adjustment_set_step_increment*(adjustment: GtkAdjustment, value: cdoubl proc gtk_adjustment_set_page_increment*(adjustment: GtkAdjustment, value: cdouble) proc gtk_adjustment_set_page_size*(adjustment: GtkAdjustment, value: cdouble) +# Gtk.Scrollbar +proc gtk_scrollbar_new*(orientation: GtkOrientation, adjustment: GtkAdjustment): GtkWidget +proc gtk_scrollbar_set_adjustment*(self: GtkWidget, adjustment: GtkAdjustment) + # Gtk.ScrolledWindow proc gtk_scrolled_window_new*(hAdjustment, vAdjustment: GtkAdjustment): GtkWidget proc gtk_scrolled_window_get_hadjustment*(window: GtkWidget): GtkAdjustment diff --git a/owlkettle/widgets.nim b/owlkettle/widgets.nim index 9cf8b819..bea176b9 100644 --- a/owlkettle/widgets.nim +++ b/owlkettle/widgets.nim @@ -3600,6 +3600,16 @@ renderable Scale of BaseWidget: echo "New value is ", newValue app.value = newValue +renderable Scrollbar of BaseWidget: + orient: Orient = OrientY + + hooks: + beforeBuild: + state.internalWidget = gtk_scrollbar_new(state.orient.toGtk(), nil.GtkAdjustment) + + hooks orient: + property: + gtk_orientable_set_orientation(state.internalWidget, state.orient.toGtk()) # See TODO at comment of PixbufObj regarding why we wrap GtkMediaStream with MediaStreamObj type @@ -4054,3 +4064,4 @@ export ProgressBar export EmojiChooser export CenterBox export ListView +export ScrollBar From 57554dafa25ebe7c1e060660ca84e4f4d416b66c Mon Sep 17 00:00:00 2001 From: Philipp Doerner Date: Sun, 22 Oct 2023 13:34:31 +0100 Subject: [PATCH 5/8] Add wrappings for GtkAdjustment --- owlkettle/bindings/gtk.nim | 10 ++++++++++ owlkettle/widgets.nim | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/owlkettle/bindings/gtk.nim b/owlkettle/bindings/gtk.nim index 10c1a830..f21f9a9e 100644 --- a/owlkettle/bindings/gtk.nim +++ b/owlkettle/bindings/gtk.nim @@ -731,6 +731,16 @@ proc gtk_adjustment_set_upper*(adjustment: GtkAdjustment, value: cdouble) proc gtk_adjustment_set_step_increment*(adjustment: GtkAdjustment, value: cdouble) proc gtk_adjustment_set_page_increment*(adjustment: GtkAdjustment, value: cdouble) proc gtk_adjustment_set_page_size*(adjustment: GtkAdjustment, value: cdouble) +proc gtk_adjustment_clamp_page*(adjustment: GtkAdjustment, lower: cdouble, upper: cdouble) +proc gtk_adjustment_configure*(adjustment: GtkAdjustment, value: cdouble, lower: cdouble, upper: cdouble, step_increment: cdouble, page_increment: cdouble, page_siz: cdouble) +proc gtk_adjustment_get_lower*(adjustment: GtkAdjustment): cdouble +proc gtk_adjustment_get_minimum_increment*(adjustment: GtkAdjustment): cdouble +proc gtk_adjustment_get_page_increment*(adjustment: GtkAdjustment): cdouble +proc gtk_adjustment_get_page_size*(adjustment: GtkAdjustment): cdouble +proc gtk_adjustment_get_step_increment*(adjustment: GtkAdjustment): cdouble +proc gtk_adjustment_get_upper*(adjustment: GtkAdjustment): cdouble +proc gtk_adjustment_get_value*(adjustment: GtkAdjustment): cdouble + # Gtk.Scrollbar proc gtk_scrollbar_new*(orientation: GtkOrientation, adjustment: GtkAdjustment): GtkWidget diff --git a/owlkettle/widgets.nim b/owlkettle/widgets.nim index bea176b9..1e316895 100644 --- a/owlkettle/widgets.nim +++ b/owlkettle/widgets.nim @@ -3600,6 +3600,45 @@ renderable Scale of BaseWidget: echo "New value is ", newValue app.value = newValue +proc newAdjustment*( + value = 0.0, + lower = 0.0, + upper = 0.0, + stepIncrement = 0.0, + pageIncrement = 0.0, + pageSize: float = 0.0 +): GtkAdjustment = + gtk_adjustment_new(value.cdouble, lower.cdouble, upper.cdouble, stepIncrement.cdouble, pageIncrement.cdouble, pageSize.cdouble) + +proc value*(adjustment: GtkAdjustment): float = + gtk_adjustment_get_value(adjustment).float +proc lower*(adjustment: GtkAdjustment): float = + gtk_adjustment_get_lower(adjustment).float +proc upper*(adjustment: GtkAdjustment): float = + gtk_adjustment_get_upper(adjustment).float +proc stepIncrement*(adjustment: GtkAdjustment): float = + gtk_adjustment_get_step_increment(adjustment).float +proc pageIncrement*(adjustment: GtkAdjustment): float = + gtk_adjustment_get_page_increment(adjustment).float +proc pageSize*(adjustment: GtkAdjustment): float = + gtk_adjustment_get_page_size(adjustment).float +proc minimumIncrement*(adjustment: GtkAdjustment): float = + gtk_adjustment_get_minimum_increment(adjustment).float +proc `value=`*(adjustment: GtkAdjustment, newValue: float) = + gtk_adjustment_set_value(adjustment, newValue.cdouble) +proc `lower=`*(adjustment: GtkAdjustment, newValue: float) = + gtk_adjustment_set_lower(adjustment, newValue.cdouble) +proc `upper=`*(adjustment: GtkAdjustment, newValue: float) = + gtk_adjustment_set_upper(adjustment, newValue.cdouble) +proc `stepIncrement=`*(adjustment: GtkAdjustment, newValue: float) = + gtk_adjustment_set_step_increment(adjustment, newValue.cdouble) +proc `pageIncrement=`*(adjustment: GtkAdjustment, newValue: float) = + gtk_adjustment_set_page_increment(adjustment, newValue.cdouble) +proc `pageSize=`*(adjustment: GtkAdjustment, newValue: float) = + gtk_adjustment_set_page_size(adjustment, newValue.cdouble) +proc configure*(adjustment: GtkAdjustment, value: float, lower: float, upper: float, step_increment: float, page_increment: float, page_siz: float) = + gtk_adjustment_configure(adjustment, value.cdouble, lower.cdouble, upper.cdouble, step_increment.cdouble, page_increment.cdouble, page_siz.cdouble) + renderable Scrollbar of BaseWidget: orient: Orient = OrientY From ed6c52daebf5f15825a9b4efe85f036fb0a6e215 Mon Sep 17 00:00:00 2001 From: Philipp Doerner Date: Sun, 22 Oct 2023 13:58:36 +0100 Subject: [PATCH 6/8] Add better docs and improve example --- examples/widgets/scrollbar.nim | 25 +++++++++++++++---------- owlkettle/widgets.nim | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/examples/widgets/scrollbar.nim b/examples/widgets/scrollbar.nim index dbece41b..676ecf35 100644 --- a/examples/widgets/scrollbar.nim +++ b/examples/widgets/scrollbar.nim @@ -29,8 +29,17 @@ viewable App: sensitive: bool = true tooltip: string = "" sizeRequest: tuple[x, y: int] = (-1, -1) + + #Adjustment + value: float = 0.0 + lower: float = 0.0 + upper: float = 0.0 + stepIncrement: float = 0.0 + pageIncrement: float = 0.0 + pageSize: float = 0.0 method view(app: AppState): Widget = + let adj = newAdjustment(app.value, app.lower, app.upper, app.stepIncrement, app.pageIncrement, app.pageSize) result = gui: Window(): title = "Orient Example" @@ -39,15 +48,11 @@ method view(app: AppState): Widget = insert(app.toAutoFormMenu()) {.addRight.} ScrolledWindow: - Box(orient = OrientX): - ListBox(): - for str in app.items: - Label(text = str) - - ScrollBar {.expand: true.}: - orient = app.orient - sensitive = app.sensitive - tooltip = app.tooltip - sizeRequest = app.sizeRequest + ScrollBar: + orient = app.orient + adjustment = adj + sensitive = app.sensitive + tooltip = app.tooltip + sizeRequest = app.sizeRequest adw.brew(gui(App())) diff --git a/owlkettle/widgets.nim b/owlkettle/widgets.nim index 1e316895..6a3a5272 100644 --- a/owlkettle/widgets.nim +++ b/owlkettle/widgets.nim @@ -3638,9 +3638,20 @@ proc `pageSize=`*(adjustment: GtkAdjustment, newValue: float) = gtk_adjustment_set_page_size(adjustment, newValue.cdouble) proc configure*(adjustment: GtkAdjustment, value: float, lower: float, upper: float, step_increment: float, page_increment: float, page_siz: float) = gtk_adjustment_configure(adjustment, value.cdouble, lower.cdouble, upper.cdouble, step_increment.cdouble, page_increment.cdouble, page_siz.cdouble) +proc `==`*(x, y: GtkAdjustment): bool = x.pointer == y.pointer renderable Scrollbar of BaseWidget: + ## A scrollbar widget whose properties can be customized via `GtkAdjustment`. + ## Use `newAdjustment` to create a GtkAdjustment. + ## `GtkAdjustment` has the following properties that can be assigned to: + ## - value: The position of the scrollbar within the upper and lower bounds. If e.g. value/upper = 0.5, the scrollbar will be at 50% height. + ## - lower: The lower bound for the value of the scrollbar. If `value` == `lower`, then the scrollbar is at the top of the page. + ## - upper: The upper bound for the value of the scrollbar. If `value` == `upper`, then the scrollbar is at the bottom of the page. + ## - stepIncrement + ## - pageIncrement + ## - pageSize: The size of the Scrollbar, also relative to the upper and lower bounds. If e.g. `pageSize` / (`upper`- `lower`) = 0.2, then the scrollbar will take up 20% of the total available height. orient: Orient = OrientY + adjustment: GtkAdjustment = nil.GtkAdjustment hooks: beforeBuild: @@ -3650,6 +3661,10 @@ renderable Scrollbar of BaseWidget: property: gtk_orientable_set_orientation(state.internalWidget, state.orient.toGtk()) + hooks adjustment: + property: + gtk_scrollbar_set_adjustment(state.internalWidget, state.adjustment) + # See TODO at comment of PixbufObj regarding why we wrap GtkMediaStream with MediaStreamObj type MediaStreamObj = object From 981820a7c609aa476ac8a233575eb13511f90eec Mon Sep 17 00:00:00 2001 From: Philipp Doerner Date: Sun, 22 Oct 2023 15:32:13 +0100 Subject: [PATCH 7/8] Add first attempt at providing signal listeners to GtkAdjustment --- owlkettle/bindings/gtk.nim | 4 ++++ owlkettle/widgets.nim | 44 +++++++++++++++++++++++++++++++++++++- owlkettle/widgetutils.nim | 15 +++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/owlkettle/bindings/gtk.nim b/owlkettle/bindings/gtk.nim index ca80e734..2a8dd376 100644 --- a/owlkettle/bindings/gtk.nim +++ b/owlkettle/bindings/gtk.nim @@ -777,6 +777,7 @@ proc gtk_adjustment_get_value*(adjustment: GtkAdjustment): cdouble # Gtk.Scrollbar proc gtk_scrollbar_new*(orientation: GtkOrientation, adjustment: GtkAdjustment): GtkWidget proc gtk_scrollbar_set_adjustment*(self: GtkWidget, adjustment: GtkAdjustment) +proc gtk_scrollbar_get_adjustment*(self: GtkWidget): GtkAdjustment # Gtk.ScrolledWindow proc gtk_scrolled_window_new*(hAdjustment, vAdjustment: GtkAdjustment): GtkWidget @@ -1275,6 +1276,9 @@ proc g_signal_connect*(app: GtkListItemFactory, signal: cstring, closure, data: proc g_signal_connect*(app: GtkSelectionModel, signal: cstring, closure, data: pointer): culong = result = g_signal_connect_data(app.pointer, signal, closure, data, nil, G_CONNECT_AFTER) + +proc g_signal_connect*(adj: GtkAdjustment, signal: cstring, closure, data: pointer): culong = + result = g_signal_connect_data(adj.pointer, signal, closure, data, nil, G_CONNECT_AFTER) {.pop.} template withCArgs(argc, argv, body: untyped) = diff --git a/owlkettle/widgets.nim b/owlkettle/widgets.nim index 4c0d391b..f61c1200 100644 --- a/owlkettle/widgets.nim +++ b/owlkettle/widgets.nim @@ -3753,6 +3753,29 @@ proc `pageSize=`*(adjustment: GtkAdjustment, newValue: float) = gtk_adjustment_set_page_size(adjustment, newValue.cdouble) proc configure*(adjustment: GtkAdjustment, value: float, lower: float, upper: float, step_increment: float, page_increment: float, page_siz: float) = gtk_adjustment_configure(adjustment, value.cdouble, lower.cdouble, upper.cdouble, step_increment.cdouble, page_increment.cdouble, page_siz.cdouble) + +# proc `valueChangedHandler=`*(adjustment: GtkAdjustment, handler: proc(newValue: float)) = +# proc valueChangedCallback(adjustment: GtkAdjustment, data: ptr EventObj[proc (newValue: float)]) {.cdecl.} = +# let event = unwrapSharedCell(data) +# event.callback(adjustment.value) + +# let event = EventObj[proc(newValue: float)]() +# let data = allocSharedCell(event) +# data.callback = handler +# data.handler = g_signal_connect(adjustment, "value-changed".cstring, valueChangedCallback, data) + +# proc `adjustmentChangedHandler=`*(adjustment: GtkAdjustment, handler: proc()) = +# proc adjustmentChangedCallback(adjustment: GtkAdjustment, data: ptr EventObj[proc()]) {.cdecl.} = +# let event = unwrapSharedCell(data) +# event.callback() + +# let event = EventObj[proc()]() +# let data = allocSharedCell(event) +# data.callback = handler +# data.handler = g_signal_connect(adjustment, "changed".cstring, adjustmentChangedCallback, data) + +# TODO: Add hooks + proc `==`*(x, y: GtkAdjustment): bool = x.pointer == y.pointer renderable Scrollbar of BaseWidget: @@ -3768,10 +3791,29 @@ renderable Scrollbar of BaseWidget: orient: Orient = OrientY adjustment: GtkAdjustment = nil.GtkAdjustment + proc scrolled(newValue: float) + proc adjustmentChanged() + hooks: beforeBuild: state.internalWidget = gtk_scrollbar_new(state.orient.toGtk(), nil.GtkAdjustment) - + connectEvents: + proc adjustmentCallback(adjustment: GtkAdjustment, data: ptr EventObj[proc()]){.cdecl.} = + echo "CB triggered?" + data[].callback() + data[].redraw() + + proc scrollCallback(adjustment: GtkAdjustment, data: ptr EventObj[proc(newValue: float)]){.cdecl.} = + echo "CB triggered?" + data[].callback(adjustment.value) + data[].redraw() + + state.adjustment.connect(state.scrolled, "changed", adjustmentCallback) + state.adjustment.connect(state.adjustmentChanged, "changed", scrollCallback) + disconnectEvents: + state.adjustment.disconnect(state.scrolled) + state.adjustment.disconnect(state.adjustmentChanged) + hooks orient: property: gtk_orientable_set_orientation(state.internalWidget, state.orient.toGtk()) diff --git a/owlkettle/widgetutils.nim b/owlkettle/widgetutils.nim index 68b2874c..5e4878be 100644 --- a/owlkettle/widgetutils.nim +++ b/owlkettle/widgetutils.nim @@ -56,6 +56,14 @@ proc connect*[T](renderable: Renderable, event.widget = renderable event.handler = g_signal_connect(renderable.internalWidget, name, eventCallback, event[].addr) +proc connect*[T](adjustment: GtkAdjustment, + event: Event[T], + name: cstring, + eventCallback: pointer) = + if not event.isNil: + event.widget = nil + event.handler = g_signal_connect(adjustment, name, eventCallback, event[].addr) + proc disconnect*[T](widget: GtkWidget, event: Event[T]) = if not event.isNil: assert event.handler > 0 @@ -63,6 +71,13 @@ proc disconnect*[T](widget: GtkWidget, event: Event[T]) = event.handler = 0 event.widget = nil +proc disconnect*[T](adj: GtkAdjustment, event: Event[T]) = + if not event.isNil: + assert event.handler > 0 + g_signal_handler_disconnect(pointer(adj), event.handler) + event.handler = 0 + event.widget = nil + proc updateStyle*[State, Widget](state: State, widget: Widget) {.inline.} = mixin classes if widget.hasPrivateStyle: From 9eed698f5fe05a2fd4fade1be360aecb5815d6b7 Mon Sep 17 00:00:00 2001 From: Philipp Doerner Date: Sun, 22 Oct 2023 15:32:18 +0100 Subject: [PATCH 8/8] Improve defaults for example --- examples/widgets/scrollbar.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/widgets/scrollbar.nim b/examples/widgets/scrollbar.nim index 676ecf35..5cd43cc0 100644 --- a/examples/widgets/scrollbar.nim +++ b/examples/widgets/scrollbar.nim @@ -33,7 +33,7 @@ viewable App: #Adjustment value: float = 0.0 lower: float = 0.0 - upper: float = 0.0 + upper: float = 100.0 stepIncrement: float = 0.0 pageIncrement: float = 0.0 pageSize: float = 0.0 @@ -55,4 +55,8 @@ method view(app: AppState): Widget = tooltip = app.tooltip sizeRequest = app.sizeRequest + proc scrolled(newValue: float) = + echo "newValue: ", newValue + + proc adjustmentChanged() = echo "Adjustment changed" adw.brew(gui(App()))