@gtkx/react 1.3.0 → 1.5.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.
Files changed (77) hide show
  1. package/README.md +5 -4
  2. package/dist/adw/dialog.js +2 -2
  3. package/dist/adw/dialog.js.map +1 -1
  4. package/dist/adw/element-behaviors.js +19 -107
  5. package/dist/adw/element-behaviors.js.map +1 -1
  6. package/dist/components/application.d.ts.map +1 -1
  7. package/dist/components/application.js +3 -3
  8. package/dist/components/application.js.map +1 -1
  9. package/dist/components/window.js +3 -3
  10. package/dist/components/window.js.map +1 -1
  11. package/dist/element-behaviors.js +55 -60
  12. package/dist/element-behaviors.js.map +1 -1
  13. package/dist/element-config.js +1 -1
  14. package/dist/element-config.js.map +1 -1
  15. package/dist/hooks/use-bind-setting.d.ts.map +1 -1
  16. package/dist/hooks/use-bind-setting.js +3 -2
  17. package/dist/hooks/use-bind-setting.js.map +1 -1
  18. package/dist/hooks/use-property.d.ts +1 -1
  19. package/dist/hooks/use-property.d.ts.map +1 -1
  20. package/dist/hooks/use-property.js +5 -1
  21. package/dist/hooks/use-property.js.map +1 -1
  22. package/dist/prop-types.d.ts +9 -1
  23. package/dist/prop-types.d.ts.map +1 -1
  24. package/dist/prop-types.js.map +1 -1
  25. package/dist/reconciler/apply-props.d.ts +2 -1
  26. package/dist/reconciler/apply-props.d.ts.map +1 -1
  27. package/dist/reconciler/apply-props.js +59 -24
  28. package/dist/reconciler/apply-props.js.map +1 -1
  29. package/dist/reconciler/behaviors.d.ts +19 -3
  30. package/dist/reconciler/behaviors.d.ts.map +1 -1
  31. package/dist/reconciler/behaviors.js +73 -17
  32. package/dist/reconciler/behaviors.js.map +1 -1
  33. package/dist/reconciler/host-config.d.ts.map +1 -1
  34. package/dist/reconciler/host-config.js +2 -1
  35. package/dist/reconciler/host-config.js.map +1 -1
  36. package/dist/reconciler/metadata.d.ts +6 -1
  37. package/dist/reconciler/metadata.d.ts.map +1 -1
  38. package/dist/reconciler/metadata.js +39 -6
  39. package/dist/reconciler/metadata.js.map +1 -1
  40. package/dist/reconciler/placement.js +4 -4
  41. package/dist/reconciler/placement.js.map +1 -1
  42. package/dist/reconciler/registry.d.ts +2 -0
  43. package/dist/reconciler/registry.d.ts.map +1 -1
  44. package/dist/reconciler/registry.js.map +1 -1
  45. package/dist/reconciler/root.d.ts.map +1 -1
  46. package/dist/reconciler/root.js.map +1 -1
  47. package/dist/reconciler/signals.d.ts +3 -2
  48. package/dist/reconciler/signals.d.ts.map +1 -1
  49. package/dist/reconciler/signals.js +49 -16
  50. package/dist/reconciler/signals.js.map +1 -1
  51. package/dist/reconciler/style.d.ts +2 -1
  52. package/dist/reconciler/style.d.ts.map +1 -1
  53. package/dist/reconciler/style.js +4 -3
  54. package/dist/reconciler/style.js.map +1 -1
  55. package/dist/reconciler/text.js +3 -3
  56. package/dist/reconciler/text.js.map +1 -1
  57. package/env.d.ts +10 -7
  58. package/package.json +6 -6
  59. package/src/adw/dialog.tsx +2 -2
  60. package/src/adw/element-behaviors.ts +22 -162
  61. package/src/components/application.tsx +18 -3
  62. package/src/components/window.tsx +3 -3
  63. package/src/element-behaviors.ts +75 -96
  64. package/src/element-config.ts +1 -1
  65. package/src/hooks/use-bind-setting.ts +3 -2
  66. package/src/hooks/use-property.ts +6 -2
  67. package/src/prop-types.ts +10 -0
  68. package/src/reconciler/apply-props.ts +73 -26
  69. package/src/reconciler/behaviors.ts +130 -33
  70. package/src/reconciler/host-config.ts +3 -1
  71. package/src/reconciler/metadata.ts +62 -6
  72. package/src/reconciler/placement.ts +4 -4
  73. package/src/reconciler/registry.ts +2 -0
  74. package/src/reconciler/root.ts +1 -1
  75. package/src/reconciler/signals.ts +62 -17
  76. package/src/reconciler/style.ts +4 -3
  77. package/src/reconciler/text.ts +3 -3
