-
Notifications
You must be signed in to change notification settings - Fork 1
Added template helpers, examples and fix for compiler error #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
BigBoyBarney
wants to merge
7
commits into
PucklaJ:master
Choose a base branch
from
BigBoyBarney:template-example
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+707
−8
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
9a1ffa4
Added template example and workaround fix for compiler error
b6b3971
Added template helper procs and complete examples
83d4833
Added option to specify the field name to bind template child to, imp…
d6ff685
Removed accidental compiled file that I pushed (oops)
a06c3c4
Added gproperty setup helper and example
8976900
Removed accidental compiled files
7c196e2
Improved examples to have more respectable memory management
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| using Gtk 4.0; | ||
|
|
||
| template $My_Box: Gtk.Box { | ||
| halign: center; | ||
| valign: center; | ||
|
|
||
| Button my-button { | ||
| label: _("Hi! Click me and look at the terminal!"); | ||
| clicked => $my_button_clicked(); | ||
| } | ||
| } |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <gresources> | ||
| <gresource prefix="/example"> | ||
| <file>box.ui</file> | ||
| </gresource> | ||
| </gresources> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <!-- | ||
| DO NOT EDIT! | ||
| This file was @generated by blueprint-compiler. Instead, edit the | ||
| corresponding .blp file and regenerate this file with blueprint-compiler. | ||
| --> | ||
| <interface> | ||
| <requires lib="gtk" version="4.0"/> | ||
| <template class="My_Box" parent="GtkBox"> | ||
| <property name="halign">3</property> | ||
| <property name="valign">3</property> | ||
| <child> | ||
| <object class="GtkButton" id="my-button"> | ||
| <property name="label" translatable="yes">Hi! Click me and look at the terminal!</property> | ||
| <signal name="clicked" handler="my_button_clicked"/> | ||
| </object> | ||
| </child> | ||
| </template> | ||
| </interface> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| package template | ||
|
|
||
| @require import "base:runtime" | ||
| @require import "core:fmt" | ||
| @require import adw "../../adwaita" | ||
| @require import "../../glib" | ||
| @require import gobj "../../glib/gobject" | ||
| @require import "../../glib/gio" | ||
| @require import "../../gtk" | ||
|
|
||
| // Return the GType of our widget, and register it if it isn't already. | ||
| my_box_get_type :: proc "c" () -> (g_type: gobj.Type) { | ||
| @static static_g_type: gobj.Type | ||
|
|
||
| // If the type is already registered, we just return the type id. | ||
| if static_g_type != 0 { return static_g_type } | ||
|
|
||
| info := gobj.TypeInfo{ | ||
| class_size = size_of(My_Box_Class), | ||
| class_init = class_init, | ||
|
|
||
| instance_size = size_of(My_Box), | ||
| instance_init = instance_init, | ||
| } | ||
| // We the type id so it sticks. | ||
| static_g_type = gobj.type_register_static(gtk.box_get_type(), "My_Box", &info, .NONE) | ||
| g_type = static_g_type | ||
| return | ||
|
|
||
| // Constructors | ||
| // These could be defined outside of this proc too, but it's nicer to have them contained here. | ||
|
|
||
| // This handles everything that's related to the overall widget class. | ||
| class_init :: proc "c" (class: glib.pointer, data: glib.pointer) { | ||
| gtk.widget_class_set_template_from_resource(cast(^gtk.WidgetClass)class, "/example/box.ui") | ||
|
|
||
| // We can set the click action here. See the blueprint `box.blp` for more information. | ||
| // Alternatively, we could set it in the `instance_init` proc as well. | ||
| widget_class := cast(^gtk.WidgetClass)class | ||
| offset := cast(glib.ssize)offset_of(My_Box, button) | ||
|
|
||
| gtk.widget_class_bind_template_child_full( | ||
| widget_class, | ||
| name = "my-button", | ||
| internal_child = false, // I have no idea what this does. Both work (shrug). | ||
| struct_offset = offset, // This will automatically bind our button into our My_Box struct. How cool! | ||
| ) | ||
|
|
||
| // We set up our button's clicked action defined in the blueprint. | ||
| // Refer to the `instance_init` proc if you want to set it without blueprint. | ||
| clicked_callback := cast(gobj.Callback)my_button_clicked | ||
| gtk.widget_class_bind_template_callback_full(widget_class, "my_button_clicked", clicked_callback) | ||
|
|
||
| // Rest of your class setup and virtual methods etc. go here as well. | ||
| } | ||
|
|
||
| // This handles the individual instances of the widget. | ||
| instance_init :: proc "c" (instance: ^gobj.TypeInstance, class_data: glib.pointer) { | ||
| gtk.widget_init_template(cast(^gtk.Widget)instance) | ||
|
|
||
| /* | ||
| // We could also set up our child button here instead, if we wanted to. | ||
|
|
||
| button := cast(^gtk.Button)( | ||
| gtk.widget_get_template_child(cast(^gtk.Widget)instance, my_button_get_type(), "my-button") | ||
| ) | ||
| my_box := cast(^My_Box)instance | ||
| my_box.button = button | ||
|
|
||
| // It is, however, important to note that we would need to unset the `my_button_clicked` signal | ||
| // in the blueprint. It's either-or. Both cannot be true at once, GTK will yell at us otherwise. | ||
|
|
||
| clicked_callback := cast(gobj.Callback)my_button_clicked | ||
| gobj.signal_connect(button, "clicked", clicked_callback, nil) | ||
| */ | ||
|
|
||
| // Rest of your instance overrides and setup go here as well. | ||
| } | ||
|
|
||
| // This will get called when we click the button. | ||
| my_button_clicked :: proc "c"(button: ^gtk.Button, data: glib.pointer) { | ||
| parent := gtk.widget_get_parent(cast(^gtk.Widget)button) | ||
| my_box := cast(^My_Box)parent | ||
|
|
||
| my_box.button_clicked += 1 | ||
|
|
||
| context = runtime.default_context() | ||
| fmt.printfln("Button clicked %d times!", my_box.button_clicked) | ||
|
|
||
| // GTK copies our string, so we are responsible for freeing the memory. | ||
| button_clicked_cstring := fmt.caprintf("Clicked %d times!", my_box.button_clicked) | ||
| defer delete(button_clicked_cstring) | ||
|
|
||
| gtk.button_set_label(button, button_clicked_cstring) | ||
| } | ||
| } | ||
|
|
||
| // Needed by GTK internally. | ||
| My_Box_Class :: struct { | ||
| parent_class: gtk.BoxClass, | ||
| } | ||
|
|
||
| // The box instance. | ||
| // Note: `parent` field has to be the first. | ||
| My_Box :: struct { | ||
| parent_instance: gtk.Box, | ||
| button_clicked: int, | ||
| button: ^gtk.Button, // this field is set by us in the `class_init` proc. | ||
| } | ||
|
|
||
| main :: proc() { | ||
| context = glib.create_context() | ||
|
|
||
| // We must initialise GTK, otherwise our parent class, `gtk.Box` is not valid. | ||
| gtk.init() | ||
|
|
||
| // We load the resource file that was compiled by `glib-compile-resources` | ||
| // The `.ui` file was created by blueprint-compiler. | ||
| // use the `#load` directive for release builds, to bundle it into the app. | ||
| resource := gio.resource_load("box.gresource", nil) | ||
| gio.resources_register(resource) | ||
|
|
||
| app := adw.application_new("some.example", .NONE) | ||
| defer gobj.object_unref(app) | ||
|
|
||
| gobj.signal_connect(app, "activate", cast(gobj.Callback)show_window, nil) | ||
| status := gio.application_run(cast(^gio.Application)app, 0, nil) | ||
|
|
||
| if status != 0 { | ||
| fmt.eprintfln("%#v", "oh no!") | ||
| } | ||
| } | ||
|
|
||
| // We set up and show the main window here. This could be split up to multiple procs, obviously. | ||
| show_window :: proc "c" (app: ^adw.Application) { | ||
| // GTK is very cast heavy, unfortunately. | ||
| // I like to prefix generic variables with `_` as a note to myself. | ||
| _window := adw.application_window_new(cast(^gtk.Application)(app)) | ||
| window := cast(^adw.ApplicationWindow)_window | ||
|
|
||
| // We create our custom box here, initialised as per its init function. | ||
| my_box_g_type := my_box_get_type() | ||
| _my_box := gobj.object_new(my_box_g_type, nil) | ||
|
|
||
| adw.application_window_set_content(window, cast(^gtk.Widget)_my_box) | ||
| gtk.window_present(cast(^gtk.Window)_window) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| using Gtk 4.0; | ||
|
|
||
| template $My_Box: Gtk.Box { | ||
| halign: center; | ||
| valign: center; | ||
|
|
||
| Button my-button { | ||
| label: bind template.button-label; | ||
| clicked => $my_button_clicked(); | ||
| } | ||
| } |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <gresources> | ||
| <gresource prefix="/example"> | ||
| <file>box.ui</file> | ||
| </gresource> | ||
| </gresources> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <!-- | ||
| DO NOT EDIT! | ||
| This file was @generated by blueprint-compiler. Instead, edit the | ||
| corresponding .blp file and regenerate this file with blueprint-compiler. | ||
| --> | ||
| <interface> | ||
| <requires lib="gtk" version="4.0"/> | ||
| <template class="My_Box" parent="GtkBox"> | ||
| <property name="halign">3</property> | ||
| <property name="valign">3</property> | ||
| <child> | ||
| <object class="GtkButton" id="my-button"> | ||
| <property name="label" bind-source="My_Box" bind-property="button-label" bind-flags="sync-create"/> | ||
| <signal name="clicked" handler="my_button_clicked"/> | ||
| </object> | ||
| </child> | ||
| </template> | ||
| </interface> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| package template | ||
|
|
||
| @require import "base:runtime" | ||
| @require import "core:fmt" | ||
| @require import adw "../../adwaita" | ||
| @require import "../../glib" | ||
| @require import gobj "../../glib/gobject" | ||
| @require import "../../glib/gio" | ||
| @require import "../../gtk" | ||
|
|
||
| // The box instance. | ||
| // Note: `parent` field has to be the first. | ||
| My_Box :: struct { | ||
| parent_instance: gtk.Box, | ||
| button_clicked: int, | ||
| button: ^gtk.Button, // This field is populated automatically. | ||
| button_label: cstring `gproperty:"1,button-label"`, | ||
| } | ||
|
|
||
| main :: proc() { | ||
| context = glib.create_context() | ||
|
|
||
| // We must initialise GTK, otherwise our parent class, `gtk.Box` is not valid. | ||
| gtk.init() | ||
|
|
||
| // We load the resource file that was compiled by `glib-compile-resources` | ||
| // The `.ui` file was created by blueprint-compiler. | ||
| // use the `#load` directive for release builds, to bundle it into the app. | ||
| resource := gio.resource_load("box.gresource", nil) | ||
| gio.resources_register(resource) | ||
|
|
||
| app := adw.application_new("some.example", .NONE) | ||
| defer gobj.object_unref(app) | ||
|
|
||
| gobj.signal_connect(app, "activate", cast(gobj.Callback)show_window, nil) | ||
| status := gio.application_run(cast(^gio.Application)app, 0, nil) | ||
|
|
||
| if status != 0 { | ||
| fmt.eprintfln("%#v", "oh no!") | ||
| } | ||
| } | ||
|
|
||
| // We set up and show the main window here. This could be split up to multiple procs, obviously. | ||
| show_window :: proc "c" (app: ^adw.Application) { | ||
| // GTK is very cast heavy, unfortunately. | ||
| // I like to prefix generic variables with `_` as a note to myself. | ||
| _window := adw.application_window_new(cast(^gtk.Application)(app)) | ||
| window := cast(^adw.ApplicationWindow)_window | ||
|
|
||
| // Context is needed for reflection used by the setup. | ||
| context = runtime.default_context() | ||
|
|
||
| // If we only care about registering the child, and don't want to bind it to our struct, | ||
| // we can leave `field_name` empty. | ||
| template_children := []gtk.Template_Child{ | ||
| { | ||
| id = "my-button", | ||
| field_name = "button", | ||
| }, | ||
| } | ||
| // See gtk.Template_Data for more information about what this is. | ||
| template_data := gtk.Template_Data{ | ||
| resource_path = "/example/box.ui", | ||
| children = template_children, | ||
| type = My_Box, | ||
| } | ||
| gtk.register_type_with_template( | ||
| My_Box, | ||
| gtk.BoxClass, | ||
| gtk.box_get_type(), | ||
| &template_data, | ||
|
|
||
| // We want to set a `clicked` signal to our button, so we must supply our own init proc. | ||
| class_init_proc = my_box_class_init, | ||
| instance_init_proc = my_box_instance_init, | ||
| ) | ||
|
|
||
| // We create our custom box here, initialised as per its init function. | ||
| my_box_g_type := gtk.custom_type_get_type(My_Box) | ||
| _my_box := gobj.object_new(my_box_g_type, nil) | ||
|
|
||
| adw.application_window_set_content(window, cast(^gtk.Widget)_my_box) | ||
| gtk.window_present(cast(^gtk.Window)_window) | ||
| } | ||
|
|
||
| // Our custom class init proc, since we want to set the `clicked` action on our button. | ||
| my_box_class_init :: proc "c" (class: glib.pointer, data: glib.pointer) { | ||
| // We register the getter and setter procs for our box's custom gproperties. | ||
| object_class := cast(^gobj.ObjectClass)class | ||
| object_class.get_property = my_box_property_get | ||
| object_class.set_property = my_box_property_set | ||
|
|
||
| // We call the default init proc, so that we don't have to do the child binding and template setup ourselves. | ||
| // This takes care of common use cases like registering children and annotated gproperties. | ||
| gtk.class_init_default_template(class, data) | ||
|
|
||
| // We register our proc as a callback to the signal defined in the blueprint. | ||
| // Alternatively, we could register the signal in the `instance_init` proc too. | ||
| widget_class := cast(^gtk.WidgetClass)class | ||
| clicked_callback := cast(gobj.Callback)my_button_clicked | ||
| gtk.widget_class_bind_template_callback_full(widget_class, "my_button_clicked", clicked_callback) | ||
| } | ||
|
|
||
| // This is called by GTK whenever it wants to know the value of any of the registered properties. | ||
| my_box_property_get :: proc "c"(object: ^gobj.Object, property_id: glib.uint_, value: ^gobj.Value, pspec: ^gobj.ParamSpec) { | ||
| my_box := cast(^My_Box)object | ||
| switch property_id { | ||
| case 1: // We set the id of our property to 1. | ||
| gobj.value_set_string(value, my_box.button_label) | ||
| } | ||
| } | ||
|
|
||
| my_box_instance_init :: proc "c" (instance: ^gobj.TypeInstance, class_data: glib.pointer) { | ||
| // We call the default template to do the setup. | ||
| gtk.instance_init_default_template(instance, class_data) | ||
|
|
||
| // We set a default value for our label, since the binding sets none by itself. | ||
| my_box := cast(^My_Box)instance | ||
| gtk.button_set_label(my_box.button, "Click me!") | ||
| } | ||
|
|
||
| // We can leave this empty for now because we don't plan on setting it through GTK. | ||
| my_box_property_set :: proc "c"(object: ^gobj.Object, property_id: glib.uint_, value: ^gobj.Value, pspec: ^gobj.ParamSpec) {} | ||
|
|
||
| // This will get called when we click the button. | ||
| my_button_clicked :: proc "c"(button: ^gtk.Button, data: glib.pointer) { | ||
| parent := gtk.widget_get_parent(cast(^gtk.Widget)button) | ||
| my_box := cast(^My_Box)parent | ||
|
|
||
| my_box.button_clicked += 1 | ||
|
|
||
| context = runtime.default_context() | ||
| fmt.printfln("Button clicked %d times!", my_box.button_clicked) | ||
|
|
||
| button_clicked_cstring := fmt.caprintf("Clicked %d times!", my_box.button_clicked) | ||
|
|
||
| // We set the property of our box, and tell GTK that it changed, which will automatically update everything that depends on it. | ||
| delete(my_box.button_label) | ||
| my_box.button_label = button_clicked_cstring | ||
| gobj.object_notify(cast(^gobj.Object)my_box, "button-label") | ||
|
|
||
| // In this case, we could have set the button label directly too, via: | ||
| // gtk.button_set_label(button, button_clicked_cstring) | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.