@gtkx/react 0.18.0 → 0.18.2
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/dist/errors.d.ts +1 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +1 -0
- package/dist/errors.js.map +1 -0
- package/dist/factory.d.ts +1 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +1 -0
- package/dist/factory.js.map +1 -0
- package/dist/fiber-root.d.ts +1 -0
- package/dist/fiber-root.d.ts.map +1 -0
- package/dist/fiber-root.js +1 -0
- package/dist/fiber-root.js.map +1 -0
- package/dist/generated/internal.d.ts +1 -0
- package/dist/generated/internal.d.ts.map +1 -0
- package/dist/generated/internal.js +1 -0
- package/dist/generated/internal.js.map +1 -0
- package/dist/generated/jsx.d.ts +1 -0
- package/dist/generated/jsx.d.ts.map +1 -0
- package/dist/generated/jsx.js +1 -0
- package/dist/generated/jsx.js.map +1 -0
- package/dist/generated/registry.d.ts +1 -0
- package/dist/generated/registry.d.ts.map +1 -0
- package/dist/generated/registry.js +1 -0
- package/dist/generated/registry.js.map +1 -0
- package/dist/host-config.d.ts +1 -0
- package/dist/host-config.d.ts.map +1 -0
- package/dist/host-config.js +1 -0
- package/dist/host-config.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx.d.ts +1 -0
- package/dist/jsx.d.ts.map +1 -0
- package/dist/jsx.js +1 -0
- package/dist/jsx.js.map +1 -0
- package/dist/metadata.d.ts +1 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +1 -0
- package/dist/metadata.js.map +1 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +1 -0
- package/dist/node.js.map +1 -0
- package/dist/nodes/adjustable.d.ts +1 -0
- package/dist/nodes/adjustable.d.ts.map +1 -0
- package/dist/nodes/adjustable.js +1 -0
- package/dist/nodes/adjustable.js.map +1 -0
- package/dist/nodes/alert-dialog-response.d.ts +1 -0
- package/dist/nodes/alert-dialog-response.d.ts.map +1 -0
- package/dist/nodes/alert-dialog-response.js +1 -0
- package/dist/nodes/alert-dialog-response.js.map +1 -0
- package/dist/nodes/animation.d.ts +1 -0
- package/dist/nodes/animation.d.ts.map +1 -0
- package/dist/nodes/animation.js +1 -0
- package/dist/nodes/animation.js.map +1 -0
- package/dist/nodes/application.d.ts +1 -0
- package/dist/nodes/application.d.ts.map +1 -0
- package/dist/nodes/application.js +1 -0
- package/dist/nodes/application.js.map +1 -0
- package/dist/nodes/calendar.d.ts +1 -0
- package/dist/nodes/calendar.d.ts.map +1 -0
- package/dist/nodes/calendar.js +1 -0
- package/dist/nodes/calendar.js.map +1 -0
- package/dist/nodes/color-dialog-button.d.ts +1 -0
- package/dist/nodes/color-dialog-button.d.ts.map +1 -0
- package/dist/nodes/color-dialog-button.js +1 -0
- package/dist/nodes/color-dialog-button.js.map +1 -0
- package/dist/nodes/column-view-column.d.ts +1 -0
- package/dist/nodes/column-view-column.d.ts.map +1 -0
- package/dist/nodes/column-view-column.js +1 -0
- package/dist/nodes/column-view-column.js.map +1 -0
- package/dist/nodes/column-view.d.ts +1 -0
- package/dist/nodes/column-view.d.ts.map +1 -0
- package/dist/nodes/column-view.js +1 -0
- package/dist/nodes/column-view.js.map +1 -0
- package/dist/nodes/container-slot.d.ts +1 -0
- package/dist/nodes/container-slot.d.ts.map +1 -0
- package/dist/nodes/container-slot.js +1 -0
- package/dist/nodes/container-slot.js.map +1 -0
- package/dist/nodes/dialog.d.ts +1 -0
- package/dist/nodes/dialog.d.ts.map +1 -0
- package/dist/nodes/dialog.js +1 -0
- package/dist/nodes/dialog.js.map +1 -0
- package/dist/nodes/drawing-area.d.ts +1 -0
- package/dist/nodes/drawing-area.d.ts.map +1 -0
- package/dist/nodes/drawing-area.js +1 -0
- package/dist/nodes/drawing-area.js.map +1 -0
- package/dist/nodes/drop-down.d.ts +1 -0
- package/dist/nodes/drop-down.d.ts.map +1 -0
- package/dist/nodes/drop-down.js +1 -0
- package/dist/nodes/drop-down.js.map +1 -0
- package/dist/nodes/event-controller.d.ts +1 -0
- package/dist/nodes/event-controller.d.ts.map +1 -0
- package/dist/nodes/event-controller.js +1 -0
- package/dist/nodes/event-controller.js.map +1 -0
- package/dist/nodes/fixed-child.d.ts +1 -0
- package/dist/nodes/fixed-child.d.ts.map +1 -0
- package/dist/nodes/fixed-child.js +1 -0
- package/dist/nodes/fixed-child.js.map +1 -0
- package/dist/nodes/font-dialog-button.d.ts +1 -0
- package/dist/nodes/font-dialog-button.d.ts.map +1 -0
- package/dist/nodes/font-dialog-button.js +1 -0
- package/dist/nodes/font-dialog-button.js.map +1 -0
- package/dist/nodes/grid-child.d.ts +1 -0
- package/dist/nodes/grid-child.d.ts.map +1 -0
- package/dist/nodes/grid-child.js +1 -0
- package/dist/nodes/grid-child.js.map +1 -0
- package/dist/nodes/grid-view.d.ts +1 -0
- package/dist/nodes/grid-view.d.ts.map +1 -0
- package/dist/nodes/grid-view.js +1 -0
- package/dist/nodes/grid-view.js.map +1 -0
- package/dist/nodes/internal/base-item-renderer.d.ts +1 -0
- package/dist/nodes/internal/base-item-renderer.d.ts.map +1 -0
- package/dist/nodes/internal/base-item-renderer.js +1 -0
- package/dist/nodes/internal/base-item-renderer.js.map +1 -0
- package/dist/nodes/internal/grid-item-renderer.d.ts +1 -0
- package/dist/nodes/internal/grid-item-renderer.d.ts.map +1 -0
- package/dist/nodes/internal/grid-item-renderer.js +1 -0
- package/dist/nodes/internal/grid-item-renderer.js.map +1 -0
- package/dist/nodes/internal/list-item-renderer.d.ts +1 -0
- package/dist/nodes/internal/list-item-renderer.d.ts.map +1 -0
- package/dist/nodes/internal/list-item-renderer.js +1 -0
- package/dist/nodes/internal/list-item-renderer.js.map +1 -0
- package/dist/nodes/internal/list-store.d.ts +1 -0
- package/dist/nodes/internal/list-store.d.ts.map +1 -0
- package/dist/nodes/internal/list-store.js +1 -0
- package/dist/nodes/internal/list-store.js.map +1 -0
- package/dist/nodes/internal/predicates.d.ts +1 -0
- package/dist/nodes/internal/predicates.d.ts.map +1 -0
- package/dist/nodes/internal/predicates.js +1 -0
- package/dist/nodes/internal/predicates.js.map +1 -0
- package/dist/nodes/internal/props.d.ts +1 -0
- package/dist/nodes/internal/props.d.ts.map +1 -0
- package/dist/nodes/internal/props.js +1 -0
- package/dist/nodes/internal/props.js.map +1 -0
- package/dist/nodes/internal/selection-model-controller.d.ts +1 -0
- package/dist/nodes/internal/selection-model-controller.d.ts.map +1 -0
- package/dist/nodes/internal/selection-model-controller.js +1 -0
- package/dist/nodes/internal/selection-model-controller.js.map +1 -0
- package/dist/nodes/internal/signal-store.d.ts +1 -0
- package/dist/nodes/internal/signal-store.d.ts.map +1 -0
- package/dist/nodes/internal/signal-store.js +1 -0
- package/dist/nodes/internal/signal-store.js.map +1 -0
- package/dist/nodes/internal/simple-list-store.d.ts +1 -0
- package/dist/nodes/internal/simple-list-store.d.ts.map +1 -0
- package/dist/nodes/internal/simple-list-store.js +1 -0
- package/dist/nodes/internal/simple-list-store.js.map +1 -0
- package/dist/nodes/internal/text-buffer-controller.d.ts +1 -0
- package/dist/nodes/internal/text-buffer-controller.d.ts.map +1 -0
- package/dist/nodes/internal/text-buffer-controller.js +1 -0
- package/dist/nodes/internal/text-buffer-controller.js.map +1 -0
- package/dist/nodes/internal/tree-store.d.ts +1 -0
- package/dist/nodes/internal/tree-store.d.ts.map +1 -0
- package/dist/nodes/internal/tree-store.js +1 -0
- package/dist/nodes/internal/tree-store.js.map +1 -0
- package/dist/nodes/internal/widget.d.ts +1 -0
- package/dist/nodes/internal/widget.d.ts.map +1 -0
- package/dist/nodes/internal/widget.js +1 -0
- package/dist/nodes/internal/widget.js.map +1 -0
- package/dist/nodes/level-bar.d.ts +1 -0
- package/dist/nodes/level-bar.d.ts.map +1 -0
- package/dist/nodes/level-bar.js +1 -0
- package/dist/nodes/level-bar.js.map +1 -0
- package/dist/nodes/list-item.d.ts +1 -0
- package/dist/nodes/list-item.d.ts.map +1 -0
- package/dist/nodes/list-item.js +1 -0
- package/dist/nodes/list-item.js.map +1 -0
- package/dist/nodes/list-view.d.ts +1 -0
- package/dist/nodes/list-view.d.ts.map +1 -0
- package/dist/nodes/list-view.js +1 -0
- package/dist/nodes/list-view.js.map +1 -0
- package/dist/nodes/menu.d.ts +1 -0
- package/dist/nodes/menu.d.ts.map +1 -0
- package/dist/nodes/menu.js +1 -0
- package/dist/nodes/menu.js.map +1 -0
- package/dist/nodes/models/grid.d.ts +1 -0
- package/dist/nodes/models/grid.d.ts.map +1 -0
- package/dist/nodes/models/grid.js +1 -0
- package/dist/nodes/models/grid.js.map +1 -0
- package/dist/nodes/models/list.d.ts +1 -0
- package/dist/nodes/models/list.d.ts.map +1 -0
- package/dist/nodes/models/list.js +1 -0
- package/dist/nodes/models/list.js.map +1 -0
- package/dist/nodes/models/menu.d.ts +1 -0
- package/dist/nodes/models/menu.d.ts.map +1 -0
- package/dist/nodes/models/menu.js +1 -0
- package/dist/nodes/models/menu.js.map +1 -0
- package/dist/nodes/navigation-page.d.ts +1 -0
- package/dist/nodes/navigation-page.d.ts.map +1 -0
- package/dist/nodes/navigation-page.js +1 -0
- package/dist/nodes/navigation-page.js.map +1 -0
- package/dist/nodes/navigation-view.d.ts +1 -0
- package/dist/nodes/navigation-view.d.ts.map +1 -0
- package/dist/nodes/navigation-view.js +1 -0
- package/dist/nodes/navigation-view.js.map +1 -0
- package/dist/nodes/notebook-page-tab.d.ts +1 -0
- package/dist/nodes/notebook-page-tab.d.ts.map +1 -0
- package/dist/nodes/notebook-page-tab.js +1 -0
- package/dist/nodes/notebook-page-tab.js.map +1 -0
- package/dist/nodes/notebook-page.d.ts +1 -0
- package/dist/nodes/notebook-page.d.ts.map +1 -0
- package/dist/nodes/notebook-page.js +1 -0
- package/dist/nodes/notebook-page.js.map +1 -0
- package/dist/nodes/notebook.d.ts +1 -0
- package/dist/nodes/notebook.d.ts.map +1 -0
- package/dist/nodes/notebook.js +1 -0
- package/dist/nodes/notebook.js.map +1 -0
- package/dist/nodes/overlay-child.d.ts +1 -0
- package/dist/nodes/overlay-child.d.ts.map +1 -0
- package/dist/nodes/overlay-child.js +1 -0
- package/dist/nodes/overlay-child.js.map +1 -0
- package/dist/nodes/popover-menu.d.ts +1 -0
- package/dist/nodes/popover-menu.d.ts.map +1 -0
- package/dist/nodes/popover-menu.js +1 -0
- package/dist/nodes/popover-menu.js.map +1 -0
- package/dist/nodes/scale.d.ts +1 -0
- package/dist/nodes/scale.d.ts.map +1 -0
- package/dist/nodes/scale.js +1 -0
- package/dist/nodes/scale.js.map +1 -0
- package/dist/nodes/scrolled-window.d.ts +1 -0
- package/dist/nodes/scrolled-window.d.ts.map +1 -0
- package/dist/nodes/scrolled-window.js +1 -0
- package/dist/nodes/scrolled-window.js.map +1 -0
- package/dist/nodes/search-bar.d.ts +1 -0
- package/dist/nodes/search-bar.d.ts.map +1 -0
- package/dist/nodes/search-bar.js +1 -0
- package/dist/nodes/search-bar.js.map +1 -0
- package/dist/nodes/shortcut-controller.d.ts +1 -0
- package/dist/nodes/shortcut-controller.d.ts.map +1 -0
- package/dist/nodes/shortcut-controller.js +1 -0
- package/dist/nodes/shortcut-controller.js.map +1 -0
- package/dist/nodes/shortcut.d.ts +1 -0
- package/dist/nodes/shortcut.d.ts.map +1 -0
- package/dist/nodes/shortcut.js +1 -0
- package/dist/nodes/shortcut.js.map +1 -0
- package/dist/nodes/slot.d.ts +1 -0
- package/dist/nodes/slot.d.ts.map +1 -0
- package/dist/nodes/slot.js +1 -0
- package/dist/nodes/slot.js.map +1 -0
- package/dist/nodes/source-view.d.ts +1 -0
- package/dist/nodes/source-view.d.ts.map +1 -0
- package/dist/nodes/source-view.js +1 -0
- package/dist/nodes/source-view.js.map +1 -0
- package/dist/nodes/stack-page.d.ts +1 -0
- package/dist/nodes/stack-page.d.ts.map +1 -0
- package/dist/nodes/stack-page.js +1 -0
- package/dist/nodes/stack-page.js.map +1 -0
- package/dist/nodes/stack.d.ts +1 -0
- package/dist/nodes/stack.d.ts.map +1 -0
- package/dist/nodes/stack.js +1 -0
- package/dist/nodes/stack.js.map +1 -0
- package/dist/nodes/text-anchor.d.ts +1 -0
- package/dist/nodes/text-anchor.d.ts.map +1 -0
- package/dist/nodes/text-anchor.js +1 -0
- package/dist/nodes/text-anchor.js.map +1 -0
- package/dist/nodes/text-content.d.ts +1 -0
- package/dist/nodes/text-content.d.ts.map +1 -0
- package/dist/nodes/text-content.js +1 -0
- package/dist/nodes/text-content.js.map +1 -0
- package/dist/nodes/text-paintable.d.ts +1 -0
- package/dist/nodes/text-paintable.d.ts.map +1 -0
- package/dist/nodes/text-paintable.js +1 -0
- package/dist/nodes/text-paintable.js.map +1 -0
- package/dist/nodes/text-segment.d.ts +1 -0
- package/dist/nodes/text-segment.d.ts.map +1 -0
- package/dist/nodes/text-segment.js +1 -0
- package/dist/nodes/text-segment.js.map +1 -0
- package/dist/nodes/text-tag.d.ts +1 -0
- package/dist/nodes/text-tag.d.ts.map +1 -0
- package/dist/nodes/text-tag.js +1 -0
- package/dist/nodes/text-tag.js.map +1 -0
- package/dist/nodes/text-view.d.ts +1 -0
- package/dist/nodes/text-view.d.ts.map +1 -0
- package/dist/nodes/text-view.js +1 -0
- package/dist/nodes/text-view.js.map +1 -0
- package/dist/nodes/toggle-group.d.ts +1 -0
- package/dist/nodes/toggle-group.d.ts.map +1 -0
- package/dist/nodes/toggle-group.js +1 -0
- package/dist/nodes/toggle-group.js.map +1 -0
- package/dist/nodes/toggle.d.ts +1 -0
- package/dist/nodes/toggle.d.ts.map +1 -0
- package/dist/nodes/toggle.js +1 -0
- package/dist/nodes/toggle.js.map +1 -0
- package/dist/nodes/virtual.d.ts +1 -0
- package/dist/nodes/virtual.d.ts.map +1 -0
- package/dist/nodes/virtual.js +1 -0
- package/dist/nodes/virtual.js.map +1 -0
- package/dist/nodes/web-view.d.ts +1 -0
- package/dist/nodes/web-view.d.ts.map +1 -0
- package/dist/nodes/web-view.js +1 -0
- package/dist/nodes/web-view.js.map +1 -0
- package/dist/nodes/widget.d.ts +1 -0
- package/dist/nodes/widget.d.ts.map +1 -0
- package/dist/nodes/widget.js +1 -0
- package/dist/nodes/widget.js.map +1 -0
- package/dist/nodes/window.d.ts +1 -0
- package/dist/nodes/window.d.ts.map +1 -0
- package/dist/nodes/window.js +1 -0
- package/dist/nodes/window.js.map +1 -0
- package/dist/portal.d.ts +1 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/portal.js +1 -0
- package/dist/portal.js.map +1 -0
- package/dist/reconciler.d.ts +1 -0
- package/dist/reconciler.d.ts.map +1 -0
- package/dist/reconciler.js +1 -0
- package/dist/reconciler.js.map +1 -0
- package/dist/registry.d.ts +1 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +1 -0
- package/dist/registry.js.map +1 -0
- package/dist/render.d.ts +1 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +1 -0
- package/dist/render.js.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +6 -4
- package/src/errors.ts +52 -0
- package/src/factory.ts +62 -0
- package/src/fiber-root.ts +20 -0
- package/src/generated/internal.ts +2134 -0
- package/src/generated/jsx.ts +20068 -0
- package/src/generated/registry.ts +16 -0
- package/src/host-config.ts +186 -0
- package/src/index.ts +11 -0
- package/src/jsx.ts +1294 -0
- package/src/metadata.ts +36 -0
- package/src/node.ts +109 -0
- package/src/nodes/adjustable.ts +68 -0
- package/src/nodes/alert-dialog-response.ts +78 -0
- package/src/nodes/animation.ts +402 -0
- package/src/nodes/application.ts +56 -0
- package/src/nodes/calendar.ts +38 -0
- package/src/nodes/color-dialog-button.ts +69 -0
- package/src/nodes/column-view-column.ts +84 -0
- package/src/nodes/column-view.ts +192 -0
- package/src/nodes/container-slot.ts +87 -0
- package/src/nodes/dialog.ts +25 -0
- package/src/nodes/drawing-area.ts +51 -0
- package/src/nodes/drop-down.ts +92 -0
- package/src/nodes/event-controller.ts +100 -0
- package/src/nodes/fixed-child.ts +108 -0
- package/src/nodes/font-dialog-button.ts +97 -0
- package/src/nodes/grid-child.ts +103 -0
- package/src/nodes/grid-view.ts +96 -0
- package/src/nodes/internal/base-item-renderer.ts +108 -0
- package/src/nodes/internal/grid-item-renderer.ts +71 -0
- package/src/nodes/internal/list-item-renderer.ts +155 -0
- package/src/nodes/internal/list-store.ts +105 -0
- package/src/nodes/internal/predicates.ts +80 -0
- package/src/nodes/internal/props.ts +51 -0
- package/src/nodes/internal/selection-model-controller.ts +115 -0
- package/src/nodes/internal/signal-store.ts +141 -0
- package/src/nodes/internal/simple-list-store.ts +99 -0
- package/src/nodes/internal/text-buffer-controller.ts +374 -0
- package/src/nodes/internal/tree-store.ts +237 -0
- package/src/nodes/internal/widget.ts +78 -0
- package/src/nodes/level-bar.ts +36 -0
- package/src/nodes/list-item.ts +107 -0
- package/src/nodes/list-view.ts +97 -0
- package/src/nodes/menu.ts +27 -0
- package/src/nodes/models/grid.ts +105 -0
- package/src/nodes/models/list.ts +140 -0
- package/src/nodes/models/menu.ts +310 -0
- package/src/nodes/navigation-page.ts +154 -0
- package/src/nodes/navigation-view.ts +58 -0
- package/src/nodes/notebook-page-tab.ts +55 -0
- package/src/nodes/notebook-page.ts +185 -0
- package/src/nodes/notebook.ts +28 -0
- package/src/nodes/overlay-child.ts +109 -0
- package/src/nodes/popover-menu.ts +59 -0
- package/src/nodes/scale.ts +28 -0
- package/src/nodes/scrolled-window.ts +23 -0
- package/src/nodes/search-bar.ts +27 -0
- package/src/nodes/shortcut-controller.ts +27 -0
- package/src/nodes/shortcut.ts +69 -0
- package/src/nodes/slot.ts +138 -0
- package/src/nodes/source-view.ts +130 -0
- package/src/nodes/stack-page.ts +170 -0
- package/src/nodes/stack.ts +35 -0
- package/src/nodes/text-anchor.ts +74 -0
- package/src/nodes/text-content.ts +14 -0
- package/src/nodes/text-paintable.ts +51 -0
- package/src/nodes/text-segment.ts +55 -0
- package/src/nodes/text-tag.ts +287 -0
- package/src/nodes/text-view.ts +93 -0
- package/src/nodes/toggle-group.ts +27 -0
- package/src/nodes/toggle.ts +72 -0
- package/src/nodes/virtual.ts +16 -0
- package/src/nodes/web-view.ts +22 -0
- package/src/nodes/widget.ts +398 -0
- package/src/nodes/window.ts +159 -0
- package/src/portal.ts +37 -0
- package/src/reconciler.ts +52 -0
- package/src/registry.ts +123 -0
- package/src/render.tsx +192 -0
- package/src/types.ts +7 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import type Reconciler from "react-reconciler";
|
|
4
|
+
import { createFiberRoot } from "../../fiber-root.js";
|
|
5
|
+
import { reconciler } from "../../reconciler.js";
|
|
6
|
+
import type { SignalStore } from "./signal-store.js";
|
|
7
|
+
|
|
8
|
+
export abstract class BaseItemRenderer<TStore = unknown> {
|
|
9
|
+
protected factory: Gtk.SignalListItemFactory;
|
|
10
|
+
protected fiberRoots = new Map<Gtk.ListItem, Reconciler.FiberRoot>();
|
|
11
|
+
protected tornDown = new Set<Gtk.ListItem>();
|
|
12
|
+
protected estimatedItemHeight: number | null = null;
|
|
13
|
+
private store: TStore | null = null;
|
|
14
|
+
protected signalStore: SignalStore;
|
|
15
|
+
|
|
16
|
+
constructor(signalStore: SignalStore) {
|
|
17
|
+
this.signalStore = signalStore;
|
|
18
|
+
this.factory = new Gtk.SignalListItemFactory();
|
|
19
|
+
this.initializeFactory();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public getFactory(): Gtk.SignalListItemFactory {
|
|
23
|
+
return this.factory;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public setEstimatedItemHeight(height: number | null): void {
|
|
27
|
+
this.estimatedItemHeight = height;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public setStore(store: TStore | null): void {
|
|
31
|
+
this.store = store;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected getStore(): TStore {
|
|
35
|
+
if (!this.store) {
|
|
36
|
+
throw new Error(`Expected store to be set on ${this.constructor.name}`);
|
|
37
|
+
}
|
|
38
|
+
return this.store;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public dispose(): void {
|
|
42
|
+
this.signalStore.clear(this);
|
|
43
|
+
this.fiberRoots.clear();
|
|
44
|
+
this.tornDown.clear();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected abstract renderItem(listItem: Gtk.ListItem): ReactNode;
|
|
48
|
+
protected abstract onSetup(listItem: Gtk.ListItem): Gtk.Widget;
|
|
49
|
+
protected abstract onBind(listItem: Gtk.ListItem, fiberRoot: Reconciler.FiberRoot): void;
|
|
50
|
+
protected abstract onUnbind(listItem: Gtk.ListItem): void;
|
|
51
|
+
|
|
52
|
+
protected onSetupComplete(_listItem: Gtk.ListItem): void {}
|
|
53
|
+
protected onTeardown(_listItem: Gtk.ListItem): void {}
|
|
54
|
+
|
|
55
|
+
protected createBox(): Gtk.Box {
|
|
56
|
+
const box = new Gtk.Box(Gtk.Orientation.HORIZONTAL);
|
|
57
|
+
box.setValign(Gtk.Align.CENTER);
|
|
58
|
+
|
|
59
|
+
if (this.estimatedItemHeight !== null) {
|
|
60
|
+
box.setSizeRequest(-1, this.estimatedItemHeight);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return box;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected clearBoxSizeRequest(box: Gtk.Widget): void {
|
|
67
|
+
if (box instanceof Gtk.Box) {
|
|
68
|
+
box.setSizeRequest(-1, -1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private initializeFactory(): void {
|
|
73
|
+
this.signalStore.set(this, this.factory, "setup", (listItem: Gtk.ListItem) => {
|
|
74
|
+
const container = this.onSetup(listItem);
|
|
75
|
+
const fiberRoot = createFiberRoot(container);
|
|
76
|
+
this.fiberRoots.set(listItem, fiberRoot);
|
|
77
|
+
const element = this.renderItem(listItem);
|
|
78
|
+
reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {
|
|
79
|
+
if (this.tornDown.has(listItem)) return;
|
|
80
|
+
this.onSetupComplete(listItem);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
this.signalStore.set(this, this.factory, "bind", (listItem: Gtk.ListItem) => {
|
|
85
|
+
const fiberRoot = this.fiberRoots.get(listItem);
|
|
86
|
+
if (!fiberRoot) return;
|
|
87
|
+
this.onBind(listItem, fiberRoot);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
this.signalStore.set(this, this.factory, "unbind", (listItem: Gtk.ListItem) => {
|
|
91
|
+
this.onUnbind(listItem);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
this.signalStore.set(this, this.factory, "teardown", (listItem: Gtk.ListItem) => {
|
|
95
|
+
const fiberRoot = this.fiberRoots.get(listItem);
|
|
96
|
+
|
|
97
|
+
if (fiberRoot) {
|
|
98
|
+
this.tornDown.add(listItem);
|
|
99
|
+
this.onTeardown(listItem);
|
|
100
|
+
reconciler.getInstance().updateContainer(null, fiberRoot, null, () => {});
|
|
101
|
+
queueMicrotask(() => {
|
|
102
|
+
this.fiberRoots.delete(listItem);
|
|
103
|
+
this.tornDown.delete(listItem);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import type Reconciler from "react-reconciler";
|
|
4
|
+
import { reconciler } from "../../reconciler.js";
|
|
5
|
+
import { BaseItemRenderer } from "./base-item-renderer.js";
|
|
6
|
+
import type { ListStore } from "./list-store.js";
|
|
7
|
+
|
|
8
|
+
export type GridRenderItemFn<T> = (item: T | null) => ReactNode;
|
|
9
|
+
|
|
10
|
+
export class GridItemRenderer extends BaseItemRenderer<ListStore> {
|
|
11
|
+
private renderFn: GridRenderItemFn<unknown> | null = () => null;
|
|
12
|
+
private boundItems = new Map<string, Gtk.ListItem>();
|
|
13
|
+
|
|
14
|
+
public setRenderFn(renderFn: GridRenderItemFn<unknown> | null): void {
|
|
15
|
+
this.renderFn = renderFn;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public rebindItem(id: string): void {
|
|
19
|
+
const listItem = this.boundItems.get(id);
|
|
20
|
+
if (!listItem) return;
|
|
21
|
+
|
|
22
|
+
const fiberRoot = this.fiberRoots.get(listItem);
|
|
23
|
+
if (!fiberRoot) return;
|
|
24
|
+
|
|
25
|
+
const item = this.getStore().getItem(id);
|
|
26
|
+
const element = this.renderFn?.(item);
|
|
27
|
+
|
|
28
|
+
reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected override renderItem(_listItem: Gtk.ListItem): ReactNode {
|
|
32
|
+
return this.renderFn?.(null);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private getItemFromListItem(listItem: Gtk.ListItem): string | null {
|
|
36
|
+
const stringObject = listItem.getItem();
|
|
37
|
+
if (!(stringObject instanceof Gtk.StringObject)) return null;
|
|
38
|
+
return stringObject.getString();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
protected override onSetup(listItem: Gtk.ListItem): Gtk.Widget {
|
|
42
|
+
const box = this.createBox();
|
|
43
|
+
listItem.setChild(box);
|
|
44
|
+
return box;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected override onBind(listItem: Gtk.ListItem, fiberRoot: Reconciler.FiberRoot): void {
|
|
48
|
+
const id = this.getItemFromListItem(listItem);
|
|
49
|
+
if (id !== null) {
|
|
50
|
+
this.boundItems.set(id, listItem);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const item = id !== null ? this.getStore().getItem(id) : null;
|
|
54
|
+
const element = this.renderFn?.(item);
|
|
55
|
+
|
|
56
|
+
reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {
|
|
57
|
+
if (this.tornDown.has(listItem)) return;
|
|
58
|
+
if (this.estimatedItemHeight !== null) return;
|
|
59
|
+
const currentFiberRoot = this.fiberRoots.get(listItem);
|
|
60
|
+
if (!currentFiberRoot) return;
|
|
61
|
+
this.clearBoxSizeRequest(currentFiberRoot.containerInfo);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
protected override onUnbind(listItem: Gtk.ListItem): void {
|
|
66
|
+
const id = this.getItemFromListItem(listItem);
|
|
67
|
+
if (id !== null) {
|
|
68
|
+
this.boundItems.delete(id);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import type Reconciler from "react-reconciler";
|
|
4
|
+
import { reconciler } from "../../reconciler.js";
|
|
5
|
+
import { BaseItemRenderer } from "./base-item-renderer.js";
|
|
6
|
+
import type { TreeStore } from "./tree-store.js";
|
|
7
|
+
|
|
8
|
+
export type RenderItemFn<T> = (item: T | null, row: Gtk.TreeListRow | null) => ReactNode;
|
|
9
|
+
|
|
10
|
+
type PendingBind = {
|
|
11
|
+
treeListRow: Gtk.TreeListRow;
|
|
12
|
+
expander: Gtk.TreeExpander;
|
|
13
|
+
id: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class ListItemRenderer extends BaseItemRenderer<TreeStore> {
|
|
17
|
+
private expanders = new Map<Gtk.ListItem, Gtk.TreeExpander>();
|
|
18
|
+
private setupComplete = new Set<Gtk.ListItem>();
|
|
19
|
+
private pendingBinds = new Map<Gtk.ListItem, PendingBind>();
|
|
20
|
+
private renderFn: RenderItemFn<unknown> | null = () => null;
|
|
21
|
+
private boundItems = new Map<string, { listItem: Gtk.ListItem; treeListRow: Gtk.TreeListRow }>();
|
|
22
|
+
|
|
23
|
+
public setRenderFn(renderFn: RenderItemFn<unknown> | null): void {
|
|
24
|
+
this.renderFn = renderFn;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public rebindItem(id: string): void {
|
|
28
|
+
const binding = this.boundItems.get(id);
|
|
29
|
+
if (!binding) return;
|
|
30
|
+
|
|
31
|
+
const fiberRoot = this.fiberRoots.get(binding.listItem);
|
|
32
|
+
if (!fiberRoot) return;
|
|
33
|
+
|
|
34
|
+
const expander = this.expanders.get(binding.listItem);
|
|
35
|
+
if (!expander) return;
|
|
36
|
+
|
|
37
|
+
this.renderBind(binding.listItem, expander, binding.treeListRow, id, fiberRoot);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public override dispose(): void {
|
|
41
|
+
super.dispose();
|
|
42
|
+
this.expanders.clear();
|
|
43
|
+
this.setupComplete.clear();
|
|
44
|
+
this.pendingBinds.clear();
|
|
45
|
+
this.boundItems.clear();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected override renderItem(_listItem: Gtk.ListItem): ReactNode {
|
|
49
|
+
return this.renderFn?.(null, null);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected override onSetup(listItem: Gtk.ListItem): Gtk.Widget {
|
|
53
|
+
const expander = new Gtk.TreeExpander();
|
|
54
|
+
const box = this.createBox();
|
|
55
|
+
expander.setChild(box);
|
|
56
|
+
listItem.setChild(expander);
|
|
57
|
+
this.expanders.set(listItem, expander);
|
|
58
|
+
return box;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
protected override onSetupComplete(listItem: Gtk.ListItem): void {
|
|
62
|
+
this.setupComplete.add(listItem);
|
|
63
|
+
this.processPendingBind(listItem);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected override onBind(listItem: Gtk.ListItem, fiberRoot: Reconciler.FiberRoot): void {
|
|
67
|
+
const expander = this.expanders.get(listItem);
|
|
68
|
+
if (!expander) return;
|
|
69
|
+
|
|
70
|
+
const treeListRow = listItem.getItem();
|
|
71
|
+
if (!(treeListRow instanceof Gtk.TreeListRow)) return;
|
|
72
|
+
|
|
73
|
+
expander.setListRow(treeListRow);
|
|
74
|
+
|
|
75
|
+
const stringObject = treeListRow.getItem();
|
|
76
|
+
if (!(stringObject instanceof Gtk.StringObject)) return;
|
|
77
|
+
|
|
78
|
+
const id = stringObject.getString();
|
|
79
|
+
this.boundItems.set(id, { listItem, treeListRow });
|
|
80
|
+
|
|
81
|
+
if (!this.setupComplete.has(listItem)) {
|
|
82
|
+
this.pendingBinds.set(listItem, { treeListRow, expander, id });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.renderBind(listItem, expander, treeListRow, id, fiberRoot);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
protected override onUnbind(listItem: Gtk.ListItem): void {
|
|
90
|
+
const expander = listItem.getChild();
|
|
91
|
+
if (expander instanceof Gtk.TreeExpander) {
|
|
92
|
+
expander.setListRow(null);
|
|
93
|
+
const treeListRow = listItem.getItem();
|
|
94
|
+
if (treeListRow instanceof Gtk.TreeListRow) {
|
|
95
|
+
const stringObject = treeListRow.getItem();
|
|
96
|
+
if (stringObject instanceof Gtk.StringObject) {
|
|
97
|
+
this.boundItems.delete(stringObject.getString());
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected override onTeardown(listItem: Gtk.ListItem): void {
|
|
104
|
+
this.expanders.delete(listItem);
|
|
105
|
+
this.setupComplete.delete(listItem);
|
|
106
|
+
this.pendingBinds.delete(listItem);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private processPendingBind(listItem: Gtk.ListItem): void {
|
|
110
|
+
const pending = this.pendingBinds.get(listItem);
|
|
111
|
+
if (!pending) return;
|
|
112
|
+
|
|
113
|
+
this.pendingBinds.delete(listItem);
|
|
114
|
+
const fiberRoot = this.fiberRoots.get(listItem);
|
|
115
|
+
if (fiberRoot) {
|
|
116
|
+
this.renderBind(listItem, pending.expander, pending.treeListRow, pending.id, fiberRoot);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private renderBind(
|
|
121
|
+
listItem: Gtk.ListItem,
|
|
122
|
+
expander: Gtk.TreeExpander,
|
|
123
|
+
treeListRow: Gtk.TreeListRow,
|
|
124
|
+
id: string,
|
|
125
|
+
fiberRoot: Reconciler.FiberRoot,
|
|
126
|
+
): void {
|
|
127
|
+
const itemData = this.getStore().getItem(id);
|
|
128
|
+
|
|
129
|
+
if (itemData) {
|
|
130
|
+
expander.setIndentForDepth(itemData.indentForDepth ?? true);
|
|
131
|
+
expander.setHideExpander(itemData.hideExpander ?? false);
|
|
132
|
+
|
|
133
|
+
if (itemData.indentForIcon !== undefined) {
|
|
134
|
+
expander.setIndentForIcon(itemData.indentForIcon);
|
|
135
|
+
} else {
|
|
136
|
+
expander.setIndentForIcon(treeListRow.isExpandable());
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
expander.setIndentForIcon(treeListRow.isExpandable());
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const element = this.renderFn?.(itemData?.value ?? null, treeListRow);
|
|
143
|
+
|
|
144
|
+
reconciler.getInstance().updateContainer(element, fiberRoot, null, () => {
|
|
145
|
+
if (this.tornDown.has(listItem)) return;
|
|
146
|
+
if (this.estimatedItemHeight !== null) return;
|
|
147
|
+
const currentExpander = this.expanders.get(listItem);
|
|
148
|
+
if (!currentExpander) return;
|
|
149
|
+
const box = currentExpander.getChild();
|
|
150
|
+
if (box) {
|
|
151
|
+
this.clearBoxSizeRequest(box);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
2
|
+
|
|
3
|
+
export type ItemUpdatedCallback = (id: string) => void;
|
|
4
|
+
|
|
5
|
+
export class ListStore {
|
|
6
|
+
private model = new Gtk.StringList();
|
|
7
|
+
private ids: string[] = [];
|
|
8
|
+
private idToIndex = new Map<string, number>();
|
|
9
|
+
private items = new Map<string, unknown>();
|
|
10
|
+
private onItemUpdated: ItemUpdatedCallback | null = null;
|
|
11
|
+
private pendingBatch: string[] | null = null;
|
|
12
|
+
|
|
13
|
+
public setOnItemUpdated(callback: ItemUpdatedCallback | null): void {
|
|
14
|
+
this.onItemUpdated = callback;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public beginBatch(): void {
|
|
18
|
+
this.pendingBatch = [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public flushBatch(): void {
|
|
22
|
+
const batch = this.pendingBatch;
|
|
23
|
+
this.pendingBatch = null;
|
|
24
|
+
if (batch && batch.length > 0) {
|
|
25
|
+
this.model.splice(0, 0, batch);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public addItem(id: string, item: unknown): void {
|
|
30
|
+
this.items.set(id, item);
|
|
31
|
+
|
|
32
|
+
const existingIndex = this.idToIndex.get(id);
|
|
33
|
+
if (existingIndex !== undefined) {
|
|
34
|
+
this.model.remove(existingIndex);
|
|
35
|
+
this.ids.splice(existingIndex, 1);
|
|
36
|
+
this.rebuildIndices(existingIndex);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.idToIndex.set(id, this.ids.length);
|
|
40
|
+
this.ids.push(id);
|
|
41
|
+
|
|
42
|
+
if (this.pendingBatch) {
|
|
43
|
+
this.pendingBatch.push(id);
|
|
44
|
+
} else {
|
|
45
|
+
this.model.append(id);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public removeItem(id: string): void {
|
|
50
|
+
const index = this.idToIndex.get(id);
|
|
51
|
+
if (index === undefined) return;
|
|
52
|
+
|
|
53
|
+
this.model.remove(index);
|
|
54
|
+
this.ids.splice(index, 1);
|
|
55
|
+
this.idToIndex.delete(id);
|
|
56
|
+
this.rebuildIndices(index);
|
|
57
|
+
this.items.delete(id);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public insertItemBefore(id: string, beforeId: string, item: unknown): void {
|
|
61
|
+
this.items.set(id, item);
|
|
62
|
+
|
|
63
|
+
const existingIndex = this.idToIndex.get(id);
|
|
64
|
+
if (existingIndex !== undefined) {
|
|
65
|
+
this.model.remove(existingIndex);
|
|
66
|
+
this.ids.splice(existingIndex, 1);
|
|
67
|
+
this.idToIndex.delete(id);
|
|
68
|
+
this.rebuildIndices(existingIndex);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const beforeIndex = this.idToIndex.get(beforeId);
|
|
72
|
+
if (beforeIndex === undefined) {
|
|
73
|
+
this.idToIndex.set(id, this.ids.length);
|
|
74
|
+
this.ids.push(id);
|
|
75
|
+
this.model.append(id);
|
|
76
|
+
} else {
|
|
77
|
+
this.ids.splice(beforeIndex, 0, id);
|
|
78
|
+
this.rebuildIndices(beforeIndex);
|
|
79
|
+
this.model.splice(beforeIndex, 0, [id]);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public updateItem(id: string, item: unknown): void {
|
|
84
|
+
if (this.items.has(id)) {
|
|
85
|
+
this.items.set(id, item);
|
|
86
|
+
this.onItemUpdated?.(id);
|
|
87
|
+
} else {
|
|
88
|
+
this.addItem(id, item);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public getItem(id: string): unknown {
|
|
93
|
+
return this.items.get(id);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public getModel(): Gtk.StringList {
|
|
97
|
+
return this.model;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private rebuildIndices(fromIndex: number): void {
|
|
101
|
+
for (let i = fromIndex; i < this.ids.length; i++) {
|
|
102
|
+
this.idToIndex.set(this.ids[i] as string, i);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime type predicates for GTK widget capability detection.
|
|
3
|
+
*
|
|
4
|
+
* These predicates check for specific APIs that widgets may or may not expose.
|
|
5
|
+
* GTK widgets don't have a consistent interface - different widgets support
|
|
6
|
+
* different child management APIs - so runtime checking is necessary.
|
|
7
|
+
*/
|
|
8
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
9
|
+
|
|
10
|
+
type AppendableWidget = Gtk.Widget & { append: (child: Gtk.Widget) => void };
|
|
11
|
+
type AddableWidget = Gtk.Widget & { add: (child: Gtk.Widget) => void };
|
|
12
|
+
type ContentWidget = Gtk.Widget & { setContent: (content?: Gtk.Widget | null) => void };
|
|
13
|
+
type SingleChildWidget = Gtk.Widget & { setChild: (child: Gtk.Widget | null) => void };
|
|
14
|
+
type RemovableWidget = Gtk.Widget & { remove: (child: Gtk.Widget) => void };
|
|
15
|
+
|
|
16
|
+
export type ReorderableWidget = Gtk.Widget & {
|
|
17
|
+
reorderChildAfter: (child: Gtk.Widget, sibling?: Gtk.Widget) => void;
|
|
18
|
+
insertChildAfter: (child: Gtk.Widget, sibling?: Gtk.Widget) => void;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type InsertableWidget = Gtk.Widget & {
|
|
22
|
+
insert: (child: Gtk.Widget, position: number) => void;
|
|
23
|
+
getFirstChild: () => Gtk.Widget | null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type EditableWidget = Gtk.Widget & {
|
|
27
|
+
getPosition: () => number;
|
|
28
|
+
setPosition: (position: number) => void;
|
|
29
|
+
getText: () => string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type BufferedWidget = Gtk.Widget & {
|
|
33
|
+
getBuffer: () => Gtk.TextBuffer;
|
|
34
|
+
setBuffer: (buffer?: Gtk.TextBuffer | null) => void;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const isAppendable = (obj: unknown): obj is AppendableWidget =>
|
|
38
|
+
obj instanceof Gtk.Widget && "append" in obj && typeof obj.append === "function";
|
|
39
|
+
|
|
40
|
+
export const isAddable = (obj: unknown): obj is AddableWidget =>
|
|
41
|
+
obj instanceof Gtk.Widget && "add" in obj && typeof obj.add === "function";
|
|
42
|
+
|
|
43
|
+
export const isContentWidget = (obj: unknown): obj is ContentWidget =>
|
|
44
|
+
obj instanceof Gtk.Widget && "setContent" in obj && typeof obj.setContent === "function";
|
|
45
|
+
|
|
46
|
+
export const isSingleChild = (obj: unknown): obj is SingleChildWidget =>
|
|
47
|
+
obj instanceof Gtk.Widget && "setChild" in obj && typeof obj.setChild === "function";
|
|
48
|
+
|
|
49
|
+
export const isRemovable = (obj: unknown): obj is RemovableWidget =>
|
|
50
|
+
obj instanceof Gtk.Widget && "remove" in obj && typeof obj.remove === "function";
|
|
51
|
+
|
|
52
|
+
export const isReorderable = (obj: unknown): obj is ReorderableWidget =>
|
|
53
|
+
obj instanceof Gtk.Widget &&
|
|
54
|
+
"reorderChildAfter" in obj &&
|
|
55
|
+
typeof obj.reorderChildAfter === "function" &&
|
|
56
|
+
"insertChildAfter" in obj &&
|
|
57
|
+
typeof obj.insertChildAfter === "function";
|
|
58
|
+
|
|
59
|
+
export const isInsertable = (obj: unknown): obj is InsertableWidget =>
|
|
60
|
+
obj instanceof Gtk.Widget &&
|
|
61
|
+
"insert" in obj &&
|
|
62
|
+
typeof obj.insert === "function" &&
|
|
63
|
+
"getFirstChild" in obj &&
|
|
64
|
+
typeof obj.getFirstChild === "function";
|
|
65
|
+
|
|
66
|
+
export const isEditable = (obj: unknown): obj is EditableWidget =>
|
|
67
|
+
obj instanceof Gtk.Widget &&
|
|
68
|
+
"getPosition" in obj &&
|
|
69
|
+
typeof obj.getPosition === "function" &&
|
|
70
|
+
"setPosition" in obj &&
|
|
71
|
+
typeof obj.setPosition === "function" &&
|
|
72
|
+
"getText" in obj &&
|
|
73
|
+
typeof obj.getText === "function";
|
|
74
|
+
|
|
75
|
+
export const isBuffered = (obj: unknown): obj is BufferedWidget =>
|
|
76
|
+
obj instanceof Gtk.Widget &&
|
|
77
|
+
"getBuffer" in obj &&
|
|
78
|
+
typeof obj.getBuffer === "function" &&
|
|
79
|
+
"setBuffer" in obj &&
|
|
80
|
+
typeof obj.setBuffer === "function";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Props } from "../../types.js";
|
|
2
|
+
|
|
3
|
+
export const filterProps = <T extends Props>(props: T, excludeKeys: readonly string[]): T => {
|
|
4
|
+
const result: Props = {};
|
|
5
|
+
|
|
6
|
+
for (const key of Object.keys(props)) {
|
|
7
|
+
if (!excludeKeys.includes(key)) {
|
|
8
|
+
result[key] = props[key];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return result as T;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const hasChanged = <T>(oldProps: T | null, newProps: T, key: keyof T): boolean =>
|
|
16
|
+
!oldProps || oldProps[key] !== newProps[key];
|
|
17
|
+
|
|
18
|
+
export const shallowArrayEqual = <T extends Record<string, unknown>>(a: T[], b: T[]): boolean => {
|
|
19
|
+
if (a.length !== b.length) return false;
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < a.length; i++) {
|
|
22
|
+
const itemA = a[i];
|
|
23
|
+
const itemB = b[i];
|
|
24
|
+
if (!itemA || !itemB) return false;
|
|
25
|
+
|
|
26
|
+
const keysA = Object.keys(itemA);
|
|
27
|
+
const keysB = Object.keys(itemB);
|
|
28
|
+
if (keysA.length !== keysB.length) return false;
|
|
29
|
+
|
|
30
|
+
for (const key of keysA) {
|
|
31
|
+
if (itemA[key] !== itemB[key]) return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return true;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const primitiveArrayEqual = <T extends string | number | boolean>(
|
|
39
|
+
a: T[] | null | undefined,
|
|
40
|
+
b: T[] | null | undefined,
|
|
41
|
+
): boolean => {
|
|
42
|
+
if (a === b) return true;
|
|
43
|
+
if (!a || !b) return false;
|
|
44
|
+
if (a.length !== b.length) return false;
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < a.length; i++) {
|
|
47
|
+
if (a[i] !== b[i]) return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type * as Gio from "@gtkx/ffi/gio";
|
|
2
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
3
|
+
import type { GtkListViewProps } from "../../jsx.js";
|
|
4
|
+
import type { SignalStore } from "./signal-store.js";
|
|
5
|
+
|
|
6
|
+
type SelectionModel = Gtk.NoSelection | Gtk.SingleSelection | Gtk.MultiSelection;
|
|
7
|
+
|
|
8
|
+
export type SelectionModelConfig = Pick<GtkListViewProps, "selectionMode" | "selected" | "onSelectionChanged"> & {
|
|
9
|
+
owner: object;
|
|
10
|
+
signalStore: SignalStore;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export class SelectionModelController {
|
|
14
|
+
private owner: object;
|
|
15
|
+
private signalStore: SignalStore;
|
|
16
|
+
private selectionModel: SelectionModel;
|
|
17
|
+
private getSelection: () => string[];
|
|
18
|
+
private resolveSelectionIndices: (ids: string[]) => Gtk.Bitset;
|
|
19
|
+
private getItemCount: () => number;
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
config: SelectionModelConfig,
|
|
23
|
+
model: Gio.ListModel,
|
|
24
|
+
getSelection: () => string[],
|
|
25
|
+
resolveSelectionIndices: (ids: string[]) => Gtk.Bitset,
|
|
26
|
+
getItemCount: () => number,
|
|
27
|
+
) {
|
|
28
|
+
this.owner = config.owner;
|
|
29
|
+
this.signalStore = config.signalStore;
|
|
30
|
+
this.selectionModel = this.createSelectionModel(config.selectionMode, model);
|
|
31
|
+
this.selectionModel.setModel(model);
|
|
32
|
+
this.getSelection = getSelection;
|
|
33
|
+
this.resolveSelectionIndices = resolveSelectionIndices;
|
|
34
|
+
this.getItemCount = getItemCount;
|
|
35
|
+
this.initSelectionHandler(config.onSelectionChanged);
|
|
36
|
+
this.setSelection(config.selected);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public getSelectionModel(): SelectionModel {
|
|
40
|
+
return this.selectionModel;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public update(
|
|
44
|
+
oldProps: SelectionModelConfig | null,
|
|
45
|
+
newProps: SelectionModelConfig,
|
|
46
|
+
model: Gio.ListModel,
|
|
47
|
+
): SelectionModel {
|
|
48
|
+
if (oldProps && oldProps.selectionMode !== newProps.selectionMode) {
|
|
49
|
+
this.signalStore.set(this.owner, this.selectionModel, "selection-changed", null);
|
|
50
|
+
this.selectionModel = this.createSelectionModel(newProps.selectionMode, model);
|
|
51
|
+
this.selectionModel.setModel(model);
|
|
52
|
+
this.initSelectionHandler(newProps.onSelectionChanged);
|
|
53
|
+
this.setSelection(newProps.selected);
|
|
54
|
+
return this.selectionModel;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!oldProps || oldProps.onSelectionChanged !== newProps.onSelectionChanged) {
|
|
58
|
+
this.initSelectionHandler(newProps.onSelectionChanged);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!oldProps || oldProps.selected !== newProps.selected) {
|
|
62
|
+
this.setSelection(newProps.selected);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return this.selectionModel;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private initSelectionHandler(onSelectionChanged?: ((ids: string[]) => void) | null): void {
|
|
69
|
+
if (!onSelectionChanged) {
|
|
70
|
+
this.signalStore.set(this.owner, this.selectionModel, "selection-changed", null);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const handler = () => {
|
|
75
|
+
onSelectionChanged(this.getSelection());
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
this.signalStore.set(this.owner, this.selectionModel, "selection-changed", handler);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private createSelectionModel(mode: Gtk.SelectionMode | null | undefined, model: Gio.ListModel): SelectionModel {
|
|
82
|
+
const selectionMode = mode ?? Gtk.SelectionMode.SINGLE;
|
|
83
|
+
|
|
84
|
+
if (selectionMode === Gtk.SelectionMode.NONE) {
|
|
85
|
+
return new Gtk.NoSelection(model);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (selectionMode === Gtk.SelectionMode.MULTIPLE) {
|
|
89
|
+
return new Gtk.MultiSelection(model);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const selectionModel = new Gtk.SingleSelection(model);
|
|
93
|
+
selectionModel.setAutoselect(selectionMode === Gtk.SelectionMode.BROWSE);
|
|
94
|
+
selectionModel.setCanUnselect(selectionMode !== Gtk.SelectionMode.BROWSE);
|
|
95
|
+
|
|
96
|
+
return selectionModel;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public reapplySelection(ids?: string[] | null): void {
|
|
100
|
+
this.setSelection(ids);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private setSelection(ids?: string[] | null): void {
|
|
104
|
+
const nItems = this.getItemCount();
|
|
105
|
+
const selected = ids ? this.resolveSelectionIndices(ids) : new Gtk.Bitset();
|
|
106
|
+
const mask = Gtk.Bitset.newRange(0, nItems);
|
|
107
|
+
|
|
108
|
+
if (this.selectionModel instanceof Gtk.SingleSelection) {
|
|
109
|
+
const position = selected.getSize() > 0 ? selected.getNth(0) : Gtk.INVALID_LIST_POSITION;
|
|
110
|
+
this.selectionModel.setSelected(position);
|
|
111
|
+
} else {
|
|
112
|
+
this.selectionModel.setSelection(selected, mask);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|