@@ -2,8 +2,6 @@ import type * as Gtk from "@gtkx/gi/gtk";
2
2
  import * as Adw from "@gtkx/gi/adw";
3
3
  import type { AlertDialogResponse } from "./prop-types.js";
4
4
  import {
5
- addRemoveSlot,
6
- adoptedChildrenSlot,
7
5
  applicationCreator,
8
6
  boxSlot,
9
7
  childMatcher,
@@ -12,6 +10,8 @@ import {
12
10
  deferred,
13
11
  indexedSlot,
14
12
  list,
13
+ methodSlot,
14
+ setterSlot,
15
15
  slot,
16
16
  } from "../reconciler/behaviors.js";
17
17
  import { type ElementBehavior, type ElementConfig, forTypes, registerElements } from "../reconciler/registry.js";
@@ -36,56 +36,18 @@ type PrefixSuffixRow = Adw.ActionRow | Adw.EntryRow | Adw.ExpanderRow;
36
36
  const SLOT_SUFFIX = "Slot";
37
37
  const childSetter = childSetterSlot<AdwChildSetter>();
38
38
  const contentSetter = contentSetterSlot<AdwContentSetter>();
39
-
40
- const breakpoints = slot<BreakpointHost, Adw.Breakpoint>("breakpoints", "AdwBreakpoint", {
41
- attach: (host, breakpoint) => {
42
- host.addBreakpoint(breakpoint);
43
- },
44
- });
39
+ const breakpoints = methodSlot<BreakpointHost, Adw.Breakpoint>("breakpoints", "AdwBreakpoint", "addBreakpoint");
45
40
 
46
41
  const prefixSuffix = [
47
- addRemoveSlot<Gtk.Widget, PrefixSuffixRow>(
48
- "prefix",
49
- "GtkWidget",
50
- (row, child) => {
51
- row.addPrefix(child);
52
- },
53
- (row, child) => {
54
- row.remove(child);
55
- },
56
- ),
57
- addRemoveSlot<Gtk.Widget, PrefixSuffixRow>(
58
- "suffix",
59
- "GtkWidget",
60
- (row, child) => {
61
- row.addSuffix(child);
62
- },
63
- (row, child) => {
64
- row.remove(child);
65
- },
66
- ),
42
+ methodSlot<PrefixSuffixRow, Gtk.Widget>("prefix", "GtkWidget", "addPrefix", "remove"),
43
+ methodSlot<PrefixSuffixRow, Gtk.Widget>("suffix", "GtkWidget", "addSuffix", "remove"),
67
44
  ];
68
45
 
69
- const preferencesDialogChildren = addRemoveSlot<Adw.PreferencesPage, Adw.PreferencesDialog>(
70
- "children",
71
- "AdwPreferencesPage",
72
- (dialog, page) => {
73
- dialog.add(page);
74
- },
75
- (dialog, page) => {
76
- dialog.remove(page);
77
- },
46
+ const preferencesDialogChildren = methodSlot<Adw.PreferencesDialog, Adw.PreferencesPage>(
47
+ "children", "AdwPreferencesPage", "add", "remove",
78
48
  );
79
49
 
80
- const alertDialogExtraChild = slot<Adw.AlertDialog, Gtk.Widget>("children", "GtkWidget", {
81
- attach: (dialog, child) => {
82
- dialog.setExtraChild(child);
83
- },
84
- detach: (dialog) => {
85
- dialog.setExtraChild(null);
86
- },
87
- });
88
-
50
+ const alertDialogExtraChild = setterSlot<Adw.AlertDialog, Gtk.Widget>("children", "GtkWidget", "setExtraChild");
89
51
  const sidebarSections = indexedSlot<Adw.Sidebar, Adw.SidebarSection>("children", "AdwSidebarSection");
90
52
  const sidebarItems = indexedSlot<Adw.SidebarSection, Adw.SidebarItem>("children", "AdwSidebarItem");
91
53
  const isWidget = childMatcher("GtkWidget");
@@ -151,14 +113,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
151
113
  },
152
114
  AdwClampScrollable: {
153
115
  behaviors: [
154
- slot<Adw.ClampScrollable, Gtk.Scrollable & Gtk.Widget>("children", "GtkScrollable", {
155
- attach: (clamp, child) => {
156
- clamp.setChild(child);
157
- },
158
- detach: (clamp) => {
159
- clamp.setChild(null);
160
- },
161
- }),
116
+ setterSlot<Adw.ClampScrollable, Gtk.Scrollable & Gtk.Widget>("children", "GtkScrollable", "setChild"),
162
117
  ],
163
118
  },
164
119
  ...forTypes(CHILD_SETTER_TYPES, {
@@ -179,15 +134,8 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
179
134
  AdwBreakpointBin: {
180
135
  behaviors: [
181
136
  childSetter,
182
- addRemoveSlot<Adw.Breakpoint, Adw.BreakpointBin>(
183
- "breakpoints",
184
- "AdwBreakpoint",
185
- (bin, breakpoint) => {
186
- bin.addBreakpoint(breakpoint);
187
- },
188
- (bin, breakpoint) => {
189
- bin.removeBreakpoint(breakpoint);
190
- },
137
+ methodSlot<Adw.BreakpointBin, Adw.Breakpoint>(
138
+ "breakpoints", "AdwBreakpoint", "addBreakpoint", "removeBreakpoint",
191
139
  ),
192
140
  ],
193
141
  },
@@ -200,16 +148,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
200
148
  AdwExpanderRow: {
201
149
  behaviors: [
202
150
  ...prefixSuffix,
203
- addRemoveSlot<Gtk.Widget, Adw.ExpanderRow>(
204
- "rows",
205
- "GtkWidget",
206
- (row, child) => {
207
- row.addRow(child);
208
- },
209
- (row, child) => {
210
- row.remove(child);
211
- },
212
- ),
151
+ methodSlot<Adw.ExpanderRow, Gtk.Widget>("rows", "GtkWidget", "addRow", "remove"),
213
152
  ],
214
153
  },
215
154
  AdwNavigationSplitView: {
@@ -249,18 +188,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
249
188
  behaviors: [preferencesDialogChildren],
250
189
  },
251
190
  AdwPreferencesGroup: {
252
- behaviors: [
253
- addRemoveSlot<Gtk.Widget, Adw.PreferencesGroup>(
254
- "children",
255
- "GtkWidget",
256
- (group, child) => {
257
- group.add(child);
258
- },
259
- (group, child) => {
260
- group.remove(child);
261
- },
262
- ),
263
- ],
191
+ behaviors: [methodSlot<Adw.PreferencesGroup, Gtk.Widget>("children", "GtkWidget", "add", "remove")],
264
192
  },
265
193
  AdwTabView: {
266
194
  behaviors: [
@@ -282,105 +210,37 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
282
210
  },
283
211
  AdwNavigationView: {
284
212
  behaviors: [
285
- addRemoveSlot<Adw.NavigationPage, Adw.NavigationView>(
286
- "children",
287
- "AdwNavigationPage",
288
- (view, page) => {
289
- view.add(page);
290
- },
291
- (view, page) => {
292
- view.remove(page);
293
- },
294
- ),
213
+ methodSlot<Adw.NavigationView, Adw.NavigationPage>("children", "AdwNavigationPage", "add", "remove"),
295
214
  ],
296
215
  },
297
216
  AdwViewStack: {
298
217
  behaviors: [
299
- adoptedChildrenSlot<Adw.ViewStack, Gtk.Widget>(
300
- "GtkWidget",
301
- (stack, child) => stack.add(child),
302
- (stack, child) => {
303
- stack.remove(child);
304
- },
305
- ),
218
+ methodSlot<Adw.ViewStack, Gtk.Widget>("children", "GtkWidget", "add", "remove"),
306
219
  deferred<Adw.ViewStack, string>("visibleChildName", (stack, name) => stack.getChildByName(name) !== null),
307
220
  ],
308
221
  },
309
222
  AdwToolbarView: {
310
223
  behaviors: [
311
224
  contentSetterSlot<Adw.ToolbarView>(),
312
- addRemoveSlot<Gtk.Widget, Adw.ToolbarView>(
313
- "topBar",
314
- "GtkWidget",
315
- (view, child) => {
316
- view.addTopBar(child);
317
- },
318
- (view, child) => {
319
- view.remove(child);
320
- },
321
- ),
322
- addRemoveSlot<Gtk.Widget, Adw.ToolbarView>(
323
- "bottomBar",
324
- "GtkWidget",
325
- (view, child) => {
326
- view.addBottomBar(child);
327
- },
328
- (view, child) => {
329
- view.remove(child);
330
- },
331
- ),
225
+ methodSlot<Adw.ToolbarView, Gtk.Widget>("topBar", "GtkWidget", "addTopBar", "remove"),
226
+ methodSlot<Adw.ToolbarView, Gtk.Widget>("bottomBar", "GtkWidget", "addBottomBar", "remove"),
332
227
  ],
333
228
  },
334
229
  AdwHeaderBar: {
335
230
  behaviors: [
336
- slot<Adw.HeaderBar, Gtk.Widget>("start", "GtkWidget", {
337
- attach: (bar, child) => {
338
- bar.packStart(child);
339
- },
340
- detach: (bar, child) => {
341
- bar.remove(child);
342
- },
343
- }),
344
- slot<Adw.HeaderBar, Gtk.Widget>("end", "GtkWidget", {
345
- attach: (bar, child) => {
346
- bar.packEnd(child);
347
- },
348
- detach: (bar, child) => {
349
- bar.remove(child);
350
- },
351
- }),
231
+ methodSlot<Adw.HeaderBar, Gtk.Widget>("start", "GtkWidget", "packStart", "remove"),
232
+ methodSlot<Adw.HeaderBar, Gtk.Widget>("end", "GtkWidget", "packEnd", "remove"),
352
233
  ],
353
234
  },
354
235
  AdwShortcutsDialog: {
355
- behaviors: [
356
- slot<Adw.ShortcutsDialog, Adw.ShortcutsSection>("children", "AdwShortcutsSection", {
357
- attach: (dialog, section) => {
358
- dialog.add(section);
359
- },
360
- }),
361
- ],
236
+ behaviors: [methodSlot<Adw.ShortcutsDialog, Adw.ShortcutsSection>("children", "AdwShortcutsSection", "add")],
362
237
  },
363
238
  AdwShortcutsSection: {
364
- behaviors: [
365
- slot<Adw.ShortcutsSection, Adw.ShortcutsItem>("children", "AdwShortcutsItem", {
366
- attach: (section, item) => {
367
- section.add(item);
368
- },
369
- }),
370
- ],
239
+ behaviors: [methodSlot<Adw.ShortcutsSection, Adw.ShortcutsItem>("children", "AdwShortcutsItem", "add")],
371
240
  },
372
241
  AdwToggleGroup: {
373
242
  behaviors: [
374
- addRemoveSlot<Adw.Toggle, Adw.ToggleGroup>(
375
- "children",
376
- "AdwToggle",
377
- (group, toggle) => {
378
- group.add(toggle);
379
- },
380
- (group, toggle) => {
381
- group.remove(toggle);
382
- },
383
- ),
243
+ methodSlot<Adw.ToggleGroup, Adw.Toggle>("children", "AdwToggle", "add", "remove"),
384
244
  deferred<Adw.ToggleGroup, string>("activeName", (group, name) => group.getToggleByName(name) !== null),
385
245
  deferred("active"),
386
246
  ],
@@ -3,7 +3,10 @@ import { quitApplication, runApplication } from "@gtkx/runtime";
3
3
  import { pickBy, warn } from "@gtkx/utils";
4
4
  import process from "node:process";
5
5
  import { type ElementType, type ReactNode, type Ref, useLayoutEffect, useState } from "react";
6
- import { applicationId as defaultApplicationId } from "virtual:gtkx-config";
6
+ import {
7
+ applicationId as defaultApplicationId,
8
+ resourceBasePath as defaultResourceBasePath,
9
+ } from "virtual:gtkx-config";
7
10
  import { ApplicationContext } from "../hooks/use-application.js";
8
11
  import { useMergedRef } from "../hooks/use-merged-refs.js";
9
12
  import { createPortaledComponent } from "./portaled.js";
@@ -12,6 +15,7 @@ type ApplicationComponentProps = {
12
15
  applicationId?: string | null | undefined;
13
16
  children?: ReactNode | undefined;
14
17
  ref?: Ref<Gtk.Application | null> | undefined;
18
+ resourceBasePath?: string | null | undefined;
15
19
  };
16
20
 
17
21
  const POST_ACTIVATE_PROPS = new Set(["menubar"]);
@@ -80,7 +84,13 @@ const applicationChildren = (application: Gtk.Application | null, children: Reac
80
84
  const createApplicationElement = (
81
85
  Component: ElementType,
82
86
  ): ((props: ApplicationComponentProps) => ReactNode) => {
83
- return ({ applicationId = defaultApplicationId, children, ref, ...rest }: ApplicationComponentProps): ReactNode => {
87
+ return ({
88
+ applicationId = defaultApplicationId,
89
+ children,
90
+ ref,
91
+ resourceBasePath = defaultResourceBasePath,
92
+ ...rest
93
+ }: ApplicationComponentProps): ReactNode => {
84
94
  const [application, setApplication] = useState<Gtk.Application | null>(null);
85
95
  const [activated, setActivated] = useState(false);
86
96
  useApplicationLifecycle(application, setActivated, applicationId);
@@ -88,7 +98,12 @@ const createApplicationElement = (
88
98
  const appliedProps = activated ? rest : pickBy(rest, (_value, key) => !POST_ACTIVATE_PROPS.has(key));
89
99
 
90
100
  return (
91
- <Component ref={mergedRef} applicationId={applicationId} {...appliedProps}>
101
+ <Component
102
+ ref={mergedRef}
103
+ applicationId={applicationId}
104
+ {...appliedProps}
105
+ resourceBasePath={resourceBasePath}
106
+ >
92
107
  {activated ? applicationChildren(application, children) : null}
93
108
  </Component>
94
109
  );
@@ -2,7 +2,7 @@ import type * as Gtk from "@gtkx/gi/gtk";
2
2
  import { type ElementType, type ReactElement, type ReactNode, use } from "react";
3
3
  import { ParentWindowContext } from "../hooks/use-parent-window.js";
4
4
  import { createPresentedComponent, type PresentedProps } from "../hooks/use-presented-instance.js";
5
- import { applyWrite } from "../reconciler/signals.js";
5
+ import { applyMutation } from "../reconciler/signals.js";
6
6
  import { createPortaledComponent } from "./portaled.js";
7
7
 
8
8
  type WindowComponentProps = PresentedProps<Gtk.Window> & {
@@ -11,13 +11,13 @@ type WindowComponentProps = PresentedProps<Gtk.Window> & {
11
11
  };
12
12
 
13
13
  const presentWindow = (window: Gtk.Window): void => {
14
- applyWrite(() => {
14
+ applyMutation(() => {
15
15
  window.present();
16
16
  });
17
17
  };
18
18
 
19
19
  const destroyWindow = (window: Gtk.Window): void => {
20
- applyWrite(() => {
20
+ applyMutation(() => {
21
21
  window.destroy();
22
22
  });
23
23
  };
@@ -14,14 +14,15 @@ import type {
14
14
  } from "./prop-types.js";
15
15
  import { BUILTIN_ELEMENTS, SINGLE_CHILD_TYPES } from "./element-config.js";
16
16
  import {
17
- addRemoveSlot,
18
- adoptedChildrenSlot,
19
17
  applicationCreator,
20
18
  boxSlot,
21
19
  childSetterSlot,
22
20
  controlledText,
23
21
  deferred,
22
+ deferredWith,
24
23
  list,
24
+ methodSlot,
25
+ setterSlot,
25
26
  slot,
26
27
  value,
27
28
  wrappingIndexedSlot,
@@ -34,7 +35,11 @@ import {
34
35
  registerElements,
35
36
  } from "./reconciler/registry.js";
36
37
  import { applyWrite } from "./reconciler/signals.js";
37
- import { applyStyle, styleClass } from "./reconciler/style.js";
38
+ import { applyStyle, CSS_CLASSES_PROP, styleClass } from "./reconciler/style.js";
39
+
40
+ const SELECTED_INDEX_PROP = "selectedIndex";
41
+ const SELECTION_SIGNAL = "selected-rows-changed";
42
+ const NO_SELECTION = -1;
38
43
 
39
44
  const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
40
45
  ...forTypes(SINGLE_CHILD_TYPES, {
@@ -42,26 +47,8 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
42
47
  }),
43
48
  ...forTypes(["GtkHeaderBar", "GtkActionBar"], {
44
49
  behaviors: [
45
- addRemoveSlot<Gtk.Widget, Gtk.HeaderBar | Gtk.ActionBar>(
46
- "start",
47
- "GtkWidget",
48
- (bar, child) => {
49
- bar.packStart(child);
50
- },
51
- (bar, child) => {
52
- bar.remove(child);
53
- },
54
- ),
55
- addRemoveSlot<Gtk.Widget, Gtk.HeaderBar | Gtk.ActionBar>(
56
- "end",
57
- "GtkWidget",
58
- (bar, child) => {
59
- bar.packEnd(child);
60
- },
61
- (bar, child) => {
62
- bar.remove(child);
63
- },
64
- ),
50
+ methodSlot<Gtk.HeaderBar | Gtk.ActionBar, Gtk.Widget>("start", "GtkWidget", "packStart", "remove"),
51
+ methodSlot<Gtk.HeaderBar | Gtk.ActionBar, Gtk.Widget>("end", "GtkWidget", "packEnd", "remove"),
65
52
  ],
66
53
  }),
67
54
  GtkWindow: {
@@ -77,24 +64,10 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
77
64
  popover.unparent();
78
65
  },
79
66
  }),
80
- addRemoveSlot<Gtk.EventController, Gtk.Widget>(
81
- "controllers",
82
- "GtkEventController",
83
- (widget, controller) => {
84
- widget.addController(controller);
85
- },
86
- (widget, controller) => {
87
- widget.removeController(controller);
88
- },
67
+ methodSlot<Gtk.Widget, Gtk.EventController>(
68
+ "controllers", "GtkEventController", "addController", "removeController",
89
69
  ),
90
- slot<Gtk.Widget, Gtk.LayoutManager>("layoutManager", "GtkLayoutManager", {
91
- attach: (widget, manager) => {
92
- widget.setLayoutManager(manager);
93
- },
94
- detach: (widget) => {
95
- widget.setLayoutManager(null);
96
- },
97
- }),
70
+ setterSlot<Gtk.Widget, Gtk.LayoutManager>("layoutManager", "GtkLayoutManager", "setLayoutManager"),
98
71
  slot<Gtk.Widget, Gio.ActionGroup>("actionGroups", "GActionGroup", {
99
72
  attach: (widget, group, info) => {
100
73
  widget.insertActionGroup((info.props.prefix as string | null) ?? "", group);
@@ -114,6 +87,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
114
87
  wrappingIndexedSlot(Gtk.ListBoxRow, (row, inner) => {
115
88
  row.setChild(inner);
116
89
  }),
90
+ selectedIndexBehavior(),
117
91
  ],
118
92
  },
119
93
  GtkFlowBox: {
@@ -139,15 +113,8 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
139
113
  },
140
114
  GtkShortcutController: {
141
115
  behaviors: [
142
- addRemoveSlot<Gtk.Shortcut, Gtk.ShortcutController>(
143
- "shortcuts",
144
- "GtkShortcut",
145
- (controller, shortcut) => {
146
- controller.addShortcut(shortcut);
147
- },
148
- (controller, shortcut) => {
149
- controller.removeShortcut(shortcut);
150
- },
116
+ methodSlot<Gtk.ShortcutController, Gtk.Shortcut>(
117
+ "shortcuts", "GtkShortcut", "addShortcut", "removeShortcut",
151
118
  ),
152
119
  ],
153
120
  },
@@ -155,16 +122,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
155
122
  behaviors: [{ create: () => Gtk.TextChildAnchor.new() }],
156
123
  },
157
124
  GtkTextView: {
158
- behaviors: [
159
- slot<Gtk.TextView, Gtk.TextBuffer>("children", "GtkTextBuffer", {
160
- attach: (view, buffer) => {
161
- view.setBuffer(buffer);
162
- },
163
- detach: (view) => {
164
- view.setBuffer(null);
165
- },
166
- }),
167
- ],
125
+ behaviors: [setterSlot<Gtk.TextView, Gtk.TextBuffer>("children", "GtkTextBuffer", "setBuffer")],
168
126
  },
169
127
  GActionMap: {
170
128
  behaviors: [
@@ -242,25 +200,11 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
242
200
  },
243
201
  GtkConstraintLayout: {
244
202
  behaviors: [
245
- addRemoveSlot<Gtk.Constraint, Gtk.ConstraintLayout>(
246
- "constraints",
247
- "GtkConstraint",
248
- (layout, constraint) => {
249
- layout.addConstraint(constraint);
250
- },
251
- (layout, constraint) => {
252
- layout.removeConstraint(constraint);
253
- },
203
+ methodSlot<Gtk.ConstraintLayout, Gtk.Constraint>(
204
+ "constraints", "GtkConstraint", "addConstraint", "removeConstraint",
254
205
  ),
255
- addRemoveSlot<Gtk.ConstraintGuide, Gtk.ConstraintLayout>(
256
- "guides",
257
- "GtkConstraintGuide",
258
- (layout, guide) => {
259
- layout.addGuide(guide);
260
- },
261
- (layout, guide) => {
262
- layout.removeGuide(guide);
263
- },
206
+ methodSlot<Gtk.ConstraintLayout, Gtk.ConstraintGuide>(
207
+ "guides", "GtkConstraintGuide", "addGuide", "removeGuide",
264
208
  ),
265
209
  list<Gtk.ConstraintLayout, VflConstraints, Gtk.Constraint[]>("vfl", {
266
210
  add: (layout, item) => [
@@ -281,13 +225,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
281
225
  },
282
226
  GtkStack: {
283
227
  behaviors: [
284
- adoptedChildrenSlot<Gtk.Stack, Gtk.Widget>(
285
- "GtkWidget",
286
- (stack, child) => stack.addChild(child),
287
- (stack, child) => {
288
- stack.remove(child);
289
- },
290
- ),
228
+ methodSlot<Gtk.Stack, Gtk.Widget>("children", "GtkWidget", "addChild", "remove"),
291
229
  deferred<Gtk.Stack, string>("visibleChildName", (stack, name) => stack.getChildByName(name) !== null),
292
230
  ],
293
231
  },
@@ -308,16 +246,7 @@ const BUILTIN_BEHAVIORS: Record<string, ElementConfig<never>> = {
308
246
  GtkApplication: {
309
247
  behaviors: [
310
248
  applicationCreator(Gtk.Application),
311
- addRemoveSlot<Gtk.Window, Gtk.Application>(
312
- "children",
313
- "GtkWindow",
314
- (application, window) => {
315
- application.addWindow(window);
316
- },
317
- (application, window) => {
318
- application.removeWindow(window);
319
- },
320
- ),
249
+ methodSlot<Gtk.Application, Gtk.Window>("children", "GtkWindow", "addWindow", "removeWindow"),
321
250
  list<Gtk.Application, ActionAccel>("actionAccels", {
322
251
  add: (application, item) => {
323
252
  application.setAccelsForAction(item.detailedActionName, item.accels);
@@ -430,8 +359,8 @@ const didWriteCssClasses = (widget: Gtk.Widget, prev: Props, next: Props, classN
430
359
  return false;
431
360
  }
432
361
 
433
- applyWrite(() => {
434
- Reflect.set(widget, "cssClasses", withStyleClass(next.cssClasses, className));
362
+ applyWrite(CSS_CLASSES_PROP, () => {
363
+ Reflect.set(widget, CSS_CLASSES_PROP, withStyleClass(next.cssClasses, className));
435
364
  });
436
365
 
437
366
  return true;
@@ -460,6 +389,56 @@ function styleBehavior(): ElementBehavior<Gtk.Widget> {
460
389
  return { update: updateStyle };
461
390
  }
462
391
 
392
+ function selectedRowIndex(box: Gtk.ListBox): number {
393
+ return box.getSelectedRow()?.getIndex() ?? NO_SELECTION;
394
+ }
395
+
396
+ function selectedIndexError(value: number): Error {
397
+ return new Error(
398
+ "The 'selectedIndex' of a <GtkListBox> must be a whole number, or -1 to select no row; " +
399
+ `received ${String(value)}.`,
400
+ );
401
+ }
402
+
403
+ function desiredIndex(value: unknown): number | undefined {
404
+ if (value === null) {
405
+ return NO_SELECTION;
406
+ }
407
+
408
+ if (typeof value !== "number") {
409
+ return undefined;
410
+ }
411
+
412
+ if (!Number.isSafeInteger(value)) {
413
+ throw selectedIndexError(value);
414
+ }
415
+
416
+ return value;
417
+ }
418
+
419
+ function applySelectedIndex(box: Gtk.ListBox, index: number): void {
420
+ if (index < 0) {
421
+ box.unselectAll();
422
+
423
+ return;
424
+ }
425
+
426
+ const row = box.getRowAtIndex(index);
427
+
428
+ if (row !== null) {
429
+ box.selectRow(row);
430
+ }
431
+ }
432
+
433
+ function selectedIndexBehavior(): ElementBehavior<Gtk.ListBox> {
434
+ return deferredWith<Gtk.ListBox, number>(SELECTED_INDEX_PROP, {
435
+ parse: desiredIndex,
436
+ read: selectedRowIndex,
437
+ write: applySelectedIndex,
438
+ signal: SELECTION_SIGNAL,
439
+ });
440
+ }
441
+
463
442
  const addMainOption = (application: Gtk.Application, option: MainOption): void => {
464
443
  application.addMainOption(
465
444
  option.longName,
@@ -82,7 +82,7 @@ const BUILTIN_ELEMENTS: Record<string, ElementConfig> = {
82
82
  props: internal("ChildrenProps"),
83
83
  },
84
84
  GtkListBox: {
85
- props: internal("ChildrenProps"),
85
+ props: internal("GtkListBoxProps"),
86
86
  },
87
87
  GtkFlowBox: {
88
88
  props: internal("ChildrenProps"),
@@ -3,6 +3,7 @@ import * as Gio from "@gtkx/gi/gio";
3
3
  import { kebabCase } from "@gtkx/utils";
4
4
  import { useLayoutEffect } from "react";
5
5
  import type { SettingsSchema, SettingsSchemaKeys } from "../utils/settings.js";
6
+ import { getPropertyName } from "../reconciler/metadata.js";
6
7
  import { type RefProp, resolveRefProp } from "../utils/ref-prop.js";
7
8
  import { useSettings } from "./use-setting.js";
8
9
 
@@ -32,7 +33,6 @@ function useBindSetting<K extends SettingsSchemaKeys>({
32
33
  flags = Gio.SettingsBindFlags.DEFAULT,
33
34
  }: UseBindSettingOptions<K>): void {
34
35
  const settings = useSettings(schema);
35
- const propertyName = kebabCase(property);
36
36
 
37
37
  useLayoutEffect(() => {
38
38
  const resolved = resolveRefProp(object);
@@ -41,12 +41,13 @@ function useBindSetting<K extends SettingsSchemaKeys>({
41
41
  return;
42
42
  }
43
43
 
44
+ const propertyName = getPropertyName(resolved, property) ?? kebabCase(property);
44
45
  settings.bind(key, resolved, propertyName, flags);
45
46
 
46
47
  return () => {
47
48
  Gio.Settings.unbind(resolved, propertyName);
48
49
  };
49
- }, [settings, key, object, propertyName, flags]);
50
+ }, [settings, key, object, property, flags]);
50
51
  }
51
52
 
52
53
  export { useBindSetting };
@@ -1,6 +1,7 @@
1
1
  import type * as GObject from "@gtkx/gi/gobject";
2
2
  import { kebabCase } from "@gtkx/utils";
3
- import type { RefProp } from "../utils/ref-prop.js";
3
+ import { getPropertyName } from "../reconciler/metadata.js";
4
+ import { type RefProp, resolveRefProp } from "../utils/ref-prop.js";
4
5
  import { useObjectValue } from "./use-object-value.js";
5
6
 
6
7
  /** The property map `T` declares, from camelCase property name to value type. */
@@ -18,7 +19,10 @@ function useProperty<T extends GObject.Object, P extends PropertyName<T>>(
18
19
  object: RefProp<T>,
19
20
  propertyName: P & string,
20
21
  ): T[P] | undefined {
21
- return useObjectValue(object, `notify::${kebabCase(propertyName)}`, (obj) => obj?.[propertyName]);
22
+ const resolved = resolveRefProp(object);
23
+ const name = (resolved === null ? undefined : getPropertyName(resolved, propertyName)) ?? kebabCase(propertyName);
24
+
25
+ return useObjectValue(object, `notify::${name}`, (obj) => obj?.[propertyName]);
22
26
  }
23
27
 
24
28
  export { useProperty };