diff --git a/README.adoc b/README.adoc index 1bc5453..43d0abd 100644 --- a/README.adoc +++ b/README.adoc @@ -6,7 +6,7 @@ :source-highlighter: pygments :pygments-style: monokai :icons: font -:toc: +:toc: left :GIR: GObject-Introspection :MAC: MacOSX @@ -958,6 +958,137 @@ main() ---- +We can easily modify the above example to get the more modern look with +a HeaderBar and the "Gears" MenuButtons: + +[[gearsmenu.nim]] +[source, nim] +.gearsmenu.nim +---- +# https://developer.gnome.org/glib/stable/glib-GVariant.html +# https://developer.gnome.org/glib/stable/glib-GVariantType.html +# https://wiki.gnome.org/HowDoI/GMenu +# https://wiki.gnome.org/HowDoI/GAction +# https://developer.gnome.org/gnome-devel-demos/stable/menubutton.c.html.en +# nim c gearsmenu.nim +import gintro/[gtk, glib, gobject, gio] +import strformat + +# https://github.com/GNOME/glib/blob/master/gio/tests/gapplication-example-actions.c +proc activateToggleAction(action: SimpleAction; parameter: Variant; app: Application) = + app.hold # hold/release taken over from C example, there may be reasons... + block: + echo fmt"action {action.name} activated" + let state: Variant = action.state + let b = state.getBoolean + action.state = newVariantBoolean(not b) + echo fmt"state change {b} -> {not b}" + app.release + +proc activateStatefulAction(action: SimpleAction; parameter: Variant; app: Application) = + app.hold + block: + echo fmt"action {action.name} activated" + let state: Variant = action.state + var l: uint64 + let oldState = state.getString(l) # yes uint64 parameter is a bit ugly + let newState = parameter.getString(l) + action.state = newVariantString(newState) + echo fmt"state change {oldState} -> {newState}" + app.release + +proc quitProgram(action: SimpleAction; parameter: Variant; app: Application) = + quit(app) + +proc appStartup(app: Application) = + echo "appStartup" + let quit = newSimpleAction("quit") # here we create the actions for whole app + connect(quit, "activate", quitProgram, app) + app.addAction(quit) + +proc appActivate(app: Application) = + echo "appActivate" + let window = newApplicationWindow(app) + # window.title = "GTK3 App with Headerbar and Gears Menu" # unused due to HeaderBar + window.defaultSize = (500, 200) + window.position = WindowPosition.center + + let menu = gio.newMenu() # root of all menus + block: # plain stateless menu + let subMenu = gio.newMenu() + menu.appendSubMenu("Application", submenu) + # let section = gio.newMenu() # no separating section needed here + # submenu.appendSection(nil, section) + # section.append("Quit", "app.quit") + submenu.append("Quit", "app.quit") + + block: #stateful menu with radio items + let subMenu = gio.newMenu() + menu.appendSubMenu("Layout", submenu) + let subMenu2 = gio.newMenu() + submenu.appendSubMenu("justify", submenu2) + let section = gio.newMenu() + submenu2.appendSection(nil, section) + section.append("left", "win.justify::left") + section.append("center", "win.justify::center") + section.append("right", "win.justify::right") + + block: # and finally a toggle menu + let subMenu = gio.newMenu() + menu.appendSubMenu("Spelling", submenu) + let section = gio.newMenu() + submenu.appendSection(nil, section) + section.append("Check", "win.toggleSpellCheck") + + let headerBar = newHeaderBar() + headerBar.setShowCloseButton + headerBar.setTitle("Title") + headerBar.setSubtitle("Subtitle") + window.setTitlebar (headerBar) + + let menubar = newMenuButton() + # menubar.setDirection(ArrowType.none) # show the gears Icon + # let image = newImageFromIconName("open-menu-symbolic", IconSize.menu.ord) + let image = newImageFromIconName("document-save", IconSize.dialog.ord) # dialog is really big! + menubar.setImage(image) # this is only an example for a custom image + # menubar.setIconName("open-menu-symbolic") # only gtk4 + headerBar.packEnd(menubar) + menubar.setMenuModel(menu) + + block: # creat the window related actions + let v = newVariantBoolean(true) + let spellCheck = newSimpleActionStateful("toggleSpellCheck", nil, v) + connect(spellCheck, "activate", activateToggleAction, app) + window.actionMap.addAction(spellCheck) + block: + let v = newVariantString("left") # default value and + let vt = newVariantType("s") # string (value type) + let justifyAction = newSimpleActionStateful("justify", vt, v) + connect(justifyAction, "activate", activateStatefulAction, app) + window.actionMap.addAction(justifyAction) + let button = newButton() + button.label = "Justify Center" + button.setDetailedActionName("win.justify::center") + #button.setActionName("app.quit") # for a stateless action + setAccelsForAction(app, "win.justify::right", "R") + window.add(button) + showAll(window) + +proc main = + let app = newApplication("app.example") + connect(app, "startup", appStartup) + connect(app, "activate", appActivate) + echo fmt"GTK Version {majorVersion()}.{minorVersion()}.{microVersion()}" + let status = run(app) + quit(status) + +main() +---- + +While in the previous example we create only a single menu instance in proc appStartup() +for all of our application windows, here we create a new menu for all of our instances +in proc appActivate(). That seems to work fine, so I assume it is correct. + == GSettings GSettings provides a convenient way to permanently storing configuration data, diff --git a/examples/gtk3/gearsmenu.nim b/examples/gtk3/gearsmenu.nim new file mode 100644 index 0000000..f0c558c --- /dev/null +++ b/examples/gtk3/gearsmenu.nim @@ -0,0 +1,118 @@ +# https://developer.gnome.org/glib/stable/glib-GVariant.html +# https://developer.gnome.org/glib/stable/glib-GVariantType.html +# https://wiki.gnome.org/HowDoI/GMenu +# https://wiki.gnome.org/HowDoI/GAction +# https://developer.gnome.org/gnome-devel-demos/stable/menubutton.c.html.en +# nim c gearsmenu.nim +import gintro/[gtk, glib, gobject, gio] +import strformat + +# https://github.com/GNOME/glib/blob/master/gio/tests/gapplication-example-actions.c +proc activateToggleAction(action: SimpleAction; parameter: Variant; app: Application) = + app.hold # hold/release taken over from C example, there may be reasons... + block: + echo fmt"action {action.name} activated" + let state: Variant = action.state + let b = state.getBoolean + action.state = newVariantBoolean(not b) + echo fmt"state change {b} -> {not b}" + app.release + +proc activateStatefulAction(action: SimpleAction; parameter: Variant; app: Application) = + app.hold + block: + echo fmt"action {action.name} activated" + let state: Variant = action.state + var l: uint64 + let oldState = state.getString(l) # yes uint64 parameter is a bit ugly + let newState = parameter.getString(l) + action.state = newVariantString(newState) + echo fmt"state change {oldState} -> {newState}" + app.release + +proc quitProgram(action: SimpleAction; parameter: Variant; app: Application) = + quit(app) + +proc appStartup(app: Application) = + echo "appStartup" + let quit = newSimpleAction("quit") # here we create the actions for whole app + connect(quit, "activate", quitProgram, app) + app.addAction(quit) + +proc appActivate(app: Application) = + echo "appActivate" + let window = newApplicationWindow(app) + # window.title = "GTK3 App with Headerbar and Gears Menu" # unused due to HeaderBar + window.defaultSize = (500, 200) + window.position = WindowPosition.center + + let menu = gio.newMenu() # root of all menus + block: # plain stateless menu + let subMenu = gio.newMenu() + menu.appendSubMenu("Application", submenu) + # let section = gio.newMenu() # no separating section needed here + # submenu.appendSection(nil, section) + # section.append("Quit", "app.quit") + submenu.append("Quit", "app.quit") + + block: #stateful menu with radio items + let subMenu = gio.newMenu() + menu.appendSubMenu("Layout", submenu) + let subMenu2 = gio.newMenu() + submenu.appendSubMenu("justify", submenu2) + let section = gio.newMenu() + submenu2.appendSection(nil, section) + section.append("left", "win.justify::left") + section.append("center", "win.justify::center") + section.append("right", "win.justify::right") + + block: # and finally a toggle menu + let subMenu = gio.newMenu() + menu.appendSubMenu("Spelling", submenu) + let section = gio.newMenu() + submenu.appendSection(nil, section) + section.append("Check", "win.toggleSpellCheck") + + let headerBar = newHeaderBar() + headerBar.setShowCloseButton + headerBar.setTitle("Title") + headerBar.setSubtitle("Subtitle") + window.setTitlebar (headerBar) + + let menubar = newMenuButton() + # menubar.setDirection(ArrowType.none) # show the gears Icon + # let image = newImageFromIconName("open-menu-symbolic", IconSize.menu.ord) + let image = newImageFromIconName("document-save", IconSize.dialog.ord) # dialog is really big! + menubar.setImage(image) # this is only an example for a custom image + # menubar.setIconName("open-menu-symbolic") # only gtk4 + headerBar.packEnd(menubar) + menubar.setMenuModel(menu) + + block: # creat the window related actions + let v = newVariantBoolean(true) + let spellCheck = newSimpleActionStateful("toggleSpellCheck", nil, v) + connect(spellCheck, "activate", activateToggleAction, app) + window.actionMap.addAction(spellCheck) + block: + let v = newVariantString("left") # default value and + let vt = newVariantType("s") # string (value type) + let justifyAction = newSimpleActionStateful("justify", vt, v) + connect(justifyAction, "activate", activateStatefulAction, app) + window.actionMap.addAction(justifyAction) + let button = newButton() + button.label = "Justify Center" + button.setDetailedActionName("win.justify::center") + #button.setActionName("app.quit") # for a stateless action + setAccelsForAction(app, "win.justify::right", "R") + window.add(button) + showAll(window) + +proc main = + let app = newApplication("app.example") + connect(app, "startup", appStartup) + connect(app, "activate", appActivate) + echo fmt"GTK Version {majorVersion()}.{minorVersion()}.{microVersion()}" + let status = run(app) + quit(status) + +main() diff --git a/examples/gtk3/menubar.nim b/examples/gtk3/menubar.nim index 83a7fc2..4087826 100644 --- a/examples/gtk3/menubar.nim +++ b/examples/gtk3/menubar.nim @@ -7,17 +7,17 @@ import gintro/[gtk, glib, gobject, gio] from strutils import `%`, format # https://github.com/GNOME/glib/blob/master/gio/tests/gapplication-example-actions.c -proc activateToggleAction(action: SimpleAction; parameter: Variant; app: Application) = +proc activateToggleAction(action: SimpleAction; parameter: Variant; app: Application) = app.hold # hold/release taken over from C example, there may be reasons... block: echo format("action $1 activated", action.name) let state: Variant = action.state let b = state.getBoolean - action.state = newVariantBoolean(not b) + action.state = newVariantBoolean(not b) echo format("state change $1 -> $2", b, not b) app.release -proc activateStatefulAction(action: SimpleAction; parameter: Variant; app: Application) = +proc activateStatefulAction(action: SimpleAction; parameter: Variant; app: Application) = app.hold block: echo format("action $1 activated", action.name) @@ -25,7 +25,7 @@ proc activateStatefulAction(action: SimpleAction; parameter: Variant; app: Appli var l: uint64 let oldState = state.getString(l) # yes uint64 parameter is a bit ugly let newState = parameter.getString(l) - action.state = newVariantString(newState) + action.state = newVariantString(newState) echo format("state change $1 -> $2", oldState, newState) app.release @@ -33,6 +33,7 @@ proc quitProgram(action: SimpleAction; parameter: Variant; app: Application) = quit(app) proc appStartup(app: Application) = + echo "appStartup" let quit = newSimpleAction("quit") # here we create the actions for whole app connect(quit, "activate", quitProgram, app) app.addAction(quit) @@ -63,10 +64,11 @@ proc appStartup(app: Application) = let section = gio.newMenu() submenu.appendSection(nil, section) section.append("Check", "win.toggleSpellCheck") - # finally add the menubar + # finally add the menubar setMenuBar(app, menu) proc appActivate(app: Application) = + echo "appActivate" let window = newApplicationWindow(app) window.title = "GTK3 App with Menubar" window.defaultSize = (500, 200) @@ -87,7 +89,7 @@ proc appActivate(app: Application) = #window.add(button) # do not add it here already: (menubar:10010): Gtk-WARNING **: 22:00:33.230: actionhelper: # action win.justify can't be activated due to parameter type mismatch (parameter type s, target type NULL) button.setDetailedActionName("win.justify::center") - #button.setActionName("app.quit") # for a stateless action + #button.setActionName("app.quit") # for a stateless action setAccelsForAction(app, "win.justify::right", "R") window.add(button) showAll(window)