import Testing @_spi(SGTKInternal) import Gtk @_spi(Portico) import Portico @MainActor @Suite(.serialized) struct ModifierTests { // MARK: - Static modifiers @Test func staticPreferredWidth() { guard Gtk.initCheck() else { return } let w = AnyView(Label(str: "x").preferredWidth(42)).makeWidget(MountContext()) #expect(w.getSizeRequest().width == 42) } @Test func staticPreferredHeight() { guard Gtk.initCheck() else { return } let w = AnyView(Label(str: "x").preferredHeight(99)).makeWidget(MountContext()) #expect(w.getSizeRequest().height == 99) } @Test func staticHexpand() { guard Gtk.initCheck() else { return } let w = AnyView(Label(str: "x").hexpand(true)).makeWidget(MountContext()) #expect(w.getHexpand() == true) let w2 = AnyView(Label(str: "x").hexpand(false)).makeWidget(MountContext()) #expect(w2.getHexpand() == false) } @Test func staticVexpand() { guard Gtk.initCheck() else { return } let w = AnyView(Label(str: "x").vexpand(true)).makeWidget(MountContext()) #expect(w.getVexpand() == true) } @Test func staticMargin() { guard Gtk.initCheck() else { return } let w = AnyView(Label(str: "x").margin(7)).makeWidget(MountContext()) #expect(w.getMarginStart() == 7) #expect(w.getMarginEnd() == 7) #expect(w.getMarginTop() == 7) #expect(w.getMarginBottom() == 7) } @Test func staticCssClass() { guard Gtk.initCheck() else { return } let w = AnyView(Label(str: "x").cssClass("myclass")).makeWidget(MountContext()) #expect(w.hasCssClass(cssClass: "myclass")) } // MARK: - Reactive modifiers @Test func reactivePreferredWidth() { guard Gtk.initCheck() else { return } let box = StateBox(40) let context = MountContext() let w = AnyView(Label(str: "x").preferredWidth(Binding(box))).makeWidget(context) defer { box.set(0) } #expect(w.getSizeRequest().width == 40) box.set(60) #expect(w.getSizeRequest().width == 60) } @Test func reactiveCssClass() { guard Gtk.initCheck() else { return } let box = StateBox("a") let context = MountContext() let w = AnyView(Label(str: "x").cssClass(Binding(box))).makeWidget(context) defer { box.set("") } #expect(w.hasCssClass(cssClass: "a")) box.set("b") #expect(!w.hasCssClass(cssClass: "a")) #expect(w.hasCssClass(cssClass: "b")) } @Test func reactivePreferredHeight() { guard Gtk.initCheck() else { return } let box = StateBox(99) let context = MountContext() let w = AnyView(Label(str: "x").preferredHeight(Binding(box))).makeWidget(context) _ = box #expect(w.getSizeRequest().height == 99) box.set(150) #expect(w.getSizeRequest().height == 150) } @Test func reactiveHexpand() { guard Gtk.initCheck() else { return } let box = StateBox(false) let context = MountContext() let w = AnyView(Label(str: "x").hexpand(Binding(box))).makeWidget(context) _ = box #expect(w.getHexpand() == false) box.set(true) #expect(w.getHexpand() == true) } @Test func reactiveVexpand() { guard Gtk.initCheck() else { return } let box = StateBox(false) let context = MountContext() let w = AnyView(Label(str: "x").vexpand(Binding(box))).makeWidget(context) _ = box #expect(w.getVexpand() == false) box.set(true) #expect(w.getVexpand() == true) } @Test func reactiveMargin() { guard Gtk.initCheck() else { return } let box = StateBox(5) let context = MountContext() let w = AnyView(Label(str: "x").margin(Binding(box))).makeWidget(context) _ = box #expect(w.getMarginStart() == 5) #expect(w.getMarginEnd() == 5) #expect(w.getMarginTop() == 5) #expect(w.getMarginBottom() == 5) box.set(12) #expect(w.getMarginStart() == 12) #expect(w.getMarginEnd() == 12) #expect(w.getMarginTop() == 12) #expect(w.getMarginBottom() == 12) } // MARK: - Axis preservation @Test func axisPreservation() { guard Gtk.initCheck() else { return } let w = AnyView( Label(str: "x") .preferredWidth(42) .preferredHeight(99) ).makeWidget(MountContext()) let sr = w.getSizeRequest() #expect(sr.width == 42) #expect(sr.height == 99) } @Test func reactiveWidthPreservesHeight() { guard Gtk.initCheck() else { return } let box = StateBox(50) let context = MountContext() let w = AnyView( Label(str: "x") .preferredWidth(100) .preferredHeight(200) .preferredWidth(Binding(box)) ).makeWidget(context) defer { box.set(0) } // After static: width=100, height=200 // After reactive initial: width=50, height=200 (width overwritten, height preserved) let sr = w.getSizeRequest() #expect(sr.height == 200) box.set(80) let sr2 = w.getSizeRequest() #expect(sr2.width == 80) #expect(sr2.height == 200) } // MARK: - .bind modifier @Test func bindBoolToVisible() { guard Gtk.initCheck() else { return } let box = StateBox(true) let context = MountContext() let w = AnyView(Label(str: "x").bind(Binding(box), to: "visible")) .makeWidget(context) defer { box.set(false) } #expect(w.getVisible() == true) box.set(false) #expect(w.getVisible() == false) } @Test func bindStringToTooltip() { guard Gtk.initCheck() else { return } let box = StateBox("hello") let context = MountContext() let w = AnyView(Label(str: "x").bind(Binding(box), to: "tooltip-text")) .makeWidget(context) defer { box.set("") } #expect(w.getTooltipText() == "hello") box.set("world") #expect(w.getTooltipText() == "world") } @Test func bindIntToWidthRequest() { guard Gtk.initCheck() else { return } let box = StateBox(50) let context = MountContext() let w = AnyView(Label(str: "x").bind(Binding(box), to: "width-request")) .makeWidget(context) _ = box #expect(w.getSizeRequest().width == 50) box.set(120) #expect(w.getSizeRequest().width == 120) } @Test func bindDoubleToOpacity() { guard Gtk.initCheck() else { return } let box = StateBox(1.0) let context = MountContext() let w = AnyView(Label(str: "x").bind(Binding(box), to: "opacity")) .makeWidget(context) _ = box #expect(w.getOpacity() == 1.0) box.set(0.0) #expect(w.getOpacity() == 0.0) } // MARK: - Raw widget drop-in @Test func rawWidgetInVStack() { guard Gtk.initCheck() else { return } let raw = Gtk.Label(str: "raw") let container = AnyView(VStack(spacing: 0) { raw }).makeWidget( MountContext()) // VStack is backed by Gtk.Box — verify the raw label is a child // GTK wrappers create new Swift objects for the same GObject, so // compare the underlying object pointers instead of using ===. var found = false var cur = container.getFirstChild() while let c = cur { if c.pointer == raw.pointer { found = true; break } cur = c.getNextSibling() } #expect(found) } @Test func rawWidgetModifierChain() { guard Gtk.initCheck() else { return } let raw = Gtk.Label(str: "chained") let w = AnyView(raw.preferredWidth(100).hexpand(true)).makeWidget( MountContext()) #expect(w.getSizeRequest().width == 100) #expect(w.getHexpand() == true) } }