@gtkx/react 0.14.0 → 0.15.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 (52) hide show
  1. package/README.md +27 -27
  2. package/dist/generated/jsx.d.ts +235 -228
  3. package/dist/host-config.js +2 -2
  4. package/dist/jsx.d.ts +111 -2
  5. package/dist/jsx.js +79 -0
  6. package/dist/nodes/adjustment.d.ts +48 -0
  7. package/dist/nodes/adjustment.js +70 -0
  8. package/dist/nodes/calendar.js +3 -16
  9. package/dist/nodes/column-view-column.d.ts +1 -0
  10. package/dist/nodes/column-view-column.js +4 -0
  11. package/dist/nodes/column-view.js +6 -6
  12. package/dist/nodes/drawing-area.d.ts +1 -0
  13. package/dist/nodes/drawing-area.js +19 -0
  14. package/dist/nodes/expander-row.js +1 -0
  15. package/dist/nodes/index.d.ts +8 -0
  16. package/dist/nodes/index.js +8 -0
  17. package/dist/nodes/internal/constants.js +3 -0
  18. package/dist/nodes/internal/list-item-renderer.d.ts +1 -0
  19. package/dist/nodes/internal/list-item-renderer.js +5 -0
  20. package/dist/nodes/internal/predicates.d.ts +4 -0
  21. package/dist/nodes/internal/predicates.js +3 -0
  22. package/dist/nodes/internal/signal-store.d.ts +1 -0
  23. package/dist/nodes/internal/signal-store.js +7 -0
  24. package/dist/nodes/internal/tree-list-item-renderer.d.ts +1 -0
  25. package/dist/nodes/internal/tree-list-item-renderer.js +8 -0
  26. package/dist/nodes/level-bar.js +3 -16
  27. package/dist/nodes/list-view.js +6 -2
  28. package/dist/nodes/navigation-page.js +32 -23
  29. package/dist/nodes/notebook-page-tab.d.ts +1 -0
  30. package/dist/nodes/notebook-page-tab.js +15 -9
  31. package/dist/nodes/notebook-page.js +9 -6
  32. package/dist/nodes/scale.js +3 -16
  33. package/dist/nodes/scrolled-window.js +3 -2
  34. package/dist/nodes/shortcut-controller.d.ts +37 -0
  35. package/dist/nodes/shortcut-controller.js +74 -0
  36. package/dist/nodes/shortcut.d.ts +38 -0
  37. package/dist/nodes/shortcut.js +46 -0
  38. package/dist/nodes/source-buffer.d.ts +73 -0
  39. package/dist/nodes/source-buffer.js +149 -0
  40. package/dist/nodes/source-view.d.ts +1 -0
  41. package/dist/nodes/source-view.js +42 -0
  42. package/dist/nodes/stack-page.js +15 -9
  43. package/dist/nodes/text-buffer.d.ts +43 -0
  44. package/dist/nodes/text-buffer.js +81 -0
  45. package/dist/nodes/text-view.d.ts +1 -0
  46. package/dist/nodes/text-view.js +45 -0
  47. package/dist/nodes/tree-list-view.js +6 -2
  48. package/dist/nodes/widget.d.ts +4 -0
  49. package/dist/nodes/widget.js +85 -10
  50. package/dist/nodes/window.js +7 -5
  51. package/dist/types.d.ts +26 -0
  52. package/package.json +3 -3
