@gtkx/react 0.1.48 → 0.1.49

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 (42) hide show
  1. package/dist/codegen/jsx-generator.js +4 -21
  2. package/dist/container-interfaces.d.ts +51 -0
  3. package/dist/container-interfaces.js +5 -0
  4. package/dist/factory.d.ts +1 -1
  5. package/dist/factory.js +17 -3
  6. package/dist/generated/jsx.d.ts +5 -11
  7. package/dist/node.d.ts +4 -4
  8. package/dist/node.js +7 -6
  9. package/dist/nodes/about-dialog.d.ts +9 -0
  10. package/dist/nodes/about-dialog.js +14 -0
  11. package/dist/nodes/action-bar.d.ts +9 -0
  12. package/dist/nodes/action-bar.js +15 -0
  13. package/dist/nodes/column-view.d.ts +5 -4
  14. package/dist/nodes/column-view.js +28 -29
  15. package/dist/nodes/dropdown.d.ts +7 -17
  16. package/dist/nodes/dropdown.js +17 -10
  17. package/dist/nodes/flow-box.d.ts +9 -0
  18. package/dist/nodes/flow-box.js +25 -0
  19. package/dist/nodes/grid.d.ts +6 -3
  20. package/dist/nodes/grid.js +28 -26
  21. package/dist/nodes/list-box.d.ts +9 -0
  22. package/dist/nodes/list-box.js +21 -0
  23. package/dist/nodes/list.d.ts +4 -3
  24. package/dist/nodes/list.js +8 -7
  25. package/dist/nodes/notebook.d.ts +7 -3
  26. package/dist/nodes/notebook.js +31 -14
  27. package/dist/nodes/overlay.d.ts +2 -1
  28. package/dist/nodes/root.d.ts +2 -3
  29. package/dist/nodes/root.js +3 -3
  30. package/dist/nodes/slot.d.ts +1 -2
  31. package/dist/nodes/slot.js +2 -2
  32. package/dist/nodes/text-view.d.ts +2 -7
  33. package/dist/nodes/text-view.js +10 -49
  34. package/dist/nodes/widget.d.ts +6 -5
  35. package/dist/nodes/widget.js +9 -149
  36. package/dist/nodes/window.d.ts +11 -0
  37. package/dist/nodes/window.js +37 -0
  38. package/dist/props.d.ts +5 -0
  39. package/dist/props.js +10 -0
  40. package/dist/reconciler.js +4 -5
  41. package/dist/types.d.ts +4 -4
  42. package/package.json +3 -3
@@ -5,7 +5,6 @@ const COLUMN_VIEW_WIDGET = "ColumnView";
5
5
  const DROPDOWN_WIDGETS = new Set(["DropDown"]);
6
6
  const GRID_WIDGETS = new Set(["Grid"]);
7
7
  const NOTEBOOK_WIDGET = "Notebook";
8
- const TEXT_VIEW_WIDGET = "TextView";
9
8
  const INTERNALLY_PROVIDED_PARAMS = {
10
9
  ApplicationWindow: new Set(["application"]),
11
10
  };
@@ -34,7 +33,6 @@ const isColumnViewWidget = (widgetName) => widgetName === COLUMN_VIEW_WIDGET;
34
33
  const isDropDownWidget = (widgetName) => DROPDOWN_WIDGETS.has(widgetName);
35
34
  const isGridWidget = (widgetName) => GRID_WIDGETS.has(widgetName);
36
35
  const isNotebookWidget = (widgetName) => widgetName === NOTEBOOK_WIDGET;
37
- const isTextViewWidget = (widgetName) => widgetName === TEXT_VIEW_WIDGET;
38
36
  const sanitizeDoc = (doc) => {
39
37
  let result = doc;
40
38
  result = result.replace(/<picture>[\s\S]*?<\/picture>/gi, "");
@@ -336,24 +334,14 @@ ${widgetPropsContent}
336
334
  lines.push(`\t * Render function for list items.`);
337
335
  lines.push(`\t * Called with null during setup (for loading state) and with the actual item during bind.`);
338
336
  lines.push(`\t */`);
339
- lines.push(`\t// biome-ignore lint/suspicious/noExplicitAny: Internal type, use generic ListView<T> export`);
340
- lines.push(`\trenderItem: (item: any) => import("react").ReactElement;`);
337
+ lines.push(`\trenderItem: (item: unknown) => import("react").ReactElement;`);
341
338
  }
