diff --git a/examples/widget-template-manual/box.blp b/examples/widget-template-manual/box.blp
new file mode 100644
index 0000000..25a6e87
--- /dev/null
+++ b/examples/widget-template-manual/box.blp
@@ -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();
+ }
+}
diff --git a/examples/widget-template-manual/box.gresource b/examples/widget-template-manual/box.gresource
new file mode 100644
index 0000000..42fb63b
Binary files /dev/null and b/examples/widget-template-manual/box.gresource differ
diff --git a/examples/widget-template-manual/box.gresource.xml b/examples/widget-template-manual/box.gresource.xml
new file mode 100644
index 0000000..8f92139
--- /dev/null
+++ b/examples/widget-template-manual/box.gresource.xml
@@ -0,0 +1,5 @@
+
+
+ box.ui
+
+
diff --git a/examples/widget-template-manual/box.ui b/examples/widget-template-manual/box.ui
new file mode 100644
index 0000000..df7158a
--- /dev/null
+++ b/examples/widget-template-manual/box.ui
@@ -0,0 +1,19 @@
+
+
+
+
+
+ 3
+ 3
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/widget-template-manual/window.odin b/examples/widget-template-manual/window.odin
new file mode 100644
index 0000000..3c3f891
--- /dev/null
+++ b/examples/widget-template-manual/window.odin
@@ -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)
+}
diff --git a/examples/widget-template-with-helper/box.blp b/examples/widget-template-with-helper/box.blp
new file mode 100644
index 0000000..12087dc
--- /dev/null
+++ b/examples/widget-template-with-helper/box.blp
@@ -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();
+ }
+}
diff --git a/examples/widget-template-with-helper/box.gresource b/examples/widget-template-with-helper/box.gresource
new file mode 100644
index 0000000..0f9ec1a
Binary files /dev/null and b/examples/widget-template-with-helper/box.gresource differ
diff --git a/examples/widget-template-with-helper/box.gresource.xml b/examples/widget-template-with-helper/box.gresource.xml
new file mode 100644
index 0000000..8f92139
--- /dev/null
+++ b/examples/widget-template-with-helper/box.gresource.xml
@@ -0,0 +1,5 @@
+
+
+ box.ui
+
+
diff --git a/examples/widget-template-with-helper/box.ui b/examples/widget-template-with-helper/box.ui
new file mode 100644
index 0000000..a2ae0c5
--- /dev/null
+++ b/examples/widget-template-with-helper/box.ui
@@ -0,0 +1,19 @@
+
+
+
+
+
+ 3
+ 3
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/widget-template-with-helper/window.odin b/examples/widget-template-with-helper/window.odin
new file mode 100644
index 0000000..dc95644
--- /dev/null
+++ b/examples/widget-template-with-helper/window.odin
@@ -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)
+}
diff --git a/glib/glib.odin b/glib/glib.odin
index 9b034e0..97174dc 100644
--- a/glib/glib.odin
+++ b/glib/glib.odin
@@ -365,8 +365,8 @@ _GHook :: struct {
}
PollFD :: _GPollFD
PollFunc :: #type proc "c" (ufds: [^]PollFD, nfsd: uint_, timeout_: int_) -> int_
-SList :: _GSList
-_GSList :: struct {
+// SList :: _GSList
+SList :: struct {
data: pointer,
next: ^SList,
}
@@ -7650,11 +7650,11 @@ when (ODIN_OS == .Linux) && (ODIN_ARCH == .amd64) {
when #config(GLIB_STATIC, false) {
when (ODIN_OS == .Linux) && (ODIN_ARCH == .amd64) {
foreign import glib_runic { "../lib/linux/x86_64/libglib-2.0.a", "../lib/linux/x86_64/libglib-wrapper.a", "system:ffi", "system:pcre2-8" }
-}
+}
} else {
when (ODIN_OS == .Linux) && (ODIN_ARCH == .amd64) {
foreign import glib_runic { "system:glib-2.0", "../lib/linux/x86_64/libglib-wrapper.a" }
-}
+}
}
}
@@ -7664,11 +7664,11 @@ when (ODIN_OS == .Linux) && (ODIN_ARCH == .arm64) {
when #config(GLIB_STATIC, false) {
when (ODIN_OS == .Linux) && (ODIN_ARCH == .arm64) {
foreign import glib_runic { "../lib/linux/aarch64/libglib-2.0.a", "../lib/linux/aarch64/libglib-wrapper.a", "system:ffi", "system:pcre2-8" }
-}
+}
} else {
when (ODIN_OS == .Linux) && (ODIN_ARCH == .arm64) {
foreign import glib_runic { "system:glib-2.0", "../lib/linux/aarch64/libglib-wrapper.a" }
-}
+}
}
}
@@ -7677,7 +7677,6 @@ when (ODIN_OS == .Windows) && (ODIN_ARCH == .amd64) {
when (ODIN_OS == .Windows) && (ODIN_ARCH == .amd64) {
foreign import glib_runic { "../lib/windows/x86_64/glib-2.0.lib", "../lib/windows/x86_64/glib-wrapper.lib" }
-}
-
}
+}
diff --git a/gtk/template_helper.odin b/gtk/template_helper.odin
new file mode 100644
index 0000000..88a4405
--- /dev/null
+++ b/gtk/template_helper.odin
@@ -0,0 +1,339 @@
+package gtk
+
+import "core:strconv"
+import "core:strings"
+import "base:runtime"
+import "core:reflect"
+@require import "core:fmt"
+import gobj "../glib/gobject"
+import "../glib/"
+
+Template_Data :: struct {
+ // URI of the `.ui` file in the gresource.
+ // Must be registered in advance with `gio.resources_register`
+ resource_path: cstring,
+
+ // A slice of template children you'd like registered.
+ children: []Template_Child,
+
+ // The owning type.
+ type: typeid,
+}
+
+Template_Child :: struct {
+ // The id in the `.ui` file.
+ id: cstring,
+
+ // The field name in our strict we want to bind it to.
+ // If empty, it won't be bound.
+ field_name: string,
+}
+
+/*
+This must be called once before you do anything with your custom widget,
+otherwise it is not defined in the gobject typesystem!
+
+Default class and instance init procs don't do anything, so if you need subtype binding or some other setup,
+you need to supply them yourself.
+*/
+register_type :: proc(
+ $instance_type: typeid,
+ $parent_class_type: typeid,
+ parent_g_type: gobj.Type,
+ instance_init_proc: gobj.InstanceInitFunc = instance_init_default,
+ class_init_proc: gobj.ClassInitFunc = class_init_default,
+) -> (
+ g_type: gobj.Type,
+) {
+
+ static_g_type_ptr := custom_type_get_type_ptr(instance_type)
+ static_g_type := static_g_type_ptr^
+ if static_g_type != 0 { return static_g_type }
+
+ info := gobj.TypeInfo{
+ class_size = size_of(Custom_Type_Class),
+ class_init = class_init_proc,
+
+ instance_size = size_of(instance_type),
+ instance_init = instance_init_proc,
+ }
+
+ my_type_name := fmt.caprint(typeid_of(instance_type))
+ defer delete(my_type_name)
+ registered_g_type := gobj.type_register_static(parent_g_type, my_type_name, &info, .NONE)
+
+ static_g_type_ptr^ = registered_g_type
+ g_type = registered_g_type
+ return
+
+ Custom_Type_Class :: struct {
+ parent_class: parent_class_type,
+ }
+}
+
+/*
+This must be called once before you do anything with your custom widget,
+otherwise it is not defined in the gobject typesystem!
+
+Default class and instance init procs don't do anything, so if you need subtype binding or some other setup,
+you need to supply them yourself.
+
+The default instance init proc does not depend on the template data, so it can safely be on the stack.
+
+**Warning**: If your instance init proc depends on the template data, you must not free it while you
+keep making widgets of this type!
+*/
+register_type_with_template :: proc(
+ $instance_type: typeid,
+ $parent_class_type: typeid,
+ parent_g_type: gobj.Type,
+ template_data: ^Template_Data,
+ instance_init_proc: gobj.InstanceInitFunc = instance_init_default_template,
+ class_init_proc: gobj.ClassInitFunc = class_init_default_template,
+) -> (
+ g_type: gobj.Type,
+) {
+ static_g_type_ptr := custom_type_get_type_ptr(instance_type)
+ static_g_type := static_g_type_ptr^
+ if static_g_type != 0 { return static_g_type }
+
+ info := gobj.TypeInfo{
+ class_size = size_of(Custom_Type_Class),
+ class_init = class_init_proc,
+ class_data = template_data,
+
+ instance_size = size_of(instance_type),
+ instance_init = instance_init_proc,
+ }
+
+ my_type_name := fmt.caprint(typeid_of(instance_type))
+ defer delete(my_type_name)
+
+ registered_g_type := gobj.type_register_static(parent_g_type, my_type_name, &info, .NONE)
+
+ static_g_type_ptr^ = registered_g_type
+ g_type = registered_g_type
+ return
+
+ Custom_Type_Class :: struct {
+ parent_class: parent_class_type,
+ }
+}
+
+// Returns the `gobject.Type` of the custom widget.
+//
+// Note: Widget must already be registered in the type system via `register_type` or
+// `register_type_from_template`.
+@(require_results)
+custom_type_get_type :: proc($instance_type: typeid) -> (g_type: gobj.Type) {
+ g_type_ptr := custom_type_get_type_ptr(instance_type)
+ g_type = g_type_ptr^
+ return
+}
+
+// Helper proc to store the internal `gobject.Type` for each custom type. Needs to be set from
+// one of the register procs.
+@(private, require_results)
+custom_type_get_type_ptr :: proc($instance_type: typeid) -> (g_type: ^gobj.Type) {
+ @static static_g_type: gobj.Type
+ g_type = &static_g_type
+ return
+}
+
+@private
+instance_init_default :: proc "c" (instance: ^gobj.TypeInstance, class_data: glib.pointer) {}
+
+@private
+class_init_default :: proc "c" (class: glib.pointer, data: glib.pointer) {}
+
+// Takes care of necessary initialisation, like binding children and setting up the template.
+// You can safely call this at the beginning of your own implementation too.
+instance_init_default_template :: proc "c" (instance: ^gobj.TypeInstance, class_data: glib.pointer) {
+ widget_init_template(cast(^Widget)instance)
+}
+
+// Takes care of necessary initialisation, like binding children and setting up the template.
+// You can safely call this at the beginning of your own implementation too.
+class_init_default_template :: proc "c" (class: glib.pointer, data: glib.pointer) {
+ widget_class := cast(^WidgetClass)class
+ template_data := cast(^Template_Data)data
+
+ widget_class_set_template_from_resource(widget_class, template_data.resource_path)
+
+ // Required by `reflect`.
+ context = runtime.default_context()
+
+ for child in template_data.children {
+ field := reflect.struct_field_by_name(template_data.type, child.field_name)
+
+ widget_class_bind_template_child_full(
+ widget_class,
+ name = child.id,
+ internal_child = false,
+ struct_offset = cast(glib.ssize)field.offset,
+ )
+ }
+
+ object_class := cast(^gobj.ObjectClass)class
+ fields := reflect.struct_fields_zipped(template_data.type)
+ for field in fields {
+ // We need to get the id and the property name, in the format "3,my-property-name"
+ tag, ok := reflect.struct_tag_lookup(field.tag, "gproperty")
+ if ok {
+ sep_index := strings.index_rune(tag, ',')
+ if sep_index == -1 { continue }
+
+ param_spec, spec_ok := create_param_spec(field.type.id, tag[sep_index + 1:])
+ if !spec_ok { continue }
+
+ id, id_ok := strconv.parse_uint(tag[:sep_index])
+ if !id_ok { continue }
+
+ gobj.object_class_install_property(object_class, u32(id), param_spec)
+ }
+ }
+
+ create_param_spec :: proc(type: typeid, tag: string) -> (param_spec: ^gobj.ParamSpec, ok: bool) {
+ ok = true
+ name := fmt.caprint(tag)
+ defer delete(name)
+
+ switch type {
+ case string:
+ param_spec = gobj.param_spec_string(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ default_value = nil,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case cstring:
+ param_spec = gobj.param_spec_string(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ default_value = nil,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case bool:
+ param_spec = gobj.param_spec_boolean(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ default_value = false,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case i8:
+ param_spec = gobj.param_spec_char(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ minimum = min(i8),
+ maximum = max(i8),
+ default_value = 0,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case u8:
+ param_spec = gobj.param_spec_uchar(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ minimum = min(u8),
+ maximum = max(u8),
+ default_value = 0,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case i32:
+ param_spec = gobj.param_spec_int(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ minimum = min(i32),
+ maximum = max(i32),
+ default_value = 0,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case u32:
+ param_spec = gobj.param_spec_uint(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ minimum = min(u32),
+ maximum = max(u32),
+ default_value = 0,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case i64:
+ param_spec = gobj.param_spec_int64(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ minimum = min(i64),
+ maximum = max(i64),
+ default_value = 0,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case int:
+ param_spec = gobj.param_spec_int64(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ minimum = min(i64),
+ maximum = max(i64),
+ default_value = 0,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case u64:
+ param_spec = gobj.param_spec_uint64(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ minimum = min(u64),
+ maximum = max(u64),
+ default_value = 0,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case uint:
+ param_spec = gobj.param_spec_uint64(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ minimum = min(u64),
+ maximum = max(u64),
+ default_value = 0,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case f32:
+ param_spec = gobj.param_spec_float(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ minimum = min(f32),
+ maximum = max(f32),
+ default_value = 0,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case f64:
+ param_spec = gobj.param_spec_double(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ minimum = min(f64),
+ maximum = max(f64),
+ default_value = 0,
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case gobj.Object:
+ param_spec = gobj.param_spec_object(
+ name = name,
+ nick = nil,
+ blurb = nil,
+ object_type = gobj.object_get_type(),
+ flags = gobj.ParamFlags.READWRITE,
+ )
+ case: ok = false
+ }
+
+ return
+ }
+}