@@ -18,6 +18,7 @@ const LIFECYCLE_SIGNALS = new Set([
18
18
  class SignalStore {
19
19
  ownerHandlers = new Map();
20
20
  blockedHandlers = new Set();
21
+ isBlocking = false;
21
22
  getOwnerMap(owner) {
22
23
  let map = this.ownerHandlers.get(owner);
23
24
  if (!map) {
@@ -41,6 +42,10 @@ class SignalStore {
41
42
  const key = `${objectId}:${signal}`;
42
43
  const handlerId = obj.connect(signal, handler);
43
44
  this.getOwnerMap(owner).set(key, { obj, handlerId });
45
+ if (this.isBlocking && !LIFECYCLE_SIGNALS.has(signal)) {
46
+ GObject.signalHandlerBlock(obj, handlerId);
47
+ this.blockedHandlers.add(handlerId);
48
+ }
44
49
  }
45
50
  set(owner, obj, signal, handler) {
46
51
  this.disconnect(owner, obj, signal);
@@ -58,6 +63,7 @@ class SignalStore {
58
63
  }
59
64
  }
60
65
  blockAll() {
66
+ this.isBlocking = true;
61
67
  this.blockedHandlers.clear();
62
68
  for (const ownerMap of this.ownerHandlers.values()) {
63
69
  for (const [key, { obj, handlerId }] of ownerMap.entries()) {
@@ -70,6 +76,7 @@ class SignalStore {
70
76
  }
71
77
  }
72
78
  unblockAll() {
79
+ this.isBlocking = false;
73
80
  for (const ownerMap of this.ownerHandlers.values()) {
74
81
  for (const { obj, handlerId } of ownerMap.values()) {
75
82
  if (this.blockedHandlers.has(handlerId)) {
@@ -18,6 +18,7 @@ export declare class TreeListItemRenderer {
18
18
  setStore(store?: TreeStore | null): void;
19
19
  setEstimatedItemHeight(height?: number): void;
20
20
  private getStore;
21
+ dispose(): void;
21
22
  private initialize;
22
23
  private processPendingBind;
23
24
  private renderBind;
@@ -35,6 +35,14 @@ export class TreeListItemRenderer {
35
35
  }
36
36
  return this.store;
37
37
  }
38
+ dispose() {
39
+ signalStore.clear(this);
40
+ this.fiberRoots.clear();
41
+ this.expanders.clear();
42
+ this.setupComplete.clear();
43
+ this.pendingBinds.clear();
44
+ this.tornDown.clear();
45
+ }
38
46
  initialize() {
39
47
  signalStore.set(this, this.factory, "setup", (_self, listItem) => {
40
48
  const ptr = getNativeId(listItem.handle);
@@ -3,7 +3,6 @@ import { registerNodeClass } from "../registry.js";
3
3
  import { CommitPriority, scheduleAfterCommit } from "../scheduler.js";
4
4
  import { isContainerType } from "./internal/utils.js";
5
5
  import { LevelBarOffsetNode } from "./level-bar-offset.js";
6
- import { SlotNode } from "./slot.js";
7
6
  import { WidgetNode } from "./widget.js";
8
7
  class LevelBarNode extends WidgetNode {
9
8
  static priority = 1;
@@ -24,11 +23,7 @@ class LevelBarNode extends WidgetNode {
24
23
  });
25
24
  return;
26
25
  }
27
- if (child instanceof SlotNode || child instanceof WidgetNode) {
28
- super.appendChild(child);
29
- return;
30
- }
31
- throw new Error(`Cannot append '${child.typeName}' to 'LevelBar': expected x.LevelBarOffset or Widget`);
26
+ super.appendChild(child);
32
27
  }
33
28
  insertBefore(child, before) {
34
29
  if (child instanceof LevelBarOffsetNode) {
@@ -43,11 +38,7 @@ class LevelBarNode extends WidgetNode {
43
38
  this.scheduleRebuildAllOffsets();
44
39
  return;
45
40
  }
46
- if (child instanceof SlotNode || child instanceof WidgetNode) {
47
- super.insertBefore(child, before);
48
- return;
49
- }
50
- throw new Error(`Cannot insert '${child.typeName}' into 'LevelBar': expected x.LevelBarOffset or Widget`);
41
+ super.insertBefore(child, before);
51
42
  }
52
43
  removeChild(child) {
53
44
  if (child instanceof LevelBarOffsetNode) {
@@ -58,11 +49,7 @@ class LevelBarNode extends WidgetNode {
58
49
  this.scheduleRebuildAllOffsets(CommitPriority.HIGH);
59
50
  return;
60
51
  }
61
- if (child instanceof SlotNode || child instanceof WidgetNode) {
62
- super.removeChild(child);
63
- return;
64
- }
65
- throw new Error(`Cannot remove '${child.typeName}' from 'LevelBar': expected x.LevelBarOffset or Widget`);
52
+ super.removeChild(child);
66
53
  }
67
54
  scheduleRebuildAllOffsets(priority = CommitPriority.NORMAL) {
68
55
  scheduleAfterCommit(() => {
@@ -28,6 +28,10 @@ class ListViewNode extends WidgetNode {
28
28
  super.mount();
29
29
  this.container.setModel(this.list.getSelectionModel());
30
30
  }
31
+ unmount() {
32
+ this.itemRenderer.dispose();
33
+ super.unmount();
34
+ }
31
35
  appendChild(child) {
32
36
  if (!(child instanceof ListItemNode)) {
33
37
  throw new Error(`Cannot append '${child.typeName}' to 'ListView': expected x.ListItem`);
@@ -54,12 +58,12 @@ class ListViewNode extends WidgetNode {
54
58
  this.itemRenderer.setEstimatedItemHeight(newProps.estimatedItemHeight);
55
59
  }
56
60
  const previousModel = this.list.getSelectionModel();
57
- this.list.updateProps(oldProps ? filterProps(oldProps, PROP_NAMES) : null, filterProps(newProps, PROP_NAMES));
61
+ this.list.updateProps(filterProps(oldProps ?? {}, PROP_NAMES), filterProps(newProps, PROP_NAMES));
58
62
  const currentModel = this.list.getSelectionModel();
59
63
  if (previousModel !== currentModel) {
60
64
  this.container.setModel(currentModel);
61
65
  }
62
- super.updateProps(oldProps ? filterProps(oldProps, PROP_NAMES) : null, filterProps(newProps, PROP_NAMES));
66
+ super.updateProps(filterProps(oldProps ?? {}, PROP_NAMES), filterProps(newProps, PROP_NAMES));
63
67
  }
64
68
  }
65
69
  registerNodeClass(ListViewNode);
@@ -1,3 +1,4 @@
1
+ import { batch } from "@gtkx/ffi";
1
2
  import * as Adw from "@gtkx/ffi/adw";
2
3
  import { registerNodeClass } from "../registry.js";
3
4
  import { SlotNode } from "./slot.js";
@@ -12,36 +13,44 @@ export class NavigationPageNode extends SlotNode {
12
13
  if (!(child instanceof Adw.NavigationPage)) {
13
14
  return;
14
15
  }
15
- if (newProps.id !== undefined && (!oldProps || oldProps.id !== newProps.id)) {
16
- child.setTag(newProps.id);
16
+ if (!oldProps || oldProps.id !== newProps.id) {
17
+ if (newProps.id !== undefined) {
18
+ child.setTag(newProps.id);
19
+ }
17
20
  }
18
- if (newProps.title !== undefined && (!oldProps || oldProps.title !== newProps.title)) {
19
- child.setTitle(newProps.title);
21
+ if (!oldProps || oldProps.title !== newProps.title) {
22
+ if (newProps.title !== undefined) {
23
+ child.setTitle(newProps.title);
24
+ }
20
25
  }
21
- if (newProps.canPop !== undefined && (!oldProps || oldProps.canPop !== newProps.canPop)) {
22
- child.setCanPop(newProps.canPop);
26
+ if (!oldProps || oldProps.canPop !== newProps.canPop) {
27
+ if (newProps.canPop !== undefined) {
28
+ child.setCanPop(newProps.canPop);
29
+ }
23
30
  }
24
31
  }
25
32
  onChildChange(oldChild) {
26
- const navigationView = this.getParent();
27
- const title = this.props.title ?? "";
28
- if (this.child) {
29
- this.child = this.props.id
30
- ? Adw.NavigationPage.newWithTag(this.child, title, this.props.id)
31
- : new Adw.NavigationPage(this.child, title);
32
- this.updateProps(null, this.props);
33
- }
34
- if (navigationView instanceof Adw.NavigationView) {
35
- if (oldChild instanceof Adw.NavigationPage) {
36
- navigationView.remove(oldChild);
37
- }
33
+ batch(() => {
34
+ const navigationView = this.getParent();
35
+ const title = this.props.title ?? "";
38
36
  if (this.child) {
39
- navigationView.add(this.child);
37
+ this.child = this.props.id
38
+ ? Adw.NavigationPage.newWithTag(this.child, title, this.props.id)
39
+ : new Adw.NavigationPage(this.child, title);
40
+ this.updateProps(null, this.props);
40
41
  }
41
- }
42
- else {
43
- super.onChildChange(oldChild);
44
- }
42
+ if (navigationView instanceof Adw.NavigationView) {
43
+ if (oldChild instanceof Adw.NavigationPage) {
44
+ navigationView.remove(oldChild);
45
+ }
46
+ if (this.child) {
47
+ navigationView.add(this.child);
48
+ }
49
+ }
50
+ else {
51
+ super.onChildChange(oldChild);
52
+ }
53
+ });
45
54
  }
46
55
  }
47
56
  registerNodeClass(NavigationPageNode);
@@ -8,6 +8,7 @@ export declare class NotebookPageTabNode extends SlotNode<Props> {
8
8
  private page?;
9
9
  static matches(type: string): boolean;
10
10
  setPage(notebook?: Gtk.Notebook, page?: Gtk.Widget): void;
11
+ updateProps(oldProps: Props | null, newProps: Props): void;
11
12
  private getNotebook;
12
13
  private getPage;
13
14
  protected onChildChange(_oldChild: Gtk.Widget | null): void;
@@ -1,3 +1,4 @@
1
+ import { batch } from "@gtkx/ffi";
1
2
  import { registerNodeClass } from "../registry.js";
2
3
  import { SlotNode } from "./slot.js";
3
4
  export class NotebookPageTabNode extends SlotNode {
@@ -12,6 +13,9 @@ export class NotebookPageTabNode extends SlotNode {
12
13
  this.page = page;
13
14
  this.setParent(notebook);
14
15
  }
16
+ updateProps(oldProps, newProps) {
17
+ super.updateProps(oldProps, newProps);
18
+ }
15
19
  getNotebook() {
16
20
  if (!this.notebook) {
17
21
  throw new Error("Expected Notebook reference to be set on NotebookPageTabNode");
@@ -25,15 +29,17 @@ export class NotebookPageTabNode extends SlotNode {
25
29
  return this.page;
26
30
  }
27
31
  onChildChange(_oldChild) {
28
- if (!this.notebook || !this.page) {
29
- return;
30
- }
31
- const notebook = this.getNotebook();
32
- const page = this.getPage();
33
- if (notebook.pageNum(page) === -1) {
34
- return;
35
- }
36
- notebook.setTabLabel(page, this.child);
32
+ batch(() => {
33
+ if (!this.notebook || !this.page) {
34
+ return;
35
+ }
36
+ const notebook = this.getNotebook();
37
+ const page = this.getPage();
38
+ if (notebook.pageNum(page) === -1) {
39
+ return;
40
+ }
41
+ notebook.setTabLabel(page, this.child);
42
+ });
37
43
  }
38
44
  }
39
45
  registerNodeClass(NotebookPageTabNode);
@@ -1,3 +1,4 @@
1
+ import { batch } from "@gtkx/ffi";
1
2
  import * as Gtk from "@gtkx/ffi/gtk";
2
3
  import { registerNodeClass } from "../registry.js";
3
4
  import { scheduleAfterCommit } from "../scheduler.js";
@@ -93,12 +94,14 @@ export class NotebookPageNode extends SlotNode {
93
94
  notebook.removePage(pageNum);
94
95
  }
95
96
  onChildChange(oldChild) {
96
- if (oldChild) {
97
- this.detachPage(oldChild);
98
- }
99
- if (this.child) {
100
- this.attachPage();
101
- }
97
+ batch(() => {
98
+ if (oldChild) {
99
+ this.detachPage(oldChild);
100
+ }
101
+ if (this.child) {
102
+ this.attachPage();
103
+ }
104
+ });
102
105
  }
103
106
  }
104
107
  registerNodeClass(NotebookPageNode);
@@ -3,7 +3,6 @@ import { registerNodeClass } from "../registry.js";
3
3
  import { CommitPriority, scheduleAfterCommit } from "../scheduler.js";
4
4
  import { isContainerType } from "./internal/utils.js";
5
5
  import { ScaleMarkNode } from "./scale-mark.js";
6
- import { SlotNode } from "./slot.js";
7
6
  import { WidgetNode } from "./widget.js";
8
7
  class ScaleNode extends WidgetNode {
9
8
  static priority = 1;
@@ -18,11 +17,7 @@ class ScaleNode extends WidgetNode {
18
17
  scheduleAfterCommit(() => child.addMark());
19
18
  return;
20
19
  }
21
- if (child instanceof SlotNode || child instanceof WidgetNode) {
22
- super.appendChild(child);
23
- return;
24
- }
25
- throw new Error(`Cannot append '${child.typeName}' to 'Scale': expected x.ScaleMark or Widget`);
20
+ super.appendChild(child);
26
21
  }
27
22
  insertBefore(child, before) {
28
23
  if (child instanceof ScaleMarkNode) {
@@ -37,11 +32,7 @@ class ScaleNode extends WidgetNode {
37
32
  this.scheduleRebuildAllMarks();
38
33
  return;
39
34
  }
40
- if (child instanceof SlotNode || child instanceof WidgetNode) {
41
- super.insertBefore(child, before);
42
- return;
43
- }
44
- throw new Error(`Cannot insert '${child.typeName}' into 'Scale': expected x.ScaleMark or Widget`);
35
+ super.insertBefore(child, before);
45
36
  }
46
37
  removeChild(child) {
47
38
  if (child instanceof ScaleMarkNode) {
@@ -52,11 +43,7 @@ class ScaleNode extends WidgetNode {
52
43
  this.scheduleRebuildAllMarks(CommitPriority.HIGH);
53
44
  return;
54
45
  }
55
- if (child instanceof SlotNode || child instanceof WidgetNode) {
56
- super.removeChild(child);
57
- return;
58
- }
59
- throw new Error(`Cannot remove '${child.typeName}' from 'Scale': expected x.ScaleMark or Widget`);
46
+ super.removeChild(child);
60
47
  }
61
48
  scheduleRebuildAllMarks(priority = CommitPriority.NORMAL) {
62
49
  scheduleAfterCommit(() => {
@@ -9,8 +9,9 @@ class ScrolledWindowNode extends WidgetNode {
9
9
  return isContainerType(Gtk.ScrolledWindow, containerOrClass);
10
10
  }
11
11
  updateProps(oldProps, newProps) {
12
- if (oldProps?.hscrollbarPolicy !== newProps.hscrollbarPolicy ||
13
- oldProps?.vscrollbarPolicy !== newProps.vscrollbarPolicy) {
12
+ if (!oldProps ||
13
+ oldProps.hscrollbarPolicy !== newProps.hscrollbarPolicy ||
14
+ oldProps.vscrollbarPolicy !== newProps.vscrollbarPolicy) {
14
15
  const hPolicy = newProps.hscrollbarPolicy ?? Gtk.PolicyType.AUTOMATIC;
15
16
  const vPolicy = newProps.vscrollbarPolicy ?? Gtk.PolicyType.AUTOMATIC;
16
17
  this.container.setPolicy(hPolicy, vPolicy);
@@ -0,0 +1,37 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import type { Node } from "../node.js";
3
+ import type { Props } from "../types.js";
4
+ import { VirtualNode } from "./virtual.js";
5
+ /**
6
+ * Props for the ShortcutController virtual element.
7
+ *
8
+ * Attaches keyboard shortcuts to a widget. Must contain `x.Shortcut` children.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <GtkBox>
13
+ * <x.ShortcutController scope={Gtk.ShortcutScope.GLOBAL}>
14
+ * <x.Shortcut trigger="<Control>f" onActivate={toggleSearch} />
15
+ * <x.Shortcut trigger="<Control>q" onActivate={quit} />
16
+ * </x.ShortcutController>
17
+ * </GtkBox>
18
+ * ```
19
+ */
20
+ export interface ShortcutControllerProps extends Props {
21
+ /** The scope for shortcuts (LOCAL, MANAGED, or GLOBAL) */
22
+ scope?: Gtk.ShortcutScope;
23
+ }
24
+ export declare class ShortcutControllerNode extends VirtualNode<ShortcutControllerProps> {
25
+ static priority: number;
26
+ static matches(type: string): boolean;
27
+ private controller?;
28
+ private parentWidget?;
29
+ private shortcuts;
30
+ setParent(widget?: Gtk.Widget): void;
31
+ appendChild(child: Node): void;
32
+ removeChild(child: Node): void;
33
+ updateProps(oldProps: ShortcutControllerProps | null, newProps: ShortcutControllerProps): void;
34
+ unmount(): void;
35
+ private applyScope;
36
+ private addShortcutToController;
37
+ }
@@ -0,0 +1,74 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import { registerNodeClass } from "../registry.js";
3
+ import { ShortcutNode } from "./shortcut.js";
4
+ import { VirtualNode } from "./virtual.js";
5
+ export class ShortcutControllerNode extends VirtualNode {
6
+ static priority = 1;
7
+ static matches(type) {
8
+ return type === "ShortcutController";
9
+ }
10
+ controller;
11
+ parentWidget;
12
+ shortcuts = [];
13
+ setParent(widget) {
14
+ if (this.parentWidget && this.controller) {
15
+ this.parentWidget.removeController(this.controller);
16
+ }
17
+ this.parentWidget = widget;
18
+ if (widget) {
19
+ this.controller = new Gtk.ShortcutController();
20
+ this.applyScope();
21
+ widget.addController(this.controller);
22
+ for (const shortcut of this.shortcuts) {
23
+ this.addShortcutToController(shortcut);
24
+ }
25
+ }
26
+ else {
27
+ this.controller = undefined;
28
+ }
29
+ }
30
+ appendChild(child) {
31
+ if (!(child instanceof ShortcutNode)) {
32
+ throw new Error(`ShortcutController only accepts Shortcut children, got '${child.typeName}'`);
33
+ }
34
+ this.shortcuts.push(child);
35
+ if (this.controller) {
36
+ this.addShortcutToController(child);
37
+ }
38
+ }
39
+ removeChild(child) {
40
+ if (!(child instanceof ShortcutNode))
41
+ return;
42
+ const index = this.shortcuts.indexOf(child);
43
+ if (index !== -1) {
44
+ this.shortcuts.splice(index, 1);
45
+ if (this.controller && child.shortcut) {
46
+ this.controller.removeShortcut(child.shortcut);
47
+ }
48
+ }
49
+ }
50
+ updateProps(oldProps, newProps) {
51
+ super.updateProps(oldProps, newProps);
52
+ if (!oldProps || oldProps.scope !== newProps.scope) {
53
+ this.applyScope();
54
+ }
55
+ }
56
+ unmount() {
57
+ this.setParent(undefined);
58
+ super.unmount();
59
+ }
60
+ applyScope() {
61
+ if (!this.controller)
62
+ return;
63
+ this.controller.setScope(this.props.scope ?? Gtk.ShortcutScope.LOCAL);
64
+ }
65
+ addShortcutToController(node) {
66
+ if (!this.controller)
67
+ return;
68
+ node.createShortcut();
69
+ if (node.shortcut) {
70
+ this.controller.addShortcut(node.shortcut);
71
+ }
72
+ }
73
+ }
74
+ registerNodeClass(ShortcutControllerNode);
@@ -0,0 +1,38 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import type { Props } from "../types.js";
3
+ import { VirtualNode } from "./virtual.js";
4
+ /**
5
+ * Props for the Shortcut virtual element.
6
+ *
7
+ * Defines a keyboard shortcut. Must be a child of `x.ShortcutController`.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * <x.ShortcutController>
12
+ * <x.Shortcut trigger="<Control>s" onActivate={save} />
13
+ * <x.Shortcut trigger={["F5", "<Control>r"]} onActivate={refresh} />
14
+ * <x.Shortcut trigger="Escape" onActivate={cancel} disabled={!canCancel} />
15
+ * </x.ShortcutController>
16
+ * ```
17
+ */
18
+ export interface ShortcutProps extends Props {
19
+ /** The trigger string(s) using GTK accelerator format (e.g., "\<Control\>s", "F1") */
20
+ trigger: string | string[];
21
+ /**
22
+ * Called when the shortcut is activated.
23
+ * Return false to indicate the shortcut was not handled; otherwise it is considered handled.
24
+ */
25
+ onActivate: () => boolean | void;
26
+ /** Whether the shortcut is disabled */
27
+ disabled?: boolean;
28
+ }
29
+ export declare class ShortcutNode extends VirtualNode<ShortcutProps> {
30
+ static priority: number;
31
+ static matches(type: string): boolean;
32
+ shortcut?: Gtk.Shortcut;
33
+ private action?;
34
+ createShortcut(): void;
35
+ updateProps(oldProps: ShortcutProps | null, newProps: ShortcutProps): void;
36
+ unmount(): void;
37
+ private createTrigger;
38
+ }
@@ -0,0 +1,46 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import { registerNodeClass } from "../registry.js";
3
+ import { VirtualNode } from "./virtual.js";
4
+ export class ShortcutNode extends VirtualNode {
5
+ static priority = 1;
6
+ static matches(type) {
7
+ return type === "Shortcut";
8
+ }
9
+ shortcut;
10
+ action;
11
+ createShortcut() {
12
+ const trigger = this.createTrigger();
13
+ this.action = new Gtk.CallbackAction(() => {
14
+ const result = this.props.onActivate();
15
+ return result !== false;
16
+ });
17
+ this.shortcut = new Gtk.Shortcut(trigger, this.action);
18
+ }
19
+ updateProps(oldProps, newProps) {
20
+ super.updateProps(oldProps, newProps);
21
+ if (this.shortcut && (oldProps?.trigger !== newProps.trigger || oldProps?.disabled !== newProps.disabled)) {
22
+ this.shortcut.setTrigger(this.createTrigger());
23
+ }
24
+ }
25
+ unmount() {
26
+ this.shortcut = undefined;
27
+ this.action = undefined;
28
+ super.unmount();
29
+ }
30
+ createTrigger() {
31
+ if (this.props.disabled) {
32
+ return Gtk.NeverTrigger.get();
33
+ }
34
+ const { trigger } = this.props;
35
+ const triggers = Array.isArray(trigger) ? trigger : [trigger];
36
+ if (triggers.length === 0) {
37
+ return Gtk.NeverTrigger.get();
38
+ }
39
+ let result = new Gtk.ShortcutTrigger(triggers[0]);
40
+ for (let i = 1; i < triggers.length; i++) {
41
+ result = new Gtk.AlternativeTrigger(result, new Gtk.ShortcutTrigger(triggers[i]));
42
+ }
43
+ return result;
44
+ }
45
+ }
46
+ registerNodeClass(ShortcutNode);
@@ -0,0 +1,73 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
+ import * as GtkSource from "@gtkx/ffi/gtksource";
3
+ import { VirtualNode } from "./virtual.js";
4
+ /**
5
+ * Props for the SourceBuffer virtual element.
6
+ *
7
+ * Used to declaratively configure the text buffer for a GtkSourceView with syntax highlighting,
8
+ * bracket matching, and other source code editing features.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <GtkSourceView>
13
+ * <x.SourceBuffer
14
+ * text={sourceCode}
15
+ * language="typescript"
16
+ * styleScheme="Adwaita-dark"
17
+ * highlightSyntax
18
+ * highlightMatchingBrackets
19
+ * onTextChanged={(text) => console.log("Code:", text)}
20
+ * />
21
+ * </GtkSourceView>
22
+ * ```
23
+ */
24
+ export type SourceBufferProps = {
25
+ /** Text content */
26
+ text?: string;
27
+ /** Whether to enable undo/redo */
28
+ enableUndo?: boolean;
29
+ /** Callback when the text content changes */
30
+ onTextChanged?: (text: string) => void;
31
+ /** Callback when can-undo state changes */
32
+ onCanUndoChanged?: (canUndo: boolean) => void;
33
+ /** Callback when can-redo state changes */
34
+ onCanRedoChanged?: (canRedo: boolean) => void;
35
+ /**
36
+ * Language for syntax highlighting.
37
+ * Can be a language ID string (e.g., "typescript", "python", "rust") or a GtkSource.Language object.
38
+ */
39
+ language?: string | GtkSource.Language;
40
+ /**
41
+ * Style scheme for syntax highlighting colors.
42
+ * Can be a scheme ID string (e.g., "Adwaita-dark", "classic") or a GtkSource.StyleScheme object.
43
+ */
44
+ styleScheme?: string | GtkSource.StyleScheme;
45
+ /** Whether to enable syntax highlighting. Defaults to true when language is set. */
46
+ highlightSyntax?: boolean;
47
+ /** Whether to highlight matching brackets when cursor is on a bracket. Defaults to true. */
48
+ highlightMatchingBrackets?: boolean;
49
+ /**
50
+ * Whether the buffer has an implicit trailing newline.
51
+ * When true (default), trailing newlines are handled automatically during file load/save.
52
+ */
53
+ implicitTrailingNewline?: boolean;
54
+ /** Callback when the cursor position changes */
55
+ onCursorMoved?: () => void;
56
+ /** Callback when syntax highlighting is updated for a region */
57
+ onHighlightUpdated?: (start: Gtk.TextIter, end: Gtk.TextIter) => void;
58
+ };
59
+ export declare class SourceBufferNode extends VirtualNode<SourceBufferProps> {
60
+ static priority: number;
61
+ private sourceView?;
62
+ private buffer?;
63
+ static matches(type: string): boolean;
64
+ setSourceView(sourceView: GtkSource.View): void;
65
+ private resolveLanguage;
66
+ private resolveStyleScheme;
67
+ private setupBuffer;
68
+ private applySourceProps;
69
+ private getBufferText;
70
+ private updateSignalHandlers;
71
+ updateProps(oldProps: SourceBufferProps | null, newProps: SourceBufferProps): void;
72
+ unmount(): void;
73
+ }