342
339
  if (isDropDownWidget(widget.name)) {
343
340
  lines.push("");
344
341
  lines.push(`\t/** Function to convert item to display label */`);
345
- lines.push(`\t// biome-ignore lint/suspicious/noExplicitAny: Internal type, use generic DropDown<T> export`);
346
- lines.push(`\titemLabel?: (item: any) => string;`);
342
+ lines.push(`\titemLabel?: (item: unknown) => string;`);
347
343
  lines.push(`\t/** Called when selection changes */`);
348
- lines.push(`\t// biome-ignore lint/suspicious/noExplicitAny: Internal type, use generic DropDown<T> export`);
349
- lines.push(`\tonSelectionChanged?: (item: any, index: number) => void;`);
350
- }
351
- if (isTextViewWidget(widget.name)) {
352
- lines.push("");
353
- lines.push(`\t/** The contents of the text buffer. */`);
354
- lines.push(`\ttext?: string;`);
355
- lines.push(`\t/** Called when the text buffer content changes */`);
356
- lines.push(`\tonChanged?: (text: string) => void;`);
344
+ lines.push(`\tonSelectionChanged?: (item: unknown, index: number) => void;`);
357
345
  }
358
346
  lines.push("");
359
347
  lines.push(`\tref?: Ref<Gtk.${widgetName}>;`);
@@ -664,12 +652,7 @@ ${widgetPropsContent}
664
652
  lines.push(`}`);
665
653
  }
