@gtkx/react 0.9.0 → 0.9.1

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/node.js CHANGED
@@ -92,7 +92,7 @@ export class Node {
92
92
  this.attachChild(childWidget);
93
93
  }
94
94
  else if (this.widget && isAppendable(this.widget)) {
95
- childWidget.insertBefore(this.widget, null);
95
+ childWidget.insertBefore(this.widget);
96
96
  }
97
97
  else if (this.widget && isAddable(this.widget)) {
98
98
  this.widget.add(childWidget);
@@ -1,4 +1,5 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
+ import { StringSorter } from "@gtkx/ffi/gtk";
2
3
  import type { ColumnContainer } from "../containers.js";
3
4
  import type { Props } from "../factory.js";
4
5
  import { Node } from "../node.js";
@@ -37,8 +38,9 @@ type ColumnViewColumnState = {
37
38
  factory: Gtk.SignalListItemFactory;
38
39
  factoryHandlers: ListItemFactoryHandlers | null;
39
40
  renderCell: RenderItemFn<unknown>;
40
- columnId: string | null;
41
+ columnId?: string;
41
42
  listItemCache: Map<number, ListItemInfo>;
43
+ sorter?: StringSorter;
42
44
  };
43
45
  export declare class ColumnViewColumnNode extends Node<never, ColumnViewColumnState> {
44
46
  static consumedPropNames: string[];
@@ -47,7 +49,7 @@ export declare class ColumnViewColumnNode extends Node<never, ColumnViewColumnSt
47
49
  private columnView;
48
50
  initialize(props: Props): void;
49
51
  getColumn(): Gtk.ColumnViewColumn;
50
- getId(): string | null;
52
+ getId(): string | undefined;
51
53
  setColumnView(columnView: ColumnViewNode | null): void;
52
54
  unmount(): void;
53
55
  updateProps(oldProps: Props, newProps: Props): void;
@@ -1,6 +1,6 @@
1
- import { getObject } from "@gtkx/ffi";
2
1
  import * as GObject from "@gtkx/ffi/gobject";
3
2
  import * as Gtk from "@gtkx/ffi/gtk";
3
+ import { StringSorter } from "@gtkx/ffi/gtk";
4
4
  import { Node } from "../node.js";
5
5
  import { connectListItemFactorySignals } from "./list-item-factory.js";
6
6
  import { SelectableListNode } from "./selectable-list.js";
@@ -53,10 +53,9 @@ export class ColumnViewNode extends SelectableListNode {
53
53
  notifySortChange() {
54
54
  if (!this.state.onSortChange)
55
55
  return;
56
- const baseSorter = this.widget.getSorter();
57
- if (!baseSorter)
56
+ const sorter = this.widget.getSorter();
57
+ if (!sorter)
58
58
  return;
59
- const sorter = getObject(baseSorter.id);
60
59
  const column = sorter.getPrimarySortColumn();
61
60
  const order = sorter.getPrimarySortOrder();
62
61
  const columnId = column?.getId() ?? null;
@@ -122,7 +121,7 @@ export class ColumnViewNode extends SelectableListNode {
122
121
  }
123
122
  applySortIndicator() {
124
123
  if (this.state.sortColumn === null) {
125
- this.widget.sortByColumn(this.state.sortOrder, null);
124
+ this.widget.sortByColumn(this.state.sortOrder);
126
125
  return;
127
126
  }
128
127
  const column = this.state.columns.find((c) => c.getId() === this.state.sortColumn);
@@ -158,7 +157,7 @@ export class ColumnViewNode extends SelectableListNode {
158
157
  }
159
158
  }
160
159
  export class ColumnViewColumnNode extends Node {
161
- static consumedPropNames = ["renderCell", "title", "expand", "resizable", "fixedWidth", "id"];
160
+ static consumedPropNames = ["renderCell", "title", "expand", "resizable", "fixedWidth", "id", "sortable"];
162
161
  static matches(type) {
163
162
  return type === "ColumnView.Column";
164
163
  }
@@ -169,7 +168,9 @@ export class ColumnViewColumnNode extends Node {
169
168
  initialize(props) {
170
169
  const factory = new Gtk.SignalListItemFactory();
171
170
  const column = new Gtk.ColumnViewColumn(props.title, factory);
172
- const columnId = props.id ?? null;
171
+ const columnId = props.id ?? undefined;
172
+ const sortable = props.sortable;
173
+ const sorter = sortable ? new StringSorter() : undefined;
173
174
  this.state = {
174
175
  column,
175
176
  factory,
@@ -177,7 +178,11 @@ export class ColumnViewColumnNode extends Node {
177
178
  renderCell: props.renderCell,
178
179
  columnId,
179
180
  listItemCache: new Map(),
181
+ sorter,
180
182
  };
183
+ if (sorter) {
184
+ column.setSorter(sorter);
185
+ }
181
186
  super.initialize(props);
182
187
  if (columnId !== null) {
183
188
  column.setId(columnId);
@@ -228,9 +233,20 @@ export class ColumnViewColumnNode extends Node {
228
233
  this.state.column.setFixedWidth(newProps.fixedWidth);
229
234
  }
230
235
  if (oldProps.id !== newProps.id) {
231
- this.state.columnId = newProps.id ?? null;
236
+ this.state.columnId = newProps.id ?? undefined;
232
237
  this.state.column.setId(this.state.columnId);
233
238
  }
239
+ if (oldProps.sortable !== newProps.sortable) {
240
+ const sortable = newProps.sortable;
241
+ if (sortable && !this.state.sorter) {
242
+ this.state.sorter = new StringSorter();
243
+ this.state.column.setSorter(this.state.sorter);
244
+ }
245
+ else if (!sortable && this.state.sorter) {
246
+ this.state.column.setSorter(undefined);
247
+ this.state.sorter = undefined;
248
+ }
249
+ }
234
250
  }
235
251
  }
236
252
  export class ColumnViewItemNode extends VirtualItemNode {
@@ -16,7 +16,7 @@ export class FlowBoxNode extends IndexedChildContainerNode {
16
16
  const parent = child.getParent();
17
17
  if (parent && isFlowBoxChild(parent)) {
18
18
  beginBatch();
19
- parent.setChild(null);
19
+ parent.setChild(undefined);
20
20
  this.widget.remove(parent);
21
21
  endBatch();
22
22
  }
@@ -16,7 +16,7 @@ export class ListBoxNode extends IndexedChildContainerNode {
16
16
  const parent = child.getParent();
17
17
  if (parent && isListBoxRow(parent)) {
18
18
  beginBatch();
19
- parent.setChild(null);
19
+ parent.setChild(undefined);
20
20
  this.widget.remove(parent);
21
21
  endBatch();
22
22
  }
@@ -38,7 +38,7 @@ export class ListBoxNode extends IndexedChildContainerNode {
38
38
  detachChild(child) {
39
39
  if (isListBoxRow(child)) {
40
40
  beginBatch();
41
- child.setChild(null);
41
+ child.setChild(undefined);
42
42
  this.widget.remove(child);
43
43
  endBatch();
44
44
  return;
@@ -1,4 +1,4 @@
1
- import { getObject, getObjectId } from "@gtkx/ffi";
1
+ import { 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 { createFiberRoot } from "../fiber-root.js";
@@ -7,7 +7,7 @@ export function connectListItemFactorySignals(config) {
7
7
  const { factory, listItemCache, getRenderFn, getItemAtPosition } = config;
8
8
  const handlerIds = [];
9
9
  const setupId = factory.connect("setup", (_self, listItemObj) => {
10
- const listItem = getObject(listItemObj.id);
10
+ const listItem = listItemObj;
11
11
  const id = getObjectId(listItemObj.id);
12
12
  const box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
13
13
  listItem.setChild(box);
@@ -18,7 +18,7 @@ export function connectListItemFactorySignals(config) {
18
18
  });
19
19
  handlerIds.push(setupId);
20
20
  const bindId = factory.connect("bind", (_self, listItemObj) => {
21
- const listItem = getObject(listItemObj.id);
21
+ const listItem = listItemObj;
22
22
  const id = getObjectId(listItemObj.id);
23
23
  const info = listItemCache.get(id);
24
24
  if (!info)
@@ -37,7 +37,7 @@ declare abstract class MenuContainerNode<T extends Gtk.Widget | undefined> exten
37
37
  protected onMenuRebuilt(): void;
38
38
  }
39
39
  type MenuWidget = Gtk.Widget & {
40
- setMenuModel(model: Gio.MenuModel | null): void;
40
+ setMenuModel(model?: Gio.MenuModel): void;
41
41
  };
42
42
  declare abstract class MenuWidgetNode<T extends MenuWidget> extends MenuContainerNode<T> {
43
43
  protected abstract createMenuWidget(menu: Gio.Menu): T;
@@ -1,4 +1,4 @@
1
- import { getCurrentApp, getInterface } from "@gtkx/ffi";
1
+ import { getCurrentApp, getObject } from "@gtkx/ffi";
2
2
  import * as Gio from "@gtkx/ffi/gio";
3
3
  import * as GObject from "@gtkx/ffi/gobject";
4
4
  import * as Gtk from "@gtkx/ffi/gtk";
@@ -67,13 +67,13 @@ class MenuContainerNode extends NodeClass {
67
67
  this.menu.removeAll();
68
68
  for (const entry of this.entries) {
69
69
  if (entry.type === "item") {
70
- this.menu.append(entry.label ?? null, entry.action ?? null);
70
+ this.menu.append(entry.label, entry.action);
71
71
  }
72
72
  else if (entry.type === "section" && entry.menu) {
73
- this.menu.appendSection(entry.menu, entry.label ?? null);
73
+ this.menu.appendSection(entry.menu, entry.label);
74
74
  }
75
75
  else if (entry.type === "submenu" && entry.menu) {
76
- this.menu.appendSubmenu(entry.menu, entry.label ?? null);
76
+ this.menu.appendSubmenu(entry.menu, entry.label);
77
77
  }
78
78
  }
79
79
  this.onMenuRebuilt();
@@ -119,7 +119,7 @@ export class ApplicationMenuNode extends MenuContainerNode {
119
119
  getCurrentApp().setMenubar(this.menu);
120
120
  }
121
121
  unmount() {
122
- getCurrentApp().setMenubar(null);
122
+ getCurrentApp().setMenubar(undefined);
123
123
  super.unmount();
124
124
  }
125
125
  onMenuRebuilt() {
@@ -199,7 +199,7 @@ export class MenuItemNode extends NodeClass {
199
199
  this.action = new Gio.SimpleAction(this.actionName);
200
200
  this.signalHandlerId = this.action.connect("activate", () => this.invokeCurrentCallback());
201
201
  const app = getCurrentApp();
202
- const action = getInterface(this.action.id, Gio.Action);
202
+ const action = getObject(this.action.id, Gio.Action);
203
203
  if (!action) {
204
204
  throw new Error("Failed to get Gio.Action interface from SimpleAction");
205
205
  }
@@ -34,15 +34,15 @@ export class NotebookNode extends Node {
34
34
  this.widget.setTabLabel(child, tabLabel);
35
35
  }
36
36
  attachChild(child) {
37
- this.widget.appendPage(child, null);
37
+ this.widget.appendPage(child);
38
38
  }
39
39
  insertChildBefore(child, before) {
40
40
  const beforePageNum = this.widget.pageNum(before);
41
41
  if (beforePageNum >= 0) {
42
- this.widget.insertPage(child, beforePageNum, null);
42
+ this.widget.insertPage(child, beforePageNum);
43
43
  }
44
44
  else {
45
- this.widget.appendPage(child, null);
45
+ this.widget.appendPage(child);
46
46
  }
47
47
  }
48
48
  detachChild(child) {
@@ -36,7 +36,7 @@ export class OverlayNode extends Node {
36
36
  }
37
37
  detachChild(childWidget) {
38
38
  if (this.mainChild === childWidget) {
39
- this.widget.setChild(null);
39
+ this.widget.setChild(undefined);
40
40
  this.mainChild = null;
41
41
  }
42
42
  else {
@@ -1,4 +1,4 @@
1
- import { getInterface } from "@gtkx/ffi";
1
+ import { getObject } from "@gtkx/ffi";
2
2
  import * as Gio from "@gtkx/ffi/gio";
3
3
  import * as GObject from "@gtkx/ffi/gobject";
4
4
  import * as Gtk from "@gtkx/ffi/gtk";
@@ -41,7 +41,7 @@ export class SelectableListNode extends NodeClass {
41
41
  initializeSelectionState(props) {
42
42
  const selectionMode = props.selectionMode ?? Gtk.SelectionMode.SINGLE;
43
43
  const stringList = new Gtk.StringList([]);
44
- const listModel = getInterface(stringList.id, Gio.ListModel);
44
+ const listModel = getObject(stringList.id, Gio.ListModel) ?? undefined;
45
45
  const selectionModel = selectionMode === Gtk.SelectionMode.MULTIPLE
46
46
  ? new Gtk.MultiSelection(listModel)
47
47
  : new Gtk.SingleSelection(listModel);
@@ -100,7 +100,7 @@ export class SelectableListNode extends NodeClass {
100
100
  const currentSelection = this.getSelectedIds();
101
101
  const hadHandler = this.state.selectionHandlerId !== null;
102
102
  this.disconnectSelectionHandler();
103
- const listModel = getInterface(this.state.stringList.id, Gio.ListModel);
103
+ const listModel = getObject(this.state.stringList.id, Gio.ListModel) ?? undefined;
104
104
  const newSelectionModel = newSelectionMode === Gtk.SelectionMode.MULTIPLE
105
105
  ? new Gtk.MultiSelection(listModel)
106
106
  : new Gtk.SingleSelection(listModel);
@@ -11,7 +11,7 @@ export class StackNode extends PagedStackNode {
11
11
  const { name, title } = props;
12
12
  let stackPage;
13
13
  if (title !== undefined) {
14
- stackPage = this.widget.addTitled(child, title, name ?? null);
14
+ stackPage = this.widget.addTitled(child, title, name);
15
15
  }
16
16
  else if (name !== undefined) {
17
17
  stackPage = this.widget.addNamed(child, name);
@@ -12,7 +12,7 @@ type StringListContainerState = {
12
12
  hasAppliedInitialSelection: boolean;
13
13
  };
14
14
  type StringListWidget = Gtk.Widget & {
15
- setModel(model: Gio.ListModel | null): void;
15
+ setModel(model?: Gio.ListModel): void;
16
16
  getSelected(): number;
17
17
  setSelected(position: number): void;
18
18
  };
@@ -1,4 +1,4 @@
1
- import { getInterface } from "@gtkx/ffi";
1
+ import { getObject } from "@gtkx/ffi";
2
2
  import * as Gio from "@gtkx/ffi/gio";
3
3
  import { Node as NodeClass } from "../node.js";
4
4
  import { getCallbackChange } from "../props.js";
@@ -44,7 +44,7 @@ export class StringListContainerNode extends NodeClass {
44
44
  const initialSelection = props.selectedId;
45
45
  this.state = { store, onSelectionChanged, initialSelection, hasAppliedInitialSelection: false };
46
46
  super.initialize(props);
47
- this.widget.setModel(getInterface(store.getModel().id, Gio.ListModel));
47
+ this.widget.setModel(getObject(store.getModel().id, Gio.ListModel) ?? undefined);
48
48
  }
49
49
  connectSelectionHandler() {
50
50
  const handler = () => {
@@ -8,10 +8,10 @@ export class ViewStackNode extends PagedStackNode {
8
8
  const { name, title, iconName } = props;
9
9
  let page;
10
10
  if (title !== undefined && iconName !== undefined) {
11
- page = this.widget.addTitledWithIcon(child, title, iconName, name ?? null);
11
+ page = this.widget.addTitledWithIcon(child, title, iconName, name);
12
12
  }
13
13
  else if (title !== undefined) {
14
- page = this.widget.addTitled(child, title, name ?? null);
14
+ page = this.widget.addTitled(child, title, name);
15
15
  }
16
16
  else if (name !== undefined) {
17
17
  page = this.widget.addNamed(child, name);
package/dist/types.d.ts CHANGED
@@ -61,6 +61,8 @@ export type ColumnViewColumnProps<T = unknown> = {
61
61
  fixedWidth?: number;
62
62
  /** Unique identifier for the column. Used for sorting. */
63
63
  id?: string;
64
+ /** Whether this column header can be clicked to trigger sorting. */
65
+ sortable?: boolean;
64
66
  /**
65
67
  * Render function for column cells.
66
68
  * Called with null during setup (for loading state) and with the actual item during bind.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/react",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Build GTK4 desktop applications with React and TypeScript",
5
5
  "keywords": [
6
6
  "gtk",
@@ -36,11 +36,11 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "react-reconciler": "^0.33.0",
39
- "@gtkx/ffi": "0.9.0"
39
+ "@gtkx/ffi": "0.9.1"
40
40
  },
41
41
  "devDependencies": {
42
- "@gtkx/native": "0.9.0",
43
- "@gtkx/gir": "0.9.0"
42
+ "@gtkx/gir": "0.9.1",
43
+ "@gtkx/native": "0.9.1"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "react": "^19"
@@ -48,6 +48,6 @@
48
48
  "scripts": {
49
49
  "build": "tsc -b && cp ../../README.md .",
50
50
  "codegen": "tsx scripts/codegen.ts",
51
- "test": "if [ -n \"$CI\" ]; then vitest run; else GDK_BACKEND=x11 xvfb-run -a vitest run; fi"
51
+ "test": "../../scripts/run-tests.sh"
52
52
  }
53
53
  }