diff --git a/.github/install_deps.bash b/.github/install_deps.bash index 027a5368..41365008 100644 --- a/.github/install_deps.bash +++ b/.github/install_deps.bash @@ -1,4 +1,4 @@ set -x sudo apt-get update -qq -sudo apt-get install -y libgirepository1.0-dev libgirepository-2.0-dev libcairo2-dev gir1.2-gtk-4.0 libffi-dev libglib2.0-dev +sudo apt-get install -y libgirepository1.0-dev libgirepository-2.0-dev libcairo2-dev gir1.2-gtk-4.0 gir1.2-adw-1 libffi-dev libglib2.0-dev sudo apt-get install -y xvfb dbus-x11 at-spi2-core diff --git a/LuaGObject/override/Adw.lua b/LuaGObject/override/Adw.lua index 7d16581c..5f6cea81 100644 --- a/LuaGObject/override/Adw.lua +++ b/LuaGObject/override/Adw.lua @@ -10,15 +10,19 @@ local log = LuaGObject.log.domain "LuaGObject.Adw" -- Constructor container support -- +Adw.Carousel._container_add = Adw.Carousel.append Adw.PreferencesDialog._container_add = Adw.PreferencesDialog._method.add Adw.PreferencesPage._container_add = Adw.PreferencesPage._method.add Adw.PreferencesGroup._container_add = Adw.PreferencesGroup._method.add Adw.ExpanderRow._container_add = Adw.ExpanderRow.add_row -- These classes were introduced in later versions of Adw, and thus may not always be available. +-- Introduced in Adw 1.7 if Adw.WrapBox then Adw.WrapBox._container_add = Adw.WrapBox._method.append end + +--Introduced in Adw 1.8 if Adw.ShortcutsDialog then Adw.ShortcutsDialog._container_add = Adw.ShortcutsDialog.add end @@ -26,6 +30,52 @@ if Adw.ShortcutsSection then Adw.ShortcutsSection._container_add = Adw.ShortcutsSection.add end +-- Adw.TabView overrides -- + +function Adw.TabView:_container_add(child) + if Gtk.Widget:is_type_of(child) then + child = { child } + end + if type(child) ~= "table" then + error("%s: Child must be table or GTK Widget", self._type.name) + end + if #child ~= 1 or not Gtk.Widget:is_type_of(child[1]) then + error("%s: Child must contain only a single GTK Widget.", self._type.name) + end + local page + if child.pinned then + page = self:append_pinned(child[1]) + else + page = self:append(child[1]) + end + if type(child.title) == "string" then + page.title = child.title + end +end + +-- Adw.ViewStack overrides -- + +function Adw.ViewStack:_container_add(child) + if Gtk.Widget:is_type_of(child) then + child = { child } + end + if type(child) ~= "table" then + error("%s: Child must be table or GTK Widget", self._type.name) + end + if #child ~= 1 or not Gtk.Widget:is_type_of(child[1]) then + error("%s: Child table must contain one GTK Widget.", self._type.name) + end + if type(child.icon_name) == "string" and type(child.title) == "string" and type(child.name) == "string" then + self:add_titled_with_icon(child[1], child.name, child.title, child.icon_name) + elseif type(child.title) == "string" and type(child.name) == "string" then + self:add_titled(child[1], child.name, child.title) + elseif type(child.name) == "string" then + self:add_named(child[1], child.name) + else + self:add(child[1]) + end +end + -- Adw.ActionRow overrides -- Adw.ActionRow._attribute = { @@ -34,14 +84,14 @@ Adw.ActionRow._attribute = { } function Adw.ActionRow._attribute.prefixes:get() - error("%s: Cannot read prefixes; attribute is write-only.", self.type.name) + error("%s: Cannot read prefixes; attribute is write-only.", self._type.name) end function Adw.ActionRow._attribute.prefixes:set(value) if Gtk.Widget:is_type_of(value) then value = { value } end if type(value) ~= "table" then - error("%s: Can only write table or Gtk.Widget to add_prefixes.", self.type.name) + error("%s: Can only write table or Gtk.Widget to add_prefixes.", self._type.name) end for _, c in ipairs(value) do self:add_prefix(c) @@ -49,14 +99,14 @@ function Adw.ActionRow._attribute.prefixes:set(value) end function Adw.ActionRow._attribute.suffixes:get() - error("%s: Cannot read suffixes; attribute is write-only.", self.type.name) + error("%s: Cannot read suffixes; attribute is write-only.", self._type.name) end function Adw.ActionRow._attribute.suffixes:set(value) if Gtk.Widget:is_type_of(value) then value = { value } end if type(value) ~= "table" then - error("%s: Can only write table or Widget to add_suffixes.", self.type.name) + error("%s: Can only write table or Widget to add_suffixes.", self._type.name) end for _, v in ipairs(value) do self:add_suffix(v) @@ -71,14 +121,14 @@ Adw.HeaderBar._attribute = { } function Adw.HeaderBar._attribute.end_packs:get() - error("%s: Cannot read end_packs; attribute is write-only.", self.type.name) + error("%s: Cannot read end_packs; attribute is write-only.", self._type.name) end function Adw.HeaderBar._attribute.end_packs:set(value) if Gtk.Widget:is_type_of(value) then value = { value } end if type(value) ~= "table" then - error("%s: Can only write table or Widget to end_packs.", self.type.name) + error("%s: Can only write table or Widget to end_packs.", self._type.name) end for _, v in ipairs(value) do self:pack_end(v) @@ -86,14 +136,14 @@ function Adw.HeaderBar._attribute.end_packs:set(value) end function Adw.HeaderBar._attribute.start_packs:get() - error("%s: Cannot read start_packs; attribute is write-only.", self.type.name) + error("%s: Cannot read start_packs; attribute is write-only.", self._type.name) end function Adw.HeaderBar._attribute.start_packs:set(value) - if Gtk.Widget:is_type_of(widget) then + if Gtk.Widget:is_type_of(value) then value = { value } end if type(value) ~= "table" then - error("%s: Can only write table or Widget to start_packs.", self.type.name) + error("%s: Can only write table or Widget to start_packs.", self._type.name) end for _, v in ipairs(value) do self:pack_start(v) @@ -102,7 +152,7 @@ end -- Adw.ToolbarView overrides -- --- Adw.ToolbarView was introduced in Adw 1.4, and may not be available. +-- Adw.ToolbarView was introduced in Adw 1.4. It should only be overridden if it exists. if Adw.ToolbarView then Adw.ToolbarView._attribute = { bottom_bars = {}, @@ -110,14 +160,14 @@ if Adw.ToolbarView then } function Adw.ToolbarView._attribute.bottom_bars:get() - error("%s: Cannot read bottom_bars; attribute is write-only.", self.type.name) + error("%s: Cannot read bottom_bars; attribute is write-only.", self._type.name) end function Adw.ToolbarView._attribute.bottom_bars:set(value) if Gtk.Widget:is_type_of(value) then value = { value } end if type(value) ~= "table" then - error("%s: Can only write table or Gtk.Widget to add_bottom_bars.", self.type.name) + error("%s: Can only write table or Gtk.Widget to add_bottom_bars.", self._type.name) end for _, v in ipairs(values) do self:add_bottom_bar(v) @@ -125,14 +175,14 @@ if Adw.ToolbarView then end function Adw.ToolbarView._attribute.top_bars:get() - error("%s: Cannot read top_bars; attribute is write-only.", self.type.name) + error("%s: Cannot read top_bars; attribute is write-only.", self._type.name) end function Adw.ToolbarView._attribute.top_bars:set(value) if Gtk.Widget:is_type_of(value) then value = { value } end if type(value) ~= "table" then - error("%s: Can only write table or Gtk.Widget to add_top_bars.", self.type.name) + error("%s: Can only write table or Gtk.Widget to add_top_bars.", self._type.name) end for _, v in ipairs(value) do self:add_top_bar(v) diff --git a/LuaGObject/override/Gtk3.lua b/LuaGObject/override/Gtk3.lua index f1d91936..4215a575 100644 --- a/LuaGObject/override/Gtk3.lua +++ b/LuaGObject/override/Gtk3.lua @@ -19,6 +19,8 @@ local cairo = LuaGObject.cairo local log = LuaGObject.log.domain('LuaGObject.Gtk3') +assert(Gtk.get_major_version() <= 3) + -- Initialize GTK. Gtk.disable_setlocale() if not Gtk.init_check() then diff --git a/LuaGObject/override/Gtk4.lua b/LuaGObject/override/Gtk4.lua index 130042e3..f548393f 100644 --- a/LuaGObject/override/Gtk4.lua +++ b/LuaGObject/override/Gtk4.lua @@ -70,9 +70,49 @@ function Gtk.Widget._attribute.children:get() return setmetatable({ _widget = self }, widget_children_mt) end --- Constructor container support -- +-- Simple container support -- Gtk.Box._container_add = Gtk.Box._method.append Gtk.FlowBox._container_add = Gtk.FlowBox._method.append Gtk.ListBox._container_add = Gtk.ListBox._method.append Gtk.Stack._container_add = Gtk.Stack._method.add_child + +-- Gtk.Grid container support -- + +function Gtk.Grid:_container_add(child) + if type(child) ~= "table" then + error("%s: Cannot add non-table child from constructor.", self._type.name) + end + if type(child.column) ~= "number" or type(child.row) ~= "number" then + error("%s: Child column and/or row are unspecified.", self._type.name) + end + if #child ~= 1 or not Gtk.Widget:is_type_of(child[1]) then + error("%s: Child table must contain only one widget.", self._type.name) + end + local column = child.column + local row = child.row + local width = child.width or 1 + local height = child.height or 1 + self:attach(child[1], column, row, width, height) +end + +-- Gtk.Notebook container support -- + +function Gtk.Notebook:_container_add(child) + if type(child) ~= "table" then + error("%s: Cannot add non-table child from constructor.", self._type.name) + end + if type(child.tab_label) == "string" then + child.tab_label = Gtk.Label { label = child.tab_label } + elseif not Gtk.Widget:is_type_of(child.tab_label) then + error("%s: Child label is not a GTK Widget.", self._type.name) + end + if #child ~= 1 or not Gtk.Widget:is_type_of(child[1]) then + error("%s: Child table must have only one widget.", self._type.name) + end + if Gtk.Widget:is_type_of(child.menu_label) then + self:append_page_menu(child[1], child.tab_label, child.menu_label) + else + self:append_page(child[1], child.tab_label) + end +end diff --git a/Makefile b/Makefile index ec039675..bfb1c038 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ VERSION = 0.10.0 MAKE ?= make -ROCK = LuaGObject-$(VERSION)-1.rockspec +ROCK = luagobject-$(VERSION)-1.rockspec .PHONY : rock all clean install check diff --git a/docs/gtk.md b/docs/gtk.md index f77c7c48..9aee5d46 100644 --- a/docs/gtk.md +++ b/docs/gtk.md @@ -49,6 +49,8 @@ GTK 4 gets rid of the Container class, making it somewhat more complicated to in - `Gtk.FlowBox` - `Gtk.ListBox` - `Gtk.Stack` +- `Gtk.Notebook` +- `Gtk.Grid` An example of instantiating a `Gtk.Box` with its own children: @@ -62,6 +64,48 @@ An example of instantiating a `Gtk.Box` with its own children: This creates a `Gtk.Box` containing four children. Note that `box`'s `orientation` property was set to `Gtk.Orientation.VERTICAL` as well in the same constructor. For specific container widgets, this creates a programmer experience not unlike using `Gtk.Builder` for creating widget hierarchies, but with a syntax ressembling Blueprint. +### `Gtk.Notebook` Children + +To construct an instance of `Gtk.Notebook` with children, special consideration must be undertaken. Adding children to this widget requires a label to be provided. Additionally, an optional menu may be provided as well. To do this, each child in the constructor must be a table with a GTK widget in the first slot, a `.tab_label` item with a child widget, and optionally a `.menu_label` item with a child widget. + + local notebook = Gtk.Notebook { + { + Gtk.TextView(), + tab_label = Gtk.Label { label = "Tab 1" }, + }, + { + Gtk.ScrolledWindow(), + tab_label = Gtk.Label { label = "Tab 2" }, + menu_label = Gtk.MenuButton(), + }, + } + +This example creates a `Gtk.Notebook` containing two tabs, labelled "Tab 1" and "Tab 2". Tab 2 also has a menu widget. + +For convenience, this override also allows a child's `.tab_label` item to be a string. In this case, LuaGObject automatically wraps this into a `Gtk.Label` with the `.label` property set the given value. Thus, the previous example can be rewritten as: + + local notebook = Gtk.Notebook { + { Gtk.TextView(), tab_label = "Tab 1" }, + { + Gtk.ScrolledWindow(), + tab_label = "Tab 2", + menu = Gtk.MenuButton(), + }, + } + +### `Gtk.Grid` Children + +Like with `Gtk.Notebook`, the `Gtk.Grid` class also requires special consideration when adding children in its constructor. Each child added this way must be a table containing a widget in the array part, as well as integer values for `.column` and `.row`. A `.width` and/or `.height` may optionally be specified, and both will default to 1 if not given. + + local grid = Gtk.Grid { + { Gtk.Button.new_with_label "1, 1", column = 1, row = 1 }, + { Gtk.Button.new_with_label "2, 1", column = 2, row = 1 }, + { + Gtk.Button.new_with_label "1–2, 2 (spans two columns)", + column = 1, row = 2, width = 2, + }, + } + ### Advanced Example: Removing Children Many container widgets implement a `:remove()` method which takes a child `Gtk.Widget` as its argument. Without the GTK 4 override, removing a child in this way can be somewhat tricky—one may need to call a function like `:get_child_at_index()`, including adjusting the index to account for 0-indexing. @@ -89,6 +133,7 @@ Certain classes only exist in later versions of libadwaita. These version requir Like `Gtk.Box` and related classes, LuaGObject overrides certain Adw classes to allow specifying children to be added in constructor tables. The following widgets are supported: +- `Adw.Carousel` - `Adw.PreferencesDialog` - Must only contain `Adw.PreferencesPage` children. - `Adw.PreferencesPage` @@ -100,6 +145,8 @@ Like `Gtk.Box` and related classes, LuaGObject overrides certain Adw classes to - Must only contain `Adw.ShortcutsSection` children. - `Adw.ShortcutsSection` (Adw 1.8 and later) - Must only contain `Adw.ShortcutsItem` children. +- `Adw.TabView` +- `Adw.ViewStack` As with GTK 4, these constructors may be nested. Here is a more complex example involving the creation of a preferences page: @@ -119,6 +166,33 @@ As with GTK 4, these constructors may be nested. Here is a more complex example }, } +### `Adw.TabView` Constructor Children + +Like with other container widgets, `Adw.TabView` can accept an arbitrary number of child widgets in the constructor table's array part. Additionally, it can also accept an arbitrary number of tables, each containing one widget in the array part and optionally also a value at `.pinned`, which will add the child widget as a pinned tab, and `.title` which is a string to be used as the tab's title. This example illustrates multiple ways to add children to a Tab View: + + local tabview = Adw.TabView { + Gtk.TextView(), -- Title will be left blank + { Gtk.TextView(), title = "Tab 2" }, + { Gtk.TextView(), title = "Tab 3", pinned = true }, + } + +### `Adw.ViewStack` Constructor Children + +As with `Adw.TabView`, `Adw.ViewStack` children may be included in the constructor table directly, or wrapped in tables with certain additional members. These are `.name`, `.title`, and `.icon_name`. For `.title`, a `.name` must be set or it won't be used. For `.icon_name`, both a `.title` and a `.name` must be set as well. For a complete example of all ways to add children to `Adw.ViewStack` in the constructor: + + local viewstack = Adw.ViewStack { + Gtk.TextView(), + { Gtk.TextView() }, + { Gtk.TextView(), name = "Name" }, + { Gtk.TextView(), name = "Name", title = "Title" }, + { + Gtk.TextView(), + name = "Name", + title = "Title", + icon_name = "icon-name", + }, + } + ## Constructing with Supplemental Widgets Certain Adw classes have multiple different kinds of child widgets, preventing them from being arbitrarily added like with other container-type widgets. Widgets that work this way usually do so to distinguish between children coming before, and after, the main widget body. Each class supporting this pattern does so using class-specific methods. LuaGObject provides pseudo-properties for specific classes allowing these kinds of children to be added at construction time. Each pseudo-property is write-only and takes a value of either a single widget or a table array of widgets. Currently, these classes are: diff --git a/tests/adw.lua b/tests/adw.lua new file mode 100644 index 00000000..2d216550 --- /dev/null +++ b/tests/adw.lua @@ -0,0 +1,107 @@ +-- LuaGObject Test Suite, Adwaita test group. +-- Copyright © 2025 Victoria Lacroix +-- Licensed under the terms of an MIT license. See http://www.opensource.org/licenses/mit-license.php for more information. + +local LuaGObject = require "LuaGObject" + +if LuaGObject.Gtk.get_major_version() ~= 4 then + -- Adwaita 1.0 requires GTK 4. If the GTK isn't version 4, Adwaita cannot be loaded, the test cannot be done, so simply return. + return +end +assert(LuaGObject.Adw.get_major_version() == 1) + +local check, checkv = testsuite.check, testsuite.checkv +local adw = testsuite.group.new "adw" + +function adw.prefsdialog_container() + local Adw, Gtk = LuaGObject.Adw, LuaGObject.Gtk + local label = Gtk.Label() + local dialog = Adw.PreferencesDialog { + title = "Dialog", + Adw.PreferencesPage { + title = "Page", + Adw.PreferencesGroup { + title = "Group", + Adw.ExpanderRow { + title = "ExpanderRow", + label, + } + } + }, + } + -- Checking the children through the children API is inadvisable, because Adw inserts a handful children into the mix to make the widget pretty. + local visible_page = dialog.visible_page + check(Adw.PreferencesPage:is_type_of(visible_page)) + -- Adw.PreferencesPage has :get_group only from v1.8 or later, which is not available in Ubuntu 24.04 LTS. This means, it's difficult to test conclusively in CI whether the container portion of the override works. Instead, it'll be assumed that if children can be added through the container override, it must be working. +end + +function adw.carousel_container() + local Adw, Gtk = LuaGObject.Adw, LuaGObject.Gtk + local label1 = Gtk.Label() + local label2 = Gtk.Label() + local label3 = Gtk.Label() + local carousel = Adw.Carousel { + label1, + label2, + label3, + } + check(carousel:get_nth_page(0) == label1) + check(carousel:get_nth_page(1) == label2) + check(carousel:get_nth_page(2) == label3) + check(not carousel:get_nth_page(3)) +end + +function adw.tabview_container() + local Adw, Gtk = LuaGObject.Adw, LuaGObject.Gtk + local label1 = Gtk.Label() + local label2 = Gtk.Label() + local label3 = Gtk.Label() + local tabview = Adw.TabView { + label1, + label2, + { label3, title = "Tab 3" }, + } + check(tabview:get_nth_page(0).child == label1) + check(tabview:get_nth_page(1).child == label2) + check(tabview:get_nth_page(2).child == label3) + check(not tabview:get_nth_page(3)) +end + +function adw.viewstack_container() + local Adw, Gtk = LuaGObject.Adw, LuaGObject.Gtk + local label1 = Gtk.Label() + local label2 = Gtk.Label() + local label3 = Gtk.Label() + local viewstack = Adw.ViewStack { + { label1, name = "view1" }, + { label2, name = "view2", title = "View 2" }, + { + label3, + name = "view3", + title = "View 3", + icon_name = "emblem-system-symbolic", + }, + } + check(viewstack:get_child_by_name "view1" == label1) + check(viewstack:get_child_by_name "view2" == label2) + check(viewstack:get_child_by_name "view3" == label3) + +end + +function adw.actionrow_childattrs() + local Adw, Gtk = LuaGObject.Adw, LuaGObject.Gtk + local actionrow = Adw.ActionRow { + -- If this doesn't crash, then it's assumed the override is working. + prefixes = Gtk.Button.new_with_label "prefix button", + suffixes = Gtk.Button.new_with_label "suffix button", + } +end + +function adw.headerbar_childattrs() + local Adw, Gtk = LuaGObject.Adw, LuaGObject.Gtk + local headerbar = Adw.HeaderBar { + -- If this doesn't crash, then it's assumed the override is working. + start_packs = Gtk.Button.new_with_label "start button", + end_packs = Gtk.Button.new_with_label "end button", + } +end diff --git a/tests/gobject.lua b/tests/gobject.lua index 1fbea66e..1eda9d9d 100644 --- a/tests/gobject.lua +++ b/tests/gobject.lua @@ -25,18 +25,16 @@ function gobject.env_base() check(next(core.object.env(obj)) == nil) end -if LuaGObject.Gtk.Window._method.add then - function gobject.env_persist() - local Gtk = LuaGObject.Gtk - local window = Gtk.Window() - local label = Gtk.Label() - local env = core.object.env(label) - window:_method_add(label) - label = nil - collectgarbage() - label = window:get_child() - check(env == core.object.env(label)) - end +function gobject.env_persist() + local Gtk = LuaGObject.Gtk + local window = Gtk.Window() + local label = Gtk.Label() + local env = core.object.env(label) + window.child = label + label = nil + collectgarbage() + label = window.child + check(env == core.object.env(label)) end function gobject.object_new() diff --git a/tests/gtk.lua b/tests/gtk.lua index 00e01430..c5b0b00a 100644 --- a/tests/gtk.lua +++ b/tests/gtk.lua @@ -1,407 +1,136 @@ ---[[-------------------------------------------------------------------------- - - LGI testsuite, Gtk overrides test group. - - Copyright (c) 2010, 2011 Pavel Holejsovsky - Licensed under the MIT license: - http://www.opensource.org/licenses/mit-license.php - ---]]-------------------------------------------------------------------------- - -local io = require 'io' -local os = require 'os' -local LuaGObject = require 'LuaGObject' - -local check = testsuite.check -local checkv = testsuite.checkv -local gtk = testsuite.group.new('gtk') - -if LuaGObject.Gtk.get_major_version() ~= 3 then - -- LuaGObject only overrides Gtk3. No tests are needed for other versions. - return -end - -function gtk.widget_style() - local Gtk = LuaGObject.Gtk - local GObject = LuaGObject.GObject - local w = Gtk.ProgressBar() - local v = GObject.Value(GObject.Type.INT) - w:style_get_property('xspacing', v) - checkv(w.style.xspacing, v.value, 'number') - check(not pcall(function() return w.style.nonexistent end)) -end - -function gtk.buildable_id() - local Gtk = LuaGObject.Gtk - local w = Gtk.Label() - checkv(w.id, nil, nil) - w.id = 'label_id' - checkv(w.id, 'label_id', 'string') -end - -function gtk.container_property() - local Gtk = LuaGObject.Gtk - local GObject = LuaGObject.GObject - local c, w, v = Gtk.Grid(), Gtk.Label() - c:add(w) - - c.property[w].left_attach = 1 - v = GObject.Value(GObject.Type.INT) - c:child_get_property(w, 'left-attach', v) - checkv(v.value, 1, 'number') - v.value = 2 - c:child_set_property(w, 'left-attach', v) - checkv(c.property[w].left_attach, 2) - check(not pcall(function() c.property[w].notexistent = 1 end)) -end - -function gtk.container_add_method() - local Gtk = LuaGObject.Gtk - local c, w - c, w = Gtk.Grid(), Gtk.Label() - c:add(w) - check(w.parent == c) - - c, w = Gtk.Grid(), Gtk.Label() - c:add { w, left_attach = 0, width = 2 } - check(w.parent == c) - checkv(c.property[w].left_attach, 0, 'number') - checkv(c.property[w].width, 2, 'number') - - c, w = Gtk.Grid(), Gtk.Label() - c:add(w, { left_attach = 0, width = 2 }) - check(w.parent == c) - checkv(c.property[w].left_attach, 0, 'number') - checkv(c.property[w].width, 2, 'number') -end - -function gtk.container_add_child() - local Gtk = LuaGObject.Gtk - local c, w - c, w = Gtk.Grid(), Gtk.Label() - c.child = w - check(w.parent == c) - - c, w = Gtk.Grid(), Gtk.Label() - c.child = { w, left_attach = 0, width = 2 } - check(w.parent == c) - checkv(c.property[w].left_attach, 0, 'number') - checkv(c.property[w].width, 2, 'number') -end - -function gtk.container_add_ctor() - local Gtk = LuaGObject.Gtk - local l1, l2 = Gtk.Label(), Gtk.Label() - local c = Gtk.Grid { { l1, width = 2 }, { l2, height = 3 } } - check(l1.parent == c) - check(l2.parent == c) - checkv(c.property[l1].width, 2, 'number') - checkv(c.property[l2].height, 3, 'number') -end - -function gtk.container_child_find() - local Gtk = LuaGObject.Gtk - local l1, l2 = Gtk.Label { id = 'id_l1' }, Gtk.Label { id = 'id_l2' } - local c = Gtk.Grid { - { l1, width = 2 }, - Gtk.Grid { id = 'in_g', { l2, height = 3 } } - } - - check(c.child.id_l1 == l1) - check(c.child.id_l2 == l2) - check(c.child.id_l2.parent == c.child.in_g) - check(c.child.notexistent == nil) -end - -local uidef = [[ - - - - - False - - - True - False - - - True - False - - - 0 - 0 - 1 - 1 - - - - - True - False - True - True - label - - - 0 - 1 - 1 - 1 - - - - - True - False - vertical - 2 - - - 0 - 2 - 1 - 1 - - - - - - -]] - -function gtk.builder_add_from_string() - local Gtk = LuaGObject.Gtk - local b = Gtk.Builder() - local res, err = b:add_from_string('syntax error') - check(not res and LuaGObject.GLib.Error:is_type_of(err)) - res, err = b:add_from_string(uidef) - check(res and not err) - check(b:get_object('window1')) -end - -function gtk.builder_add_objects_from_string() - local Gtk = LuaGObject.Gtk - local b = Gtk.Builder() - check(b:add_objects_from_string(uidef, -1, { 'statusbar1', 'label1' })) - check(b:get_object('statusbar1') and b:get_object('label1')) - check(not b:get_object('window1') and not b:get_object('toolbar1')) -end - -function gtk.builder_add_from_file() - local Gtk = LuaGObject.Gtk - local tempname = os.tmpname() - local tempfile = io.open(tempname, 'w+') - tempfile:write(uidef) - tempfile:close() - local b = Gtk.Builder() - local res, err = b:add_from_string('syntax error') - check(not res and LuaGObject.GLib.Error:is_type_of(err)) - res, err = b:add_from_file(tempname) - check(res and not err) - check(b:get_object('window1')) - os.remove(tempname) -end - -function gtk.builder_add_objects_from_file() - local Gtk = LuaGObject.Gtk - local tempname = os.tmpname() - local tempfile = io.open(tempname, 'w+') - tempfile:write(uidef) - tempfile:close() - local b = Gtk.Builder() - check(b:add_objects_from_file(tempname, { 'statusbar1', 'label1' })) - check(b:get_object('statusbar1') and b:get_object('label1')) - check(not b:get_object('window1') and not b:get_object('toolbar1')) - os.remove(tempname) -end - -function gtk.builder_objects() - local Gtk = LuaGObject.Gtk - local builder = Gtk.Builder() - check(builder:add_from_string(uidef)) - check(builder.objects.window1 == builder:get_object('window1')) - check(builder.objects.statusbar1 == builder:get_object('statusbar1')) - check(not builder.objects.notexistent) -end - -function gtk.text_tag_table_ctor() - local Gtk = LuaGObject.Gtk - local t1, t2 = Gtk.TextTag { name = 'tag1' }, Gtk.TextTag { name = 'tag2' } - local t = Gtk.TextTagTable { t1, t2 } - check(t:lookup('tag1') == t1) - check(t:lookup('tag2') == t2) - check(t:lookup('notexist') == nil) -end - -function gtk.text_tag_table_tag() - local Gtk = LuaGObject.Gtk - local t1, t2 = Gtk.TextTag { name = 'tag1' }, Gtk.TextTag { name = 'tag2' } - local t = Gtk.TextTagTable { t1, t2 } - check(t.tag.tag1 == t1) - check(t.tag.tag2 == t2) - check(t.tag.notexist == nil) -end - -function gtk.liststore() - local Gtk = LuaGObject.Gtk - local GObject = LuaGObject.GObject - local cols = { int = 1, string = 2 } - local store = Gtk.ListStore.new { GObject.Type.INT, GObject.Type.STRING } - local first = store:insert(0, { [cols.int] = 42, [cols.string] = 'hello' }) - checkv(store:get_value(first, cols.int - 1).value, 42, 'number') - checkv(store[first][cols.int], 42, 'number') - checkv(store:get_value(first, cols.string - 1).value, 'hello', 'string') - checkv(store[first][cols.string], 'hello', 'string') - store[first] = { [cols.string] = 'changed' } - checkv(store[first][cols.string], 'changed', 'string') - checkv(store[first][cols.int], 42, 'number') - store[first][cols.int] = 16 - checkv(store[first][cols.string], 'changed', 'string') - checkv(store[first][cols.int], 16, 'number') -end - -function gtk.treestore() - local Gtk = LuaGObject.Gtk - local GObject = LuaGObject.GObject - local cols = { int = 1, string = 2 } - local store = Gtk.TreeStore.new { GObject.Type.INT, GObject.Type.STRING } - local first = store:insert( - nil, 0, { [cols.int] = 42, [cols.string] = 'hello' }) - checkv(store:get_value(first, cols.int - 1).value, 42, 'number') - checkv(store[first][cols.int], 42, 'number') - checkv(store:get_value(first, cols.string - 1).value, 'hello', 'string') - checkv(store[first][cols.string], 'hello', 'string') - store[first] = { [cols.string] = 'changed' } - checkv(store[first][cols.string], 'changed', 'string') - checkv(store[first][cols.int], 42, 'number') - store[first][cols.int] = 16 - checkv(store[first][cols.string], 'changed', 'string') - checkv(store[first][cols.int], 16, 'number') -end - -function gtk.treeiter() - local Gtk = LuaGObject.Gtk - local GObject = LuaGObject.GObject - local giter = Gtk.TreeIter() - giter.user_data = giter._native - local Model = GObject.Object:derive('LgiTestModel2', { Gtk.TreeModel }) - function Model:do_get_iter(path) - return giter - end - local model = Model() - local niter = model:get_iter(Gtk.TreePath.new_from_string('0')) - check(giter.user_data == niter.user_data) - check(giter ~= niter) -end - -function gtk.treemodel_pairs() - local Gtk = LuaGObject.Gtk - local GObject = LuaGObject.GObject - local cols = { int = 1, string = 2 } - local store = Gtk.TreeStore.new { GObject.Type.INT, GObject.Type.STRING } - local first = store:append( - nil, { [cols.int] = 42, [cols.string] = 'hello' }) - local sub1 = store:append( - first, { [cols.int] = 100, [cols.string] = 'sub1' }) - local sub2 = store:append( - first, { [cols.int] = 101, [cols.string] = 'sub2' }) - - local count = 0 - for i, item in store:pairs() do - count = count + 1 - check(Gtk.TreeIter:is_type_of(i)) - check(item[cols.string] == 'hello') - end - check(count == 1) - - count = 0 - for i, item in store:pairs(first) do - count = count + 1 - check(Gtk.TreeIter:is_type_of(i)) - if (count == 1) then - check(item[cols.string] == 'sub1') - else - check(item[cols.string] == 'sub2') - end - end - check(count == 2) - - count = 0 - for i, item in store:pairs(sub1) do - count = count + 1 - end - check(count == 0) -end - -function gtk.treeview() - local Gtk = LuaGObject.Gtk - local GObject = LuaGObject.GObject - local cols = { int = 1, string = 2 } - local store = Gtk.TreeStore.new { GObject.Type.INT, GObject.Type.STRING } - local renderer = Gtk.CellRendererText { id = 'renderer' } - local column = Gtk.TreeViewColumn { - id = 'column', - { renderer, { text = cols.int } }, - { Gtk.CellRendererText {}, expand = true, pack = 'end', - function(column, cell, model, iter) - return model[iter][cols.string]:toupper() - end }, - } - - local view = Gtk.TreeView { - id = 'view', - model = store, - column - } - -- Check that column is accessible by its 'id' attribute. - check(view.child.view == view) - check(view.child.column == column) - - -- Check that renderer is accessible by its 'id' attribute. - check(view.child.renderer == renderer) -end - -function gtk.actiongroup_add() - local Gtk = LuaGObject.Gtk - -- Adding normal action and action with an accelerator. - local ag = Gtk.ActionGroup() - local a1, a2 = Gtk.Action { name = 'a1' }, Gtk.Action { name = 'a2' } - ag:add(a1) - check(#ag:list_actions() == 1) - check(ag:get_action('a1') == a1) - ag:add { a2, accelerator = 'A' } - check(#ag:list_actions() == 2) - check(ag:get_action('a2') == a2) - - -- Adding a group of radio actions, this time inside the group ctor. - local chosen - a1 = Gtk.RadioAction { name = 'a1', value = 1 } - a2 = Gtk.RadioAction { name = 'a2', value = 2 } - ag = Gtk.ActionGroup { - { a1, { a2, accelerator = 'a' }, - on_change = function(action) chosen = action end } - } - check(#ag:list_actions() == 2) - check(ag:get_action('a1') == a1) - check(ag:get_action('a2') == a2) - check(chosen == nil) - a1:activate() - check(chosen == a1) - a2:activate() - check(chosen == a2) -end - -function gtk.actiongroup_index() - local Gtk = LuaGObject.Gtk - local a1, a2 = Gtk.Action { name = 'a1' }, Gtk.Action { name = 'a2' } - local ag = Gtk.ActionGroup { a1, a2 } - check(ag.action.a1 == a1) - check(ag.action.a2 == a2) -end - -function gtk.treemodelsort_method() - local Gtk = LuaGObject.Gtk - -- Shouldn't error when making TreePath, only print warning - local noop = Gtk.TreeModelSort().set_sort_func +-- LuaGObject Test Suite, GTK 4 test group. +-- Copyright © 2025 Victoria Lacroix +-- Licensed under the terms of an MIT license. See http://www.opensource.org/licenses/mit-license.php for more information. + +local LuaGObject = require "LuaGObject" + +if LuaGObject.Gtk.get_major_version() ~= 4 then + -- The latest available version of GTK isn't GTK 4, so this test cannot be done. + return +end + +local check, checkv = testsuite.check, testsuite.checkv +local gtk = testsuite.group.new "gtk" + +function gtk.dimensions() + local Gtk = LuaGObject.Gtk + local box = Gtk.Box() + box.width = 600 + box.height = 600 + check(box.width_request == 600) + check(box:get_allocated_width() == box.width) + check(box.height_request == 600) + check(box:get_allocated_height() == box.height) +end + +function gtk.children() + local Gtk = LuaGObject.Gtk + local box = Gtk.Box() + local label1 = Gtk.Label() + local label2 = Gtk.Label() + local label3 = Gtk.Label() + box:append(label1) + box:append(label2) + box:append(label3) + check(box.children[1] == label1) + check(box.children[2] == label2) + check(box.children[3] == label3) +end + +function gtk.box_container() + local Gtk = LuaGObject.Gtk + local label1 = Gtk.Label() + local label2 = Gtk.Label() + local label3 = Gtk.Label() + local box = Gtk.Box { + label1, + label2, + label3, + } + check(box.children[1] == label1) + check(box.children[2] == label2) + check(box.children[3] == label3) +end + +function gtk.flowbox_container() + local Gtk = LuaGObject.Gtk + local label1 = Gtk.Label() + local label2 = Gtk.Label() + local label3 = Gtk.Label() + local flowbox = Gtk.FlowBox { + label1, + label2, + label3, + } + check(flowbox.children[1].child == label1) + check(flowbox.children[2].child == label2) + check(flowbox.children[3].child == label3) +end + +function gtk.listbox_container() + local Gtk = LuaGObject.Gtk + local label1 = Gtk.Label() + local label2 = Gtk.Label() + local label3 = Gtk.Label() + local listbox = Gtk.ListBox { + label1, + label2, + label3, + } + check(listbox.children[1].child == label1) + check(listbox.children[2].child == label2) + check(listbox.children[3].child == label3) +end + +function gtk.stack_container() + local Gtk = LuaGObject.Gtk + local label1 = Gtk.Label() + local label2 = Gtk.Label() + local label3 = Gtk.Label() + local stack = Gtk.Stack { + label1, + label2, + label3, + } + check(stack.children[1] == label1) + check(stack.children[2] == label2) + check(stack.children[3] == label3) +end + +function gtk.grid_container() + local Gtk = LuaGObject.Gtk + local label1 = Gtk.Label() + local label2 = Gtk.Label() + local label3 = Gtk.Label() + local grid = Gtk.Grid { + { label1, column = 1, row = 1 }, + { label2, column = 3, row = 2 }, + { label3, column = 2, row = 3 }, + } + check(grid:get_child_at(1, 1) == label1) + check(grid:get_child_at(3, 2) == label2) + check(grid:get_child_at(2, 3) == label3) + check(not grid:get_child_at(2, 2)) +end + +function gtk.notebook_container() + local Gtk = LuaGObject.Gtk + local label1 = Gtk.Label() + local label2 = Gtk.Label() + local label3 = Gtk.Label() + local notebook = Gtk.Notebook { + { label1, tab_label = "Tab 1" }, + { + label2, + tab_label = Gtk.Label { label = "Tab 2" }, + }, + { + label3, + tab_label = Gtk.Label { label = "Tab 3" }, + menu_label = Gtk.MenuButton(), + }, + } + check(notebook:get_nth_page(0) == label1) + check(notebook:get_nth_page(1) == label2) + check(notebook:get_nth_page(2) == label3) end diff --git a/tests/test.lua b/tests/test.lua index 72b49415..e2fc0c1a 100644 --- a/tests/test.lua +++ b/tests/test.lua @@ -115,6 +115,7 @@ for _, sourcefile in ipairs { 'variant.lua', 'dbus.lua', 'gtk.lua', + 'adw.lua', 'cairo.lua', 'pango.lua', 'gio.lua',