666
654
  else if (isColumnViewWidget(widgetName)) {
667
- // Root props type - extends with ColumnViewRootProps for sorting support
668
- // Uses generics: T for item type, C for column ID union type
669
- lines.push(`interface ${name}RootPropsExtended<T = unknown, C extends string = string> extends ${name}Props, ColumnViewRootProps<C> {`);
670
- lines.push(`\t/** Comparison function for sorting items by column. Takes item a, item b, and column id. */`);
671
- lines.push(`\tsortFn?: (a: T, b: T, columnId: C) => number;`);
672
- lines.push(`}`);
655
+ lines.push(`interface ${name}RootPropsExtended<T = unknown, C extends string = string> extends ${name}Props, ColumnViewRootProps<T, C> {}`);
673
656
  lines.push(``);
674
657
  // Root wrapper (generic)
675
658
  lines.push(`function ${name}Root<T = unknown, C extends string = string>(props: ${name}RootPropsExtended<T, C>): import("react").ReactElement {`);
@@ -0,0 +1,51 @@
1
+ import type * as Gtk from "@gtkx/ffi/gtk";
2
+ import type { Node } from "./node.js";
3
+ /**
4
+ * Interface for containers that manage child widgets with attach/detach semantics.
5
+ * Used by ActionBar, FlowBox, ListBox, Overlay.
6
+ */
7
+ export interface ChildContainer {
8
+ attachChild(child: Gtk.Widget): void;
9
+ insertChildBefore(child: Gtk.Widget, before: Gtk.Widget): void;
10
+ detachChild(child: Gtk.Widget): void;
11
+ }
12
+ /**
13
+ * Interface for page-based containers like Notebook.
14
+ */
15
+ export interface PageContainer {
16
+ addPage(child: Gtk.Widget, label: string): void;
17
+ insertPageBefore(child: Gtk.Widget, label: string, beforeChild: Gtk.Widget): void;
18
+ removePage(child: Gtk.Widget): void;
19
+ updatePageLabel(child: Gtk.Widget, label: string): void;
20
+ }
21
+ /**
22
+ * Interface for grid-based containers.
23
+ */
24
+ export interface GridContainer {
25
+ attachToGrid(child: Gtk.Widget, column: number, row: number, colSpan: number, rowSpan: number): void;
26
+ removeFromGrid(child: Gtk.Widget): void;
27
+ }
28
+ /**
29
+ * Interface for item-based containers like ListView, ColumnView, DropDown.
30
+ */
31
+ export interface ItemContainer<T> {
32
+ addItem(item: T): void;
33
+ insertItemBefore(item: T, beforeItem: T): void;
34
+ removeItem(item: T): void;
35
+ }
36
+ /**
37
+ * Interface for column-based containers like ColumnView.
38
+ * Note: Column type is generic to support both raw Gtk.ColumnViewColumn and wrapper nodes.
39
+ */
40
+ export interface ColumnContainer {
41
+ addColumn(column: unknown): void;
42
+ insertColumnBefore(column: unknown, beforeColumn: unknown): void;
43
+ removeColumn(column: unknown): void;
44
+ getItems(): unknown[];
45
+ getSortFn(): ((a: unknown, b: unknown, columnId: string) => number) | null;
46
+ }
47
+ export declare const isChildContainer: (node: Node) => node is Node & ChildContainer;
48
+ export declare const isPageContainer: (node: Node) => node is Node & PageContainer;
49
+ export declare const isGridContainer: (node: Node) => node is Node & GridContainer;
50
+ export declare const isItemContainer: <T>(node: Node) => node is Node & ItemContainer<T>;
51
+ export declare const isColumnContainer: (node: Node) => node is Node & ColumnContainer;
@@ -0,0 +1,5 @@
1
+ export const isChildContainer = (node) => "attachChild" in node && "detachChild" in node && "insertChildBefore" in node;
2
+ export const isPageContainer = (node) => "addPage" in node && "removePage" in node && "insertPageBefore" in node && "updatePageLabel" in node;
3
+ export const isGridContainer = (node) => "attachToGrid" in node && "removeFromGrid" in node;
4
+ export const isItemContainer = (node) => "addItem" in node && "insertItemBefore" in node && "removeItem" in node;
5
+ export const isColumnContainer = (node) => "addColumn" in node && "removeColumn" in node && "getSortFn" in node;
package/dist/factory.d.ts CHANGED
@@ -3,4 +3,4 @@ import type { Node } from "./node.js";
3
3
  import { type ROOT_NODE_CONTAINER } from "./nodes/root.js";
4
4
  export type Props = Record<string, unknown>;
5
5
  export { ROOT_NODE_CONTAINER } from "./nodes/root.js";
6
- export declare const createNode: (type: string, props: Props, app: Gtk.Application, existingWidget?: Gtk.Widget | typeof ROOT_NODE_CONTAINER) => Node;
6
+ export declare const createNode: (type: string, props: Props, existingWidget?: Gtk.Widget | typeof ROOT_NODE_CONTAINER) => Node;
package/dist/factory.js CHANGED
@@ -1,16 +1,22 @@
1
+ import { AboutDialogNode } from "./nodes/about-dialog.js";
2
+ import { ActionBarNode } from "./nodes/action-bar.js";
1
3
  import { ColumnViewColumnNode, ColumnViewItemNode, ColumnViewNode } from "./nodes/column-view.js";
2
4
  import { DropDownItemNode, DropDownNode } from "./nodes/dropdown.js";
5
+ import { FlowBoxNode } from "./nodes/flow-box.js";
3
6
  import { GridChildNode, GridNode } from "./nodes/grid.js";
4
7
  import { ListItemNode, ListViewNode } from "./nodes/list.js";
8
+ import { ListBoxNode } from "./nodes/list-box.js";
5
9
  import { NotebookNode, NotebookPageNode } from "./nodes/notebook.js";
6
10
  import { OverlayNode } from "./nodes/overlay.js";
7
11
  import { RootNode } from "./nodes/root.js";
8
12
  import { SlotNode } from "./nodes/slot.js";
9
13
  import { TextViewNode } from "./nodes/text-view.js";
10
14
  import { WidgetNode } from "./nodes/widget.js";
15
+ import { WindowNode } from "./nodes/window.js";
11
16
  export { ROOT_NODE_CONTAINER } from "./nodes/root.js";
12
17
  const NODE_CLASSES = [
13
18
  RootNode,
19
+ // Virtual nodes (no widget)
14
20
  ColumnViewColumnNode,
15
21
  ColumnViewItemNode,
16
22
  ListItemNode,
@@ -18,19 +24,27 @@ const NODE_CLASSES = [
18
24
  GridChildNode,
19
25
  NotebookPageNode,
20
26
  SlotNode,
27
+ // Specialized widget nodes
28
+ WindowNode,
29
+ AboutDialogNode,
21
30
  TextViewNode,
31
+ // Container nodes
32
+ ActionBarNode,
33
+ FlowBoxNode,
34
+ ListBoxNode,
22
35
  DropDownNode,
23
36
  GridNode,
24
37
  OverlayNode,
25
38
  ColumnViewNode,
26
39
  ListViewNode,
27
40
  NotebookNode,
41
+ // Catch-all (must be last)
28
42
  WidgetNode,
29
43
  ];
30
- export const createNode = (type, props, app, existingWidget) => {
44
+ export const createNode = (type, props, existingWidget) => {
31
45
  for (const NodeClass of NODE_CLASSES) {
32
- if (NodeClass.matches(type, props, existingWidget)) {
33
- return new NodeClass(type, props, app, existingWidget);
46
+ if (NodeClass.matches(type, existingWidget)) {
47
+ return new NodeClass(type, props, existingWidget);
34
48
  }
35
49
  }
36
50
  throw new Error(`No matching node class for type: ${type}`);
@@ -1861,9 +1861,9 @@ export interface DropDownProps extends WidgetProps {
1861
1861
  */
1862
1862
  onActivate?: (self: Gtk.DropDown) => void;
1863
1863
  /** Function to convert item to display label */
1864
- itemLabel?: (item: any) => string;
1864
+ itemLabel?: (item: unknown) => string;
1865
1865
  /** Called when selection changes */
1866
- onSelectionChanged?: (item: any, index: number) => void;
1866
+ onSelectionChanged?: (item: unknown, index: number) => void;
1867
1867
  ref?: Ref<Gtk.DropDown>;
1868
1868
  }
1869
1869
  /** Props for the {@link EditableLabel} widget. */
@@ -2917,7 +2917,7 @@ export interface GridViewProps extends ListBaseProps {
2917
2917
  * Render function for list items.
2918
2918
  * Called with null during setup (for loading state) and with the actual item during bind.
2919
2919
  */
2920
- renderItem: (item: any) => import("react").ReactElement;
2920
+ renderItem: (item: unknown) => import("react").ReactElement;
2921
2921
  ref?: Ref<Gtk.GridView>;
2922
2922
  }
2923
2923
  /** Props for the {@link HeaderBar} widget. */
@@ -3735,7 +3735,7 @@ export interface ListViewProps extends ListBaseProps {
3735
3735
  * Render function for list items.
3736
3736
  * Called with null during setup (for loading state) and with the actual item during bind.
3737
3737
  */
3738
- renderItem: (item: any) => import("react").ReactElement;
3738
+ renderItem: (item: unknown) => import("react").ReactElement;
3739
3739
  ref?: Ref<Gtk.ListView>;
3740
3740
  }
3741
3741
  /** Props for the {@link LockButton} widget. */
@@ -5684,10 +5684,6 @@ export interface TextViewProps extends WidgetProps {
5684
5684
  * The default binding for this signal is `Insert`.
5685
5685
  */
5686
5686
  onToggleOverwrite?: (self: Gtk.TextView) => void;
5687
- /** The contents of the text buffer. */
5688
- text?: string;
5689
- /** Called when the text buffer content changes */
5690
- onChanged?: (text: string) => void;
5691
5687
  ref?: Ref<Gtk.TextView>;
5692
5688
  }
5693
5689
  /** Props for the {@link ToggleButton} widget. */
@@ -7075,9 +7071,7 @@ export declare const ColorChooserWidget: "ColorChooserWidget";
7075
7071
  * it gets the .color style class.
7076
7072
  */
7077
7073
  export declare const ColorDialogButton: "ColorDialogButton";
7078
- interface ColumnViewRootPropsExtended<T = unknown, C extends string = string> extends ColumnViewProps, ColumnViewRootProps<C> {
7079
- /** Comparison function for sorting items by column. Takes item a, item b, and column id. */
7080
- sortFn?: (a: T, b: T, columnId: C) => number;
7074
+ interface ColumnViewRootPropsExtended<T = unknown, C extends string = string> extends ColumnViewProps, ColumnViewRootProps<T, C> {
7081
7075
  }
7082
7076
  declare function ColumnViewRoot<T = unknown, C extends string = string>(props: ColumnViewRootPropsExtended<T, C>): import("react").ReactElement;
7083
7077
  interface ColumnViewGenericColumnProps<T> extends Omit<ColumnViewColumnProps, "renderCell"> {
package/dist/node.d.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
2
  import type { Props, ROOT_NODE_CONTAINER } from "./factory.js";
3
3
  export declare abstract class Node<T extends Gtk.Widget | undefined = Gtk.Widget | undefined> {
4
- static matches(_type: string, _props: Props, _existingWidget?: Gtk.Widget | typeof ROOT_NODE_CONTAINER): boolean;
4
+ static matches(_type: string, _existingWidget?: Gtk.Widget | typeof ROOT_NODE_CONTAINER): boolean;
5
5
  protected signalHandlers: Map<string, number>;
6
6
  protected widget: T;
7
7
  protected widgetType: string;
8
8
  protected isVirtual(): boolean;
9
- constructor(type: string, props: Props, app: Gtk.Application, existingWidget?: Gtk.Widget);
10
- protected createWidget(type: string, props: Props, app: Gtk.Application): T;
9
+ constructor(type: string, props: Props, existingWidget?: Gtk.Widget);
10
+ protected createWidget(type: string, props: Props): T;
11
11
  getWidget(): T;
12
12
  appendChild(child: Node): void;
13
13
  removeChild(child: Node): void;
@@ -21,5 +21,5 @@ export declare abstract class Node<T extends Gtk.Widget | undefined = Gtk.Widget
21
21
  protected disconnectSignal(eventName: string): void;
22
22
  protected connectSignal(widget: Gtk.Widget, eventName: string, handler: (...args: unknown[]) => unknown): void;
23
23
  protected setProperty(widget: Gtk.Widget, key: string, value: unknown): void;
24
- mount(_app: Gtk.Application): void;
24
+ mount(): void;
25
25
  }
package/dist/node.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { getCurrentApp } from "@gtkx/ffi";
1
2
  import * as GObject from "@gtkx/ffi/gobject";
2
3
  import * as Gtk from "@gtkx/ffi/gtk";
3
4
  import { CONSTRUCTOR_PARAMS, PROP_SETTERS, SETTER_GETTERS } from "./generated/jsx.js";
@@ -9,7 +10,7 @@ const extractConstructorArgs = (type, props) => {
9
10
  return params.map((p) => props[p.name]);
10
11
  };
11
12
  export class Node {
12
- static matches(_type, _props, _existingWidget) {
13
+ static matches(_type, _existingWidget) {
13
14
  return false;
14
15
  }
15
16
  signalHandlers = new Map();
@@ -18,16 +19,16 @@ export class Node {
18
19
  isVirtual() {
19
20
  return false;
20
21
  }
21
- constructor(type, props, app, existingWidget) {
22
+ constructor(type, props, existingWidget) {
22
23
  this.widgetType = type.split(".")[0] || type;
23
24
  if (existingWidget) {
24
25
  this.widget = existingWidget;
25
26
  return;
26
27
  }
27
- this.widget = (this.isVirtual() ? undefined : this.createWidget(type, props, app));
28
+ this.widget = (this.isVirtual() ? undefined : this.createWidget(type, props));
28
29
  this.updateProps({}, props);
29
30
  }
30
- createWidget(type, props, app) {
31
+ createWidget(type, props) {
31
32
  const normalizedType = type.split(".")[0] || type;
32
33
  // biome-ignore lint/performance/noDynamicNamespaceImportAccess: dynamic widget creation
33
34
  const WidgetClass = Gtk[normalizedType];
@@ -35,7 +36,7 @@ export class Node {
35
36
  throw new Error(`Unknown GTK widget type: ${normalizedType}`);
36
37
  }
37
38
  if (WidgetClass === Gtk.ApplicationWindow) {
38
- return new WidgetClass(app);
39
+ return new WidgetClass(getCurrentApp());
39
40
  }
40
41
  return new WidgetClass(...extractConstructorArgs(normalizedType, props));
41
42
  }
@@ -164,5 +165,5 @@ export class Node {
164
165
  }
165
166
  setter.call(widget, value);
166
167
  }
167
- mount(_app) { }
168
+ mount() { }
168
169
  }
@@ -0,0 +1,9 @@
1
+ import type * as Gtk from "@gtkx/ffi/gtk";
2
+ import { Node } from "../node.js";
3
+ export declare class AboutDialogNode extends Node<Gtk.AboutDialog> {
4
+ static matches(type: string): boolean;
5
+ attachToParent(_parent: Node): void;
6
+ attachToParentBefore(_parent: Node, _before: Node): void;
7
+ detachFromParent(_parent: Node): void;
8
+ mount(): void;
9
+ }
@@ -0,0 +1,14 @@
1
+ import { Node } from "../node.js";
2
+ export class AboutDialogNode extends Node {
3
+ static matches(type) {
4
+ return type === "AboutDialog";
5
+ }
6
+ attachToParent(_parent) { }
7
+ attachToParentBefore(_parent, _before) { }
8
+ detachFromParent(_parent) {
9
+ this.widget.destroy();
10
+ }
11
+ mount() {
12
+ this.widget.present();
13
+ }
14
+ }
@@ -0,0 +1,9 @@
1
+ import type * as Gtk from "@gtkx/ffi/gtk";
2
+ import type { ChildContainer } from "../container-interfaces.js";
3
+ import { Node } from "../node.js";
4
+ export declare class ActionBarNode extends Node<Gtk.ActionBar> implements ChildContainer {
5
+ static matches(type: string): boolean;
6
+ attachChild(child: Gtk.Widget): void;
7
+ insertChildBefore(child: Gtk.Widget, _before: Gtk.Widget): void;
8
+ detachChild(child: Gtk.Widget): void;
9
+ }
@@ -0,0 +1,15 @@
1
+ import { Node } from "../node.js";
2
+ export class ActionBarNode extends Node {
3
+ static matches(type) {
4
+ return type === "ActionBar";
5
+ }
6
+ attachChild(child) {
7
+ this.widget.packStart(child);
8
+ }
9
+ insertChildBefore(child, _before) {
10
+ this.attachChild(child);
11
+ }
12
+ detachChild(child) {
13
+ this.widget.remove(child);
14
+ }
15
+ }
@@ -1,8 +1,9 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
+ import { type ColumnContainer, type ItemContainer } from "../container-interfaces.js";
2
3
  import type { Props } from "../factory.js";
3
4
  import { Node } from "../node.js";
4
5
  import type { ColumnSortFn } from "../types.js";
5
- export declare class ColumnViewNode extends Node<Gtk.ColumnView> {
6
+ export declare class ColumnViewNode extends Node<Gtk.ColumnView> implements ItemContainer<unknown>, ColumnContainer {
6
7
  static matches(type: string): boolean;
7
8
  private stringList;
8
9
  private selectionModel;
@@ -18,7 +19,7 @@ export declare class ColumnViewNode extends Node<Gtk.ColumnView> {
18
19
  private sorterChangedHandlerId;
19
20
  private lastNotifiedColumn;
20
21
  private lastNotifiedOrder;
21
- constructor(type: string, props: Props, app: Gtk.Application);
22
+ constructor(type: string, props: Props);
22
23
  private connectSorterChangedSignal;
23
24
  private waitForSortComplete;
24
25
  private disconnectSorterChangedSignal;
@@ -48,7 +49,7 @@ export declare class ColumnViewColumnNode extends Node {
48
49
  private listItemCache;
49
50
  private columnId;
50
51
  private sorter;
51
- constructor(type: string, props: Props, app: Gtk.Application);
52
+ constructor(type: string, props: Props);
52
53
  getColumn(): Gtk.ColumnViewColumn;
53
54
  getId(): string | null;
54
55
  setColumnView(columnView: ColumnViewNode | null): void;
@@ -63,7 +64,7 @@ export declare class ColumnViewItemNode extends Node {
63
64
  static matches(type: string): boolean;
64
65
  protected isVirtual(): boolean;
65
66
  private item;
66
- constructor(type: string, props: Props, app: Gtk.Application);
67
+ constructor(type: string, props: Props);
67
68
  getItem(): unknown;
68
69
  attachToParent(parent: Node): void;
69
70
  attachToParentBefore(parent: Node, before: Node): void;
@@ -2,6 +2,7 @@ import { getObject, getObjectId } from "@gtkx/ffi";
2
2
  import * as GObject from "@gtkx/ffi/gobject";
3
3
  import * as Gtk from "@gtkx/ffi/gtk";
4
4
  import { scheduleFlush } from "../batch.js";
5
+ import { isColumnContainer, isItemContainer, } from "../container-interfaces.js";
5
6
  import { createFiberRoot } from "../fiber-root.js";
6
7
  import { Node } from "../node.js";
7
8
  import { reconciler } from "../reconciler.js";
@@ -23,8 +24,8 @@ export class ColumnViewNode extends Node {
23
24
  sorterChangedHandlerId = null;
24
25
  lastNotifiedColumn = null;
25
26
  lastNotifiedOrder = Gtk.SortType.ASCENDING;
26
- constructor(type, props, app) {
27
- super(type, props, app);
27
+ constructor(type, props) {
28
+ super(type, props);
28
29
  this.stringList = new Gtk.StringList([]);
29
30
  this.sortListModel = new Gtk.SortListModel(this.stringList, this.widget.getSorter());
30
31
  this.sortListModel.setIncremental(true);
@@ -46,13 +47,12 @@ export class ColumnViewNode extends Node {
46
47
  });
47
48
  }
48
49
  waitForSortComplete(callback) {
49
- const pending = this.sortListModel.getPending();
50
- if (pending === 0) {
51
- callback();
50
+ const sortingInProgress = this.sortListModel.getPending() > 0;
51
+ if (sortingInProgress) {
52
+ setTimeout(() => this.waitForSortComplete(callback), 0);
52
53
  }
53
54
  else {
54
- // Sorting still in progress, check again after a short delay
55
- setTimeout(() => this.waitForSortComplete(callback), 10);
55
+ callback();
56
56
  }
57
57
  }
58
58
  disconnectSorterChangedSignal() {
@@ -74,8 +74,8 @@ export class ColumnViewNode extends Node {
74
74
  const column = sorter.getPrimarySortColumn();
75
75
  const order = sorter.getPrimarySortOrder();
76
76
  const columnId = column?.getId() ?? null;
77
- // Deduplicate: only notify if the sort state actually changed
78
- if (columnId === this.lastNotifiedColumn && order === this.lastNotifiedOrder) {
77
+ const sortStateUnchanged = columnId === this.lastNotifiedColumn && order === this.lastNotifiedOrder;
78
+ if (sortStateUnchanged) {
79
79
  return;
80
80
  }
81
81
  this.lastNotifiedColumn = columnId;
@@ -145,9 +145,8 @@ export class ColumnViewNode extends Node {
145
145
  const newLength = this.items.length;
146
146
  if (newLength === this.committedLength)
147
147
  return;
148
- // Store indices as strings so we can map back to items in the sorter
149
- const indices = Array.from({ length: newLength }, (_, i) => String(i));
150
- this.stringList.splice(0, this.committedLength, indices);
148
+ const itemIndicesForSorter = Array.from({ length: newLength }, (_, i) => String(i));
149
+ this.stringList.splice(0, this.committedLength, itemIndicesForSorter);
151
150
  this.committedLength = newLength;
152
151
  };
153
152
  addItem(item) {
@@ -189,11 +188,12 @@ export class ColumnViewNode extends Node {
189
188
  const hadCallback = this.onSortChange !== null;
190
189
  this.onSortChange = newOnSortChange;
191
190
  const hasCallback = this.onSortChange !== null;
192
- // Connect or disconnect the signal handler as needed
193
- if (!hadCallback && hasCallback) {
191
+ const callbackAdded = !hadCallback && hasCallback;
192
+ const callbackRemoved = hadCallback && !hasCallback;
193
+ if (callbackAdded) {
194
194
  this.connectSorterChangedSignal();
195
195
  }
196
- else if (hadCallback && !hasCallback) {
196
+ else if (callbackRemoved) {
197
197
  this.disconnectSorterChangedSignal();
198
198
  }
199
199
  }
@@ -226,8 +226,8 @@ export class ColumnViewColumnNode extends Node {
226
226
  listItemCache = new Map();
227
227
  columnId = null;
228
228
  sorter = null;
229
- constructor(type, props, app) {
230
- super(type, props, app);
229
+ constructor(type, props) {
230
+ super(type, props);
231
231
  this.factory = new Gtk.SignalListItemFactory();
232
232
  this.column = new Gtk.ColumnViewColumn(props.title, this.factory);
233
233
  this.renderCell = props.renderCell;
@@ -308,11 +308,10 @@ export class ColumnViewColumnNode extends Node {
308
308
  }
309
309
  const columnId = this.columnId;
310
310
  const columnView = this.columnView;
311
- const wrappedSortFn = (_a, _b) => {
311
+ const wrappedSortFn = (stringObjPtrA, stringObjPtrB) => {
312
312
  const items = columnView.getItems();
313
- // _a and _b are GtkStringObject pointers - get the string content (indices)
314
- const stringObjA = getObject(_a, Gtk.StringObject);
315
- const stringObjB = getObject(_b, Gtk.StringObject);
313
+ const stringObjA = getObject(stringObjPtrA, Gtk.StringObject);
314
+ const stringObjB = getObject(stringObjPtrB, Gtk.StringObject);
316
315
  const indexA = Number.parseInt(stringObjA.getString(), 10);
317
316
  const indexB = Number.parseInt(stringObjB.getString(), 10);
318
317
  if (Number.isNaN(indexA) || Number.isNaN(indexB))
@@ -328,12 +327,12 @@ export class ColumnViewColumnNode extends Node {
328
327
  this.column.setSorter(this.sorter);
329
328
  }
330
329
  attachToParent(parent) {
331
- if (parent instanceof ColumnViewNode) {
330
+ if (isColumnContainer(parent)) {
332
331
  parent.addColumn(this);
333
332
  }
334
333
  }
335
334
  attachToParentBefore(parent, before) {
336
- if (parent instanceof ColumnViewNode && before instanceof ColumnViewColumnNode) {
335
+ if (isColumnContainer(parent) && before instanceof ColumnViewColumnNode) {
337
336
  parent.insertColumnBefore(this, before);
338
337
  }
339
338
  else {
@@ -341,7 +340,7 @@ export class ColumnViewColumnNode extends Node {
341
340
  }
342
341
  }
343
342
  detachFromParent(parent) {
344
- if (parent instanceof ColumnViewNode) {
343
+ if (isColumnContainer(parent)) {
345
344
  parent.removeColumn(this);
346
345
  }
347
346
  }
@@ -387,20 +386,20 @@ export class ColumnViewItemNode extends Node {
387
386
  return true;
388
387
  }
389
388
  item;
390
- constructor(type, props, app) {
391
- super(type, props, app);
389
+ constructor(type, props) {
390
+ super(type, props);
392
391
  this.item = props.item;
393
392
  }
394
393
  getItem() {
395
394
  return this.item;
396
395
  }
397
396
  attachToParent(parent) {
398
- if (parent instanceof ColumnViewNode) {
397
+ if (isItemContainer(parent)) {
399
398
  parent.addItem(this.item);
400
399
  }
401
400
  }
402
401
  attachToParentBefore(parent, before) {
403
- if (parent instanceof ColumnViewNode && before instanceof ColumnViewItemNode) {
402
+ if (isItemContainer(parent) && before instanceof ColumnViewItemNode) {
404
403
  parent.insertItemBefore(this.item, before.getItem());
405
404
  }
406
405
  else {
@@ -408,7 +407,7 @@ export class ColumnViewItemNode extends Node {
408
407
  }
409
408
  }
410
409
  detachFromParent(parent) {
411
- if (parent instanceof ColumnViewNode) {
410
+ if (isItemContainer(parent)) {
412
411
  parent.removeItem(this.item);
413
412
  }
414
413
  }
@@ -1,24 +1,15 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
+ import { type ItemContainer } from "../container-interfaces.js";
2
3
  import type { Props } from "../factory.js";
3
4
  import { Node } from "../node.js";
4
- type ItemLabelFn = (item: unknown) => string;
5
- declare class DropDownStore {
6
- private stringList;
7
- private items;
8
- private labelFn;
9
- constructor(labelFn: ItemLabelFn);
10
- getModel(): Gtk.StringList;
11
- append(item: unknown): void;
12
- remove(item: unknown): void;
13
- getItem(index: number): unknown;
14
- get length(): number;
15
- }
16
- export declare class DropDownNode extends Node<Gtk.DropDown> {
5
+ export declare class DropDownNode extends Node<Gtk.DropDown> implements ItemContainer<unknown> {
17
6
  static matches(type: string): boolean;
18
7
  private store;
19
8
  private onSelectionChanged?;
20
- constructor(type: string, props: Props, app: Gtk.Application);
21
- getStore(): DropDownStore;
9
+ constructor(type: string, props: Props);
10
+ addItem(item: unknown): void;
11
+ insertItemBefore(item: unknown, _beforeItem: unknown): void;
12
+ removeItem(item: unknown): void;
22
13
  protected consumedProps(): Set<string>;
23
14
  updateProps(oldProps: Props, newProps: Props): void;
24
15
  }
@@ -26,9 +17,8 @@ export declare class DropDownItemNode extends Node<never> {
26
17
  static matches(type: string): boolean;
27
18
  protected isVirtual(): boolean;
28
19
  private item;
29
- constructor(type: string, props: Props, app: Gtk.Application);
20
+ constructor(type: string, props: Props);
30
21
  getItem(): unknown;
31
22
  attachToParent(parent: Node): void;
32
23
  detachFromParent(parent: Node): void;
33
24
  }
34
- export {};
@@ -1,4 +1,5 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
+ import { isItemContainer } from "../container-interfaces.js";
2
3
  import { Node } from "../node.js";
3
4
  class DropDownStore {
4
5
  stringList;
@@ -36,8 +37,8 @@ export class DropDownNode extends Node {
36
37
  }
37
38
  store;
38
39
  onSelectionChanged;
39
- constructor(type, props, app) {
40
- super(type, props, app);
40
+ constructor(type, props) {
41
+ super(type, props);
41
42
  const labelFn = props.itemLabel ?? ((item) => String(item));
42
43
  this.onSelectionChanged = props.onSelectionChanged;
43
44
  this.store = new DropDownStore(labelFn);
@@ -51,8 +52,14 @@ export class DropDownNode extends Node {
51
52
  this.connectSignal(this.widget, "notify::selected", handler);
52
53
  }
53
54
  }
54
- getStore() {
55
- return this.store;
55
+ addItem(item) {
56
+ this.store.append(item);
57
+ }
58
+ insertItemBefore(item, _beforeItem) {
59
+ this.addItem(item);
60
+ }
61
+ removeItem(item) {
62
+ this.store.remove(item);
56
63
  }
57
64
  consumedProps() {
58
65
  const consumed = super.consumedProps();
@@ -75,21 +82,21 @@ export class DropDownItemNode extends Node {
75
82
  return true;
76
83
  }
77
84
  item;
78
- constructor(type, props, app) {
79
- super(type, props, app);
85
+ constructor(type, props) {
86
+ super(type, props);
80
87
  this.item = props.item;
81
88
  }
82
89
  getItem() {
83
90
  return this.item;
84
91
  }
85
92
  attachToParent(parent) {
86
- if (!(parent instanceof DropDownNode))
93
+ if (!isItemContainer(parent))
87
94
  return;
88
- parent.getStore().append(this.item);
95
+ parent.addItem(this.item);
89
96
  }
90
97
  detachFromParent(parent) {
91
- if (!(parent instanceof DropDownNode))
98
+ if (!isItemContainer(parent))
92
99
  return;
93
- parent.getStore().remove(this.item);
100
+ parent.removeItem(this.item);
94
101
  }
95
102
  }