portico/Tests/PorticoTests/ModifierTests.swift

249 lines
8 KiB
Swift

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<Int32>(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<Int32>(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<Bool>(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<Bool>(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<Int32>(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<Int32>(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<Bool>(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<Int32>(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<Double>(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)
}
}