@gtkx/react 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -62
- package/dist/components/compound.d.ts +40 -0
- package/dist/components/compound.d.ts.map +1 -0
- package/dist/components/compound.js +46 -0
- package/dist/components/compound.js.map +1 -0
- package/dist/components/list.d.ts +42 -2
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +42 -1
- package/dist/components/list.js.map +1 -1
- package/dist/components/slot-widget.d.ts +15 -0
- package/dist/components/slot-widget.d.ts.map +1 -0
- package/dist/components/slot-widget.js +37 -0
- package/dist/components/slot-widget.js.map +1 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +8 -6
- package/dist/errors.js.map +1 -1
- package/dist/generated/compounds.d.ts +2672 -0
- package/dist/generated/compounds.d.ts.map +1 -0
- package/dist/generated/compounds.js +2624 -0
- package/dist/generated/compounds.js.map +1 -0
- package/dist/generated/internal.d.ts +2 -2
- package/dist/generated/internal.d.ts.map +1 -1
- package/dist/generated/internal.js +2751 -4748
- package/dist/generated/internal.js.map +1 -1
- package/dist/generated/jsx.d.ts +2042 -4592
- package/dist/generated/jsx.d.ts.map +1 -1
- package/dist/generated/jsx.js +919 -3478
- package/dist/generated/jsx.js.map +1 -1
- package/dist/generated/registry.d.ts +1 -0
- package/dist/generated/registry.d.ts.map +1 -1
- package/dist/generated/registry.js +0 -1
- package/dist/generated/registry.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/jsx.d.ts +116 -414
- package/dist/jsx.d.ts.map +1 -1
- package/dist/jsx.js +5 -328
- package/dist/jsx.js.map +1 -1
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js.map +1 -1
- package/dist/nodes/alert-dialog.d.ts +14 -0
- package/dist/nodes/alert-dialog.d.ts.map +1 -0
- package/dist/nodes/alert-dialog.js +41 -0
- package/dist/nodes/alert-dialog.js.map +1 -0
- package/dist/nodes/animation.d.ts +5 -4
- package/dist/nodes/animation.d.ts.map +1 -1
- package/dist/nodes/animation.js +65 -49
- package/dist/nodes/animation.js.map +1 -1
- package/dist/nodes/column-view-column.js +2 -2
- package/dist/nodes/column-view-column.js.map +1 -1
- package/dist/nodes/container-slot.d.ts +3 -1
- package/dist/nodes/container-slot.d.ts.map +1 -1
- package/dist/nodes/container-slot.js +28 -16
- package/dist/nodes/container-slot.js.map +1 -1
- package/dist/nodes/drawing-area.d.ts +3 -1
- package/dist/nodes/drawing-area.d.ts.map +1 -1
- package/dist/nodes/drawing-area.js +20 -22
- package/dist/nodes/drawing-area.js.map +1 -1
- package/dist/nodes/event-controller.d.ts.map +1 -1
- package/dist/nodes/event-controller.js +3 -7
- package/dist/nodes/event-controller.js.map +1 -1
- package/dist/nodes/fixed-child.d.ts +1 -0
- package/dist/nodes/fixed-child.d.ts.map +1 -1
- package/dist/nodes/fixed-child.js +13 -0
- package/dist/nodes/fixed-child.js.map +1 -1
- package/dist/nodes/grid-child.d.ts +1 -0
- package/dist/nodes/grid-child.d.ts.map +1 -1
- package/dist/nodes/grid-child.js +13 -0
- package/dist/nodes/grid-child.js.map +1 -1
- package/dist/nodes/internal/construct.js +2 -2
- package/dist/nodes/internal/construct.js.map +1 -1
- package/dist/nodes/internal/widget.d.ts.map +1 -1
- package/dist/nodes/internal/widget.js +5 -9
- package/dist/nodes/internal/widget.js.map +1 -1
- package/dist/nodes/list-item-node.d.ts +6 -6
- package/dist/nodes/list-item-node.d.ts.map +1 -1
- package/dist/nodes/list-item-node.js +27 -5
- package/dist/nodes/list-item-node.js.map +1 -1
- package/dist/nodes/notebook-page.js +2 -2
- package/dist/nodes/notebook-page.js.map +1 -1
- package/dist/nodes/overlay-child.d.ts +2 -0
- package/dist/nodes/overlay-child.d.ts.map +1 -1
- package/dist/nodes/overlay-child.js +29 -8
- package/dist/nodes/overlay-child.js.map +1 -1
- package/dist/nodes/spin-row.d.ts +14 -0
- package/dist/nodes/spin-row.d.ts.map +1 -0
- package/dist/nodes/spin-row.js +46 -0
- package/dist/nodes/spin-row.js.map +1 -0
- package/dist/nodes/switch-row.d.ts +11 -0
- package/dist/nodes/switch-row.d.ts.map +1 -0
- package/dist/nodes/switch-row.js +15 -0
- package/dist/nodes/switch-row.js.map +1 -0
- package/dist/nodes/text-anchor.d.ts.map +1 -1
- package/dist/nodes/text-anchor.js +10 -0
- package/dist/nodes/text-anchor.js.map +1 -1
- package/dist/nodes/text-tag.d.ts.map +1 -1
- package/dist/nodes/text-tag.js +45 -39
- package/dist/nodes/text-tag.js.map +1 -1
- package/dist/nodes/toggle-group.d.ts +12 -6
- package/dist/nodes/toggle-group.d.ts.map +1 -1
- package/dist/nodes/toggle-group.js +53 -4
- package/dist/nodes/toggle-group.js.map +1 -1
- package/dist/nodes/widget.d.ts.map +1 -1
- package/dist/nodes/widget.js +7 -14
- package/dist/nodes/widget.js.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +7 -5
- package/dist/registry.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/use-property.d.ts +29 -0
- package/dist/use-property.d.ts.map +1 -0
- package/dist/use-property.js +44 -0
- package/dist/use-property.js.map +1 -0
- package/dist/use-setting.d.ts +36 -0
- package/dist/use-setting.d.ts.map +1 -0
- package/dist/use-setting.js +68 -0
- package/dist/use-setting.js.map +1 -0
- package/package.json +3 -3
- package/src/components/compound.tsx +57 -0
- package/src/components/list.tsx +59 -2
- package/src/components/slot-widget.tsx +46 -0
- package/src/errors.ts +8 -7
- package/src/generated/compounds.ts +2741 -0
- package/src/generated/internal.ts +2752 -4754
- package/src/generated/jsx.ts +2495 -5012
- package/src/generated/registry.ts +2 -1
- package/src/index.ts +2 -0
- package/src/jsx.ts +121 -443
- package/src/metadata.ts +1 -1
- package/src/nodes/alert-dialog.ts +55 -0
- package/src/nodes/animation.ts +67 -60
- package/src/nodes/column-view-column.ts +2 -2
- package/src/nodes/container-slot.ts +30 -17
- package/src/nodes/drawing-area.ts +23 -32
- package/src/nodes/event-controller.ts +3 -7
- package/src/nodes/fixed-child.ts +13 -0
- package/src/nodes/grid-child.ts +13 -0
- package/src/nodes/internal/construct.ts +2 -2
- package/src/nodes/internal/widget.ts +6 -12
- package/src/nodes/list-item-node.ts +33 -9
- package/src/nodes/notebook-page.ts +2 -2
- package/src/nodes/overlay-child.ts +30 -9
- package/src/nodes/spin-row.ts +72 -0
- package/src/nodes/switch-row.ts +26 -0
- package/src/nodes/text-anchor.ts +9 -0
- package/src/nodes/text-tag.ts +45 -40
- package/src/nodes/toggle-group.ts +63 -9
- package/src/nodes/widget.ts +6 -13
- package/src/registry.ts +7 -5
- package/src/types.ts +1 -0
- package/src/use-property.ts +58 -0
- package/src/use-setting.ts +96 -0
|
@@ -161,11 +161,11 @@ export class NotebookPageNode extends VirtualNode<NotebookPageProps, WidgetNode<
|
|
|
161
161
|
if (!page) return;
|
|
162
162
|
|
|
163
163
|
if (this.props.tabExpand !== undefined) {
|
|
164
|
-
page.
|
|
164
|
+
page.tabExpand = this.props.tabExpand;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
if (this.props.tabFill !== undefined) {
|
|
168
|
-
page.
|
|
168
|
+
page.tabFill = this.props.tabFill;
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
2
2
|
import type { OverlayChildProps } from "../jsx.js";
|
|
3
3
|
import type { Node } from "../node.js";
|
|
4
|
+
import { isRemovable } from "./internal/predicates.js";
|
|
4
5
|
import { hasChanged } from "./internal/props.js";
|
|
5
6
|
import { VirtualNode } from "./virtual.js";
|
|
6
7
|
import { WidgetNode } from "./widget.js";
|
|
@@ -32,6 +33,7 @@ export class OverlayChildNode extends VirtualNode<OverlayChildProps, WidgetNode<
|
|
|
32
33
|
super.appendChild(child);
|
|
33
34
|
|
|
34
35
|
if (this.parent) {
|
|
36
|
+
this.detachFromGtkParent(child);
|
|
35
37
|
this.attachToParent(this.parent.container, child.container);
|
|
36
38
|
}
|
|
37
39
|
}
|
|
@@ -40,19 +42,12 @@ export class OverlayChildNode extends VirtualNode<OverlayChildProps, WidgetNode<
|
|
|
40
42
|
super.insertBefore(child, before);
|
|
41
43
|
|
|
42
44
|
if (this.parent) {
|
|
43
|
-
this.
|
|
45
|
+
this.reinsertAllChildren();
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
public override removeChild(child: WidgetNode): void {
|
|
48
|
-
|
|
49
|
-
const widget = child.container;
|
|
50
|
-
const currentParent = widget.getParent();
|
|
51
|
-
if (currentParent && currentParent === this.parent.container) {
|
|
52
|
-
this.parent.container.removeOverlay(widget);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
50
|
+
this.detachFromGtkParent(child);
|
|
56
51
|
super.removeChild(child);
|
|
57
52
|
}
|
|
58
53
|
|
|
@@ -98,6 +93,32 @@ export class OverlayChildNode extends VirtualNode<OverlayChildProps, WidgetNode<
|
|
|
98
93
|
}
|
|
99
94
|
}
|
|
100
95
|
|
|
96
|
+
private detachFromGtkParent(child: WidgetNode): void {
|
|
97
|
+
const currentParent = child.container.getParent();
|
|
98
|
+
if (currentParent !== null) {
|
|
99
|
+
if (currentParent instanceof Gtk.Overlay) {
|
|
100
|
+
currentParent.removeOverlay(child.container);
|
|
101
|
+
} else if (isRemovable(currentParent)) {
|
|
102
|
+
currentParent.remove(child.container);
|
|
103
|
+
} else {
|
|
104
|
+
child.container.unparent();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private reinsertAllChildren(): void {
|
|
110
|
+
if (!this.parent) return;
|
|
111
|
+
const parent = this.parent.container;
|
|
112
|
+
|
|
113
|
+
for (const child of this.children) {
|
|
114
|
+
this.detachFromGtkParent(child);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
for (const child of this.children) {
|
|
118
|
+
this.attachToParent(parent, child.container);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
101
122
|
private detachAllChildren(parent: Gtk.Overlay): void {
|
|
102
123
|
for (const child of this.children) {
|
|
103
124
|
const currentParent = child.container.getParent();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type * as Adw from "@gtkx/ffi/adw";
|
|
2
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
3
|
+
import type { AdjustableProps } from "../jsx.js";
|
|
4
|
+
import { filterProps, hasChanged } from "./internal/props.js";
|
|
5
|
+
import { WidgetNode } from "./widget.js";
|
|
6
|
+
|
|
7
|
+
type SpinRowProps = AdjustableProps & {
|
|
8
|
+
onValueChanged?: ((value: number, self: Adw.SpinRow) => void) | null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const OWN_PROPS = ["value", "lower", "upper", "stepIncrement", "pageIncrement", "pageSize", "onValueChanged"] as const;
|
|
12
|
+
|
|
13
|
+
export class SpinRowNode extends WidgetNode<Adw.SpinRow, SpinRowProps> {
|
|
14
|
+
private adjustment: Gtk.Adjustment | null = null;
|
|
15
|
+
|
|
16
|
+
public override commitUpdate(oldProps: SpinRowProps | null, newProps: SpinRowProps): void {
|
|
17
|
+
super.commitUpdate(oldProps ? filterProps(oldProps, OWN_PROPS) : null, filterProps(newProps, OWN_PROPS));
|
|
18
|
+
this.applyOwnProps(oldProps, newProps);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private ensureAdjustment(props: SpinRowProps): Gtk.Adjustment {
|
|
22
|
+
if (!this.adjustment) {
|
|
23
|
+
this.adjustment = new Gtk.Adjustment(
|
|
24
|
+
props.value ?? 0,
|
|
25
|
+
props.lower ?? 0,
|
|
26
|
+
props.upper ?? 100,
|
|
27
|
+
props.stepIncrement ?? 1,
|
|
28
|
+
props.pageIncrement ?? 10,
|
|
29
|
+
props.pageSize ?? 0,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
this.container.setAdjustment(this.adjustment);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return this.adjustment;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private applyOwnProps(oldProps: SpinRowProps | null, newProps: SpinRowProps): void {
|
|
39
|
+
const adjustment = this.ensureAdjustment(newProps);
|
|
40
|
+
|
|
41
|
+
if (hasChanged(oldProps, newProps, "onValueChanged")) {
|
|
42
|
+
const { onValueChanged } = newProps;
|
|
43
|
+
this.signalStore.set(
|
|
44
|
+
this,
|
|
45
|
+
this.container,
|
|
46
|
+
"notify::value",
|
|
47
|
+
onValueChanged ? (self: Adw.SpinRow) => onValueChanged(self.getValue(), self) : undefined,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!oldProps) return;
|
|
52
|
+
|
|
53
|
+
if (hasChanged(oldProps, newProps, "lower")) {
|
|
54
|
+
adjustment.setLower(newProps.lower ?? 0);
|
|
55
|
+
}
|
|
56
|
+
if (hasChanged(oldProps, newProps, "upper")) {
|
|
57
|
+
adjustment.setUpper(newProps.upper ?? 100);
|
|
58
|
+
}
|
|
59
|
+
if (hasChanged(oldProps, newProps, "stepIncrement")) {
|
|
60
|
+
adjustment.setStepIncrement(newProps.stepIncrement ?? 1);
|
|
61
|
+
}
|
|
62
|
+
if (hasChanged(oldProps, newProps, "pageIncrement")) {
|
|
63
|
+
adjustment.setPageIncrement(newProps.pageIncrement ?? 10);
|
|
64
|
+
}
|
|
65
|
+
if (hasChanged(oldProps, newProps, "pageSize")) {
|
|
66
|
+
adjustment.setPageSize(newProps.pageSize ?? 0);
|
|
67
|
+
}
|
|
68
|
+
if (hasChanged(oldProps, newProps, "value") && newProps.value !== undefined) {
|
|
69
|
+
adjustment.setValue(newProps.value);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type * as Adw from "@gtkx/ffi/adw";
|
|
2
|
+
import { hasChanged } from "./internal/props.js";
|
|
3
|
+
import { WidgetNode } from "./widget.js";
|
|
4
|
+
|
|
5
|
+
type SwitchRowProps = {
|
|
6
|
+
onActiveChanged?: ((active: boolean, self: Adw.SwitchRow) => void) | null;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export class SwitchRowNode extends WidgetNode<Adw.SwitchRow, SwitchRowProps> {
|
|
10
|
+
public override commitUpdate(oldProps: SwitchRowProps | null, newProps: SwitchRowProps): void {
|
|
11
|
+
super.commitUpdate(oldProps, newProps);
|
|
12
|
+
this.applyOwnProps(oldProps, newProps);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private applyOwnProps(oldProps: SwitchRowProps | null, newProps: SwitchRowProps): void {
|
|
16
|
+
if (hasChanged(oldProps, newProps, "onActiveChanged")) {
|
|
17
|
+
const { onActiveChanged } = newProps;
|
|
18
|
+
this.signalStore.set(
|
|
19
|
+
this,
|
|
20
|
+
this.container,
|
|
21
|
+
"notify::active",
|
|
22
|
+
onActiveChanged ? (self: Adw.SwitchRow) => onActiveChanged(self.getActive(), self) : undefined,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/nodes/text-anchor.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
2
2
|
import type { TextAnchorProps } from "../jsx.js";
|
|
3
3
|
import type { Node } from "../node.js";
|
|
4
|
+
import { isRemovable } from "./internal/predicates.js";
|
|
4
5
|
import { TEXT_OBJECT_REPLACEMENT, type TextContentParent } from "./text-content.js";
|
|
5
6
|
import { isTextContentParent } from "./text-segment.js";
|
|
6
7
|
import { VirtualNode } from "./virtual.js";
|
|
@@ -66,6 +67,14 @@ export class TextAnchorNode extends VirtualNode<TextAnchorProps, Node & TextCont
|
|
|
66
67
|
super.appendChild(child);
|
|
67
68
|
|
|
68
69
|
if (this.textView && this.anchor && child.container) {
|
|
70
|
+
const currentParent = child.container.getParent();
|
|
71
|
+
if (currentParent !== null) {
|
|
72
|
+
if (isRemovable(currentParent)) {
|
|
73
|
+
currentParent.remove(child.container);
|
|
74
|
+
} else {
|
|
75
|
+
child.container.unparent();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
69
78
|
this.textView.addChildAtAnchor(child.container, this.anchor);
|
|
70
79
|
}
|
|
71
80
|
}
|
package/src/nodes/text-tag.ts
CHANGED
|
@@ -8,45 +8,45 @@ import { TextPaintableNode } from "./text-paintable.js";
|
|
|
8
8
|
import { isTextContentParent, TextSegmentNode } from "./text-segment.js";
|
|
9
9
|
import { VirtualNode } from "./virtual.js";
|
|
10
10
|
|
|
11
|
-
const STYLE_PROPS: Partial<Record<keyof TextTagProps, keyof Gtk.TextTag>> = {
|
|
11
|
+
const STYLE_PROPS: Partial<Record<keyof TextTagProps, keyof Gtk.TextTag | string>> = {
|
|
12
12
|
background: "setBackground",
|
|
13
|
-
backgroundFullHeight: "
|
|
13
|
+
backgroundFullHeight: "backgroundFullHeight",
|
|
14
14
|
foreground: "setForeground",
|
|
15
|
-
family: "
|
|
16
|
-
font: "
|
|
17
|
-
sizePoints: "
|
|
18
|
-
size: "
|
|
19
|
-
scale: "
|
|
20
|
-
weight: "
|
|
21
|
-
style: "
|
|
22
|
-
stretch: "
|
|
23
|
-
variant: "
|
|
24
|
-
strikethrough: "
|
|
25
|
-
underline: "
|
|
26
|
-
overline: "
|
|
27
|
-
rise: "
|
|
28
|
-
letterSpacing: "
|
|
29
|
-
lineHeight: "
|
|
30
|
-
leftMargin: "
|
|
31
|
-
rightMargin: "
|
|
32
|
-
indent: "
|
|
33
|
-
pixelsAboveLines: "
|
|
34
|
-
pixelsBelowLines: "
|
|
35
|
-
pixelsInsideWrap: "
|
|
36
|
-
justification: "
|
|
37
|
-
direction: "
|
|
38
|
-
wrapMode: "
|
|
39
|
-
editable: "
|
|
40
|
-
invisible: "
|
|
41
|
-
allowBreaks: "
|
|
42
|
-
insertHyphens: "
|
|
43
|
-
fallback: "
|
|
44
|
-
accumulativeMargin: "
|
|
15
|
+
family: "family",
|
|
16
|
+
font: "font",
|
|
17
|
+
sizePoints: "sizePoints",
|
|
18
|
+
size: "size",
|
|
19
|
+
scale: "scale",
|
|
20
|
+
weight: "weight",
|
|
21
|
+
style: "style",
|
|
22
|
+
stretch: "stretch",
|
|
23
|
+
variant: "variant",
|
|
24
|
+
strikethrough: "strikethrough",
|
|
25
|
+
underline: "underline",
|
|
26
|
+
overline: "overline",
|
|
27
|
+
rise: "rise",
|
|
28
|
+
letterSpacing: "letterSpacing",
|
|
29
|
+
lineHeight: "lineHeight",
|
|
30
|
+
leftMargin: "leftMargin",
|
|
31
|
+
rightMargin: "rightMargin",
|
|
32
|
+
indent: "indent",
|
|
33
|
+
pixelsAboveLines: "pixelsAboveLines",
|
|
34
|
+
pixelsBelowLines: "pixelsBelowLines",
|
|
35
|
+
pixelsInsideWrap: "pixelsInsideWrap",
|
|
36
|
+
justification: "justification",
|
|
37
|
+
direction: "direction",
|
|
38
|
+
wrapMode: "wrapMode",
|
|
39
|
+
editable: "editable",
|
|
40
|
+
invisible: "invisible",
|
|
41
|
+
allowBreaks: "allowBreaks",
|
|
42
|
+
insertHyphens: "insertHyphens",
|
|
43
|
+
fallback: "fallback",
|
|
44
|
+
accumulativeMargin: "accumulativeMargin",
|
|
45
45
|
paragraphBackground: "setParagraphBackground",
|
|
46
|
-
showSpaces: "
|
|
47
|
-
textTransform: "
|
|
48
|
-
fontFeatures: "
|
|
49
|
-
language: "
|
|
46
|
+
showSpaces: "showSpaces",
|
|
47
|
+
textTransform: "textTransform",
|
|
48
|
+
fontFeatures: "fontFeatures",
|
|
49
|
+
language: "language",
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
type TextTagParent = Node & TextContentParent;
|
|
@@ -225,10 +225,15 @@ export class TextTagNode
|
|
|
225
225
|
for (const prop of Object.keys(STYLE_PROPS) as (keyof TextTagProps)[]) {
|
|
226
226
|
if (hasChanged(oldProps, newProps, prop)) {
|
|
227
227
|
const value = newProps[prop];
|
|
228
|
-
const
|
|
229
|
-
if (value !== undefined &&
|
|
230
|
-
const
|
|
231
|
-
|
|
228
|
+
const target = STYLE_PROPS[prop];
|
|
229
|
+
if (value !== undefined && target) {
|
|
230
|
+
const tag = this.tag as unknown as Record<string, unknown>;
|
|
231
|
+
const member = tag[target];
|
|
232
|
+
if (typeof member === "function") {
|
|
233
|
+
(member as (v: unknown) => void).call(this.tag, value);
|
|
234
|
+
} else {
|
|
235
|
+
tag[target] = value;
|
|
236
|
+
}
|
|
232
237
|
}
|
|
233
238
|
}
|
|
234
239
|
}
|
|
@@ -1,19 +1,41 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type
|
|
1
|
+
import * as Adw from "@gtkx/ffi/adw";
|
|
2
|
+
import type * as Gtk from "@gtkx/ffi/gtk";
|
|
3
|
+
import type { AdwToggleGroupProps, ToggleProps } from "../jsx.js";
|
|
4
|
+
import type { Container, Props } from "../types.js";
|
|
5
|
+
import { createContainerWithProperties } from "./internal/construct.js";
|
|
3
6
|
import { filterProps, hasChanged } from "./internal/props.js";
|
|
4
7
|
import { WidgetNode } from "./widget.js";
|
|
5
8
|
|
|
6
|
-
const
|
|
9
|
+
const DEFERRED_PROPS = ["activeName", "active"] as const;
|
|
10
|
+
const OWN_PROPS = ["onActiveChanged", "toggles", ...DEFERRED_PROPS] as const;
|
|
7
11
|
|
|
8
|
-
type
|
|
12
|
+
type OwnProps = Pick<AdwToggleGroupProps, (typeof OWN_PROPS)[number]>;
|
|
9
13
|
|
|
10
|
-
export class ToggleGroupNode extends WidgetNode<Adw.ToggleGroup,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
export class ToggleGroupNode extends WidgetNode<Adw.ToggleGroup, OwnProps> {
|
|
15
|
+
private managedToggles: Adw.Toggle[] = [];
|
|
16
|
+
|
|
17
|
+
public static override createContainer(props: Props, containerClass: typeof Gtk.Widget): Container | null {
|
|
18
|
+
const { activeName: _, active: __, ...rest } = props;
|
|
19
|
+
return createContainerWithProperties(containerClass, rest);
|
|
14
20
|
}
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
public override commitUpdate(oldProps: OwnProps | null, newProps: OwnProps): void {
|
|
23
|
+
if (hasChanged(oldProps, newProps, "toggles")) {
|
|
24
|
+
this.syncToggles(newProps.toggles ?? []);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
super.commitUpdate(oldProps ? filterProps(oldProps, OWN_PROPS) : null, filterProps(newProps, OWN_PROPS));
|
|
28
|
+
|
|
29
|
+
if (hasChanged(oldProps, newProps, "activeName")) {
|
|
30
|
+
this.container.setActiveName(newProps.activeName ?? null);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (hasChanged(oldProps, newProps, "active")) {
|
|
34
|
+
if (newProps.active != null) {
|
|
35
|
+
this.container.setActive(newProps.active);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
17
39
|
if (hasChanged(oldProps, newProps, "onActiveChanged")) {
|
|
18
40
|
const callback = newProps.onActiveChanged;
|
|
19
41
|
this.signalStore.set(
|
|
@@ -24,4 +46,36 @@ export class ToggleGroupNode extends WidgetNode<Adw.ToggleGroup, ToggleGroupProp
|
|
|
24
46
|
);
|
|
25
47
|
}
|
|
26
48
|
}
|
|
49
|
+
|
|
50
|
+
public override detachDeletedInstance(): void {
|
|
51
|
+
this.clearToggles();
|
|
52
|
+
super.detachDeletedInstance();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private syncToggles(newToggles: ToggleProps[]): void {
|
|
56
|
+
this.clearToggles();
|
|
57
|
+
|
|
58
|
+
for (const toggleProps of newToggles) {
|
|
59
|
+
const toggle = new Adw.Toggle();
|
|
60
|
+
applyToggleProps(toggle, toggleProps);
|
|
61
|
+
this.container.add(toggle);
|
|
62
|
+
this.managedToggles.push(toggle);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private clearToggles(): void {
|
|
67
|
+
for (const toggle of this.managedToggles) {
|
|
68
|
+
this.container.remove(toggle);
|
|
69
|
+
}
|
|
70
|
+
this.managedToggles = [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function applyToggleProps(toggle: Adw.Toggle, props: ToggleProps): void {
|
|
75
|
+
if (props.id != null) toggle.setName(props.id);
|
|
76
|
+
if (props.label != null) toggle.setLabel(props.label);
|
|
77
|
+
if (props.iconName != null) toggle.setIconName(props.iconName);
|
|
78
|
+
if (props.tooltip !== undefined) toggle.setTooltip(props.tooltip);
|
|
79
|
+
if (props.enabled !== undefined) toggle.setEnabled(props.enabled);
|
|
80
|
+
if (props.useUnderline !== undefined) toggle.setUseUnderline(props.useUnderline);
|
|
27
81
|
}
|
package/src/nodes/widget.ts
CHANGED
|
@@ -297,23 +297,16 @@ export class WidgetNode<
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
private setProperty(key: string, value: unknown): void {
|
|
300
|
-
const
|
|
301
|
-
if (!
|
|
300
|
+
const propName = resolvePropMeta(this.container, key);
|
|
301
|
+
if (!propName) return;
|
|
302
302
|
|
|
303
|
-
const
|
|
304
|
-
const setter = this.container[setterName as keyof typeof this.container];
|
|
305
|
-
if (!setter || typeof setter !== "function") return;
|
|
303
|
+
const target = this.container as Record<string, unknown>;
|
|
306
304
|
|
|
307
|
-
if (
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (getter && typeof getter === "function") {
|
|
311
|
-
const currentValue = getter.call(this.container);
|
|
312
|
-
if (currentValue === value) return;
|
|
313
|
-
}
|
|
305
|
+
if (findProperty(this.container, key) instanceof ParamSpecString) {
|
|
306
|
+
if (target[propName] === value) return;
|
|
314
307
|
}
|
|
315
308
|
|
|
316
|
-
|
|
309
|
+
target[propName] = value;
|
|
317
310
|
}
|
|
318
311
|
|
|
319
312
|
private insertBeforeReorderable(container: ReorderableWidget, child: WidgetNode, before: WidgetNode): void {
|
package/src/registry.ts
CHANGED
|
@@ -5,7 +5,7 @@ import * as WebKit from "@gtkx/ffi/webkit";
|
|
|
5
5
|
import type { Node } from "./node.js";
|
|
6
6
|
import { AdjustableNode } from "./nodes/adjustable.js";
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { AlertDialogNode } from "./nodes/alert-dialog.js";
|
|
9
9
|
import { AnimationNode } from "./nodes/animation.js";
|
|
10
10
|
import { ApplicationNode } from "./nodes/application.js";
|
|
11
11
|
import { CalendarNode } from "./nodes/calendar.js";
|
|
@@ -35,14 +35,15 @@ import { SearchBarNode } from "./nodes/search-bar.js";
|
|
|
35
35
|
import { ShortcutNode } from "./nodes/shortcut.js";
|
|
36
36
|
import { SlotNode } from "./nodes/slot.js";
|
|
37
37
|
import { SourceViewNode } from "./nodes/source-view.js";
|
|
38
|
+
import { SpinRowNode } from "./nodes/spin-row.js";
|
|
38
39
|
import { StackNode } from "./nodes/stack.js";
|
|
39
40
|
import { StackPageNode } from "./nodes/stack-page.js";
|
|
41
|
+
import { SwitchRowNode } from "./nodes/switch-row.js";
|
|
40
42
|
import { TextAnchorNode } from "./nodes/text-anchor.js";
|
|
41
43
|
import { TextPaintableNode } from "./nodes/text-paintable.js";
|
|
42
44
|
import { TextSegmentNode } from "./nodes/text-segment.js";
|
|
43
45
|
import { TextTagNode } from "./nodes/text-tag.js";
|
|
44
46
|
import { TextViewNode } from "./nodes/text-view.js";
|
|
45
|
-
import { ToggleNode } from "./nodes/toggle.js";
|
|
46
47
|
import { ToggleGroupNode } from "./nodes/toggle-group.js";
|
|
47
48
|
import { WebViewNode } from "./nodes/web-view.js";
|
|
48
49
|
import { WidgetNode } from "./nodes/widget.js";
|
|
@@ -72,8 +73,7 @@ type NodeRegistryEntry = [RegistryKey, NodeClass];
|
|
|
72
73
|
|
|
73
74
|
export const NODE_REGISTRY: NodeRegistryEntry[] = [
|
|
74
75
|
["ContainerSlot", ContainerSlotNode],
|
|
75
|
-
["
|
|
76
|
-
["Animation", AnimationNode],
|
|
76
|
+
[["AdwTimedAnimation", "AdwSpringAnimation"], AnimationNode],
|
|
77
77
|
["ColumnViewColumn", ColumnViewColumnNode],
|
|
78
78
|
["FixedChild", FixedChildNode],
|
|
79
79
|
["GridChild", GridChildNode],
|
|
@@ -89,14 +89,16 @@ export const NODE_REGISTRY: NodeRegistryEntry[] = [
|
|
|
89
89
|
["TextPaintable", TextPaintableNode],
|
|
90
90
|
["TextSegment", TextSegmentNode],
|
|
91
91
|
["TextTag", TextTagNode],
|
|
92
|
-
["Toggle", ToggleNode],
|
|
93
92
|
[Gtk.Application, ApplicationNode],
|
|
94
93
|
[Gtk.EventController, EventControllerNode],
|
|
95
94
|
[GtkSource.View, SourceViewNode],
|
|
96
95
|
[Gtk.TextView, TextViewNode],
|
|
97
96
|
[WebKit.WebView, WebViewNode],
|
|
97
|
+
[Adw.AlertDialog, AlertDialogNode],
|
|
98
98
|
[Adw.Dialog, DialogNode],
|
|
99
99
|
[Gtk.Window, WindowNode],
|
|
100
|
+
[Adw.SpinRow, SpinRowNode],
|
|
101
|
+
[Adw.SwitchRow, SwitchRowNode],
|
|
100
102
|
[Gtk.Scale, ScaleNode],
|
|
101
103
|
[Gtk.LevelBar, LevelBarNode],
|
|
102
104
|
[Gtk.ScrolledWindow, ScrolledWindowNode],
|
package/src/types.ts
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as GObject from "@gtkx/ffi/gobject";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
type ReadableKey<T> = {
|
|
5
|
+
[K in keyof T]: K extends string ? (T[K] extends (...args: unknown[]) => unknown ? never : K) : never;
|
|
6
|
+
}[keyof T];
|
|
7
|
+
|
|
8
|
+
const toKebabCase = (str: string): string =>
|
|
9
|
+
str.replace(/[A-Z]/g, (c, i: number) => (i === 0 ? c.toLowerCase() : `-${c.toLowerCase()}`));
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Subscribes to a GObject property and returns its current value as React state.
|
|
13
|
+
*
|
|
14
|
+
* Connects to the `notify::property-name` signal on `obj` and re-renders
|
|
15
|
+
* whenever the property changes. The initial value is read synchronously
|
|
16
|
+
* at mount time. Disconnects automatically on unmount or when inputs change.
|
|
17
|
+
*
|
|
18
|
+
* When `obj` is `null` or `undefined`, the hook is inactive and returns
|
|
19
|
+
* `undefined`. This allows safe usage with nullable objects without
|
|
20
|
+
* violating React's rules of hooks.
|
|
21
|
+
*
|
|
22
|
+
* @param obj - The GObject instance to observe, or null/undefined to disable
|
|
23
|
+
* @param propertyName - The property name matching an ES6 accessor on the object
|
|
24
|
+
* @returns The current property value, or undefined when obj is null/undefined
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* const app = useApplication();
|
|
29
|
+
* const activeWindow = useProperty(app, "activeWindow");
|
|
30
|
+
* const title = useProperty(activeWindow, "title");
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function useProperty<T extends GObject.Object, K extends ReadableKey<T>>(
|
|
34
|
+
obj: T | null | undefined,
|
|
35
|
+
propertyName: K,
|
|
36
|
+
): T[K] | undefined {
|
|
37
|
+
const [value, setValue] = useState<T[K] | undefined>(() => (obj ? (obj[propertyName] as T[K]) : undefined));
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!obj) {
|
|
41
|
+
setValue(undefined);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setValue(obj[propertyName] as T[K]);
|
|
46
|
+
|
|
47
|
+
const signal = `notify::${toKebabCase(propertyName as string)}`;
|
|
48
|
+
const handlerId = obj.connect(signal, () => {
|
|
49
|
+
setValue(obj[propertyName] as T[K]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
GObject.signalHandlerDisconnect(obj, handlerId);
|
|
54
|
+
};
|
|
55
|
+
}, [obj, propertyName]);
|
|
56
|
+
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as Gio from "@gtkx/ffi/gio";
|
|
2
|
+
import * as GObject from "@gtkx/ffi/gobject";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
+
|
|
5
|
+
interface SettingTypeMap {
|
|
6
|
+
boolean: boolean;
|
|
7
|
+
int: number;
|
|
8
|
+
double: number;
|
|
9
|
+
string: string;
|
|
10
|
+
strv: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type SettingType = keyof SettingTypeMap;
|
|
14
|
+
|
|
15
|
+
const GETTERS: Record<SettingType, string> = {
|
|
16
|
+
boolean: "getBoolean",
|
|
17
|
+
int: "getInt",
|
|
18
|
+
double: "getDouble",
|
|
19
|
+
string: "getString",
|
|
20
|
+
strv: "getStrv",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const SETTERS: Record<SettingType, string> = {
|
|
24
|
+
boolean: "setBoolean",
|
|
25
|
+
int: "setInt",
|
|
26
|
+
double: "setDouble",
|
|
27
|
+
string: "setString",
|
|
28
|
+
strv: "setStrv",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type SettingAccessor = (key: string, value?: unknown) => unknown;
|
|
32
|
+
|
|
33
|
+
function readSetting(settings: Gio.Settings, key: string, type: SettingType): unknown {
|
|
34
|
+
const getter = (settings as unknown as Record<string, SettingAccessor>)[GETTERS[type]] as SettingAccessor;
|
|
35
|
+
return getter.call(settings, key);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function writeSetting(settings: Gio.Settings, key: string, type: SettingType, value: unknown): void {
|
|
39
|
+
const setter = (settings as unknown as Record<string, SettingAccessor>)[SETTERS[type]] as SettingAccessor;
|
|
40
|
+
setter.call(settings, key, value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Subscribes to a GSettings key and returns its current value alongside a
|
|
45
|
+
* setter, similar to `useState`.
|
|
46
|
+
*
|
|
47
|
+
* Creates a `Gio.Settings` instance for the given schema (stable across
|
|
48
|
+
* re-renders), connects to `changed::key`, and re-renders whenever the
|
|
49
|
+
* setting changes. The initial value is read synchronously at mount time.
|
|
50
|
+
* Calling the returned setter writes the new value to GSettings, which in
|
|
51
|
+
* turn triggers a re-render through the `changed` signal.
|
|
52
|
+
*
|
|
53
|
+
* @param schemaId - The GSettings schema ID (e.g. `"org.gnome.desktop.interface"`)
|
|
54
|
+
* @param key - The settings key in kebab-case (e.g. `"color-scheme"`)
|
|
55
|
+
* @param type - The value type, used to select the appropriate GSettings getter/setter
|
|
56
|
+
* @returns A `[value, setValue]` tuple kept in sync with the GSettings backend
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* const [colorScheme, setColorScheme] = useSetting("org.gnome.desktop.interface", "color-scheme", "string");
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* const [fontSize, setFontSize] = useSetting("com.example.myapp", "font-size", "int");
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export function useSetting<T extends SettingType>(
|
|
69
|
+
schemaId: string,
|
|
70
|
+
key: string,
|
|
71
|
+
type: T,
|
|
72
|
+
): [SettingTypeMap[T], (value: SettingTypeMap[T]) => void] {
|
|
73
|
+
const settings = useMemo(() => new Gio.Settings(schemaId), [schemaId]);
|
|
74
|
+
const [value, setValue] = useState<SettingTypeMap[T]>(() => readSetting(settings, key, type) as SettingTypeMap[T]);
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
setValue(readSetting(settings, key, type) as SettingTypeMap[T]);
|
|
78
|
+
|
|
79
|
+
const handlerId = settings.connect(`changed::${key}`, () => {
|
|
80
|
+
setValue(readSetting(settings, key, type) as SettingTypeMap[T]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return () => {
|
|
84
|
+
GObject.signalHandlerDisconnect(settings, handlerId);
|
|
85
|
+
};
|
|
86
|
+
}, [settings, key, type]);
|
|
87
|
+
|
|
88
|
+
const set = useCallback(
|
|
89
|
+
(newValue: SettingTypeMap[T]) => {
|
|
90
|
+
writeSetting(settings, key, type, newValue);
|
|
91
|
+
},
|
|
92
|
+
[settings, key, type],
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return [value, set];
|
|
96
|
+
}
|