@gtkx/cli 0.12.1 → 0.13.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "CLI for GTKX - create and develop GTK4 React applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtkx",
|
|
@@ -57,20 +57,20 @@
|
|
|
57
57
|
"citty": "^0.1.6",
|
|
58
58
|
"ejs": "^3.1.10",
|
|
59
59
|
"react-refresh": "^0.18.0",
|
|
60
|
-
"vite": "^7.3.
|
|
61
|
-
"@gtkx/ffi": "0.
|
|
62
|
-
"@gtkx/mcp": "0.
|
|
63
|
-
"@gtkx/react": "0.
|
|
60
|
+
"vite": "^7.3.1",
|
|
61
|
+
"@gtkx/ffi": "0.13.1",
|
|
62
|
+
"@gtkx/mcp": "0.13.1",
|
|
63
|
+
"@gtkx/react": "0.13.1"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@types/ejs": "^3.1.5",
|
|
67
67
|
"@types/react-refresh": "^0.14.7",
|
|
68
68
|
"memfs": "^4.51.1",
|
|
69
|
-
"@gtkx/testing": "0.
|
|
69
|
+
"@gtkx/testing": "0.13.1"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
72
|
"react": "^19",
|
|
73
|
-
"@gtkx/testing": "0.
|
|
73
|
+
"@gtkx/testing": "0.13.1"
|
|
74
74
|
},
|
|
75
75
|
"peerDependenciesMeta": {
|
|
76
76
|
"@gtkx/testing": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { GtkApplicationWindow, GtkLabel, render, quit } from "@gtkx/react";
|
|
9
9
|
|
|
10
10
|
const App = () => (
|
|
11
|
-
<GtkApplicationWindow title="Hello" defaultWidth={400} defaultHeight={300}
|
|
11
|
+
<GtkApplicationWindow title="Hello" defaultWidth={400} defaultHeight={300} onClose={quit}>
|
|
12
12
|
<GtkLabel label="Hello, World!" />
|
|
13
13
|
</GtkApplicationWindow>
|
|
14
14
|
);
|
|
@@ -27,21 +27,20 @@ import {
|
|
|
27
27
|
AdwWindowTitle,
|
|
28
28
|
AdwStatusPage,
|
|
29
29
|
GtkButton,
|
|
30
|
-
Slot,
|
|
31
|
-
Toolbar,
|
|
32
30
|
quit,
|
|
31
|
+
x,
|
|
33
32
|
} from "@gtkx/react";
|
|
34
33
|
|
|
35
34
|
export const App = () => (
|
|
36
|
-
<AdwApplicationWindow title="My App" defaultWidth={800} defaultHeight={600}
|
|
35
|
+
<AdwApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onClose={quit}>
|
|
37
36
|
<AdwToolbarView>
|
|
38
|
-
<
|
|
37
|
+
<x.ToolbarTop>
|
|
39
38
|
<AdwHeaderBar>
|
|
40
|
-
<Slot for={AdwHeaderBar} id="titleWidget">
|
|
39
|
+
<x.Slot for={AdwHeaderBar} id="titleWidget">
|
|
41
40
|
<AdwWindowTitle title="My App" subtitle="Welcome" />
|
|
42
|
-
</Slot>
|
|
41
|
+
</x.Slot>
|
|
43
42
|
</AdwHeaderBar>
|
|
44
|
-
</
|
|
43
|
+
</x.ToolbarTop>
|
|
45
44
|
<AdwStatusPage
|
|
46
45
|
iconName="applications-system-symbolic"
|
|
47
46
|
title="Welcome"
|
|
@@ -65,7 +64,7 @@ export const appId = "com.example.myapp";
|
|
|
65
64
|
|
|
66
65
|
```tsx
|
|
67
66
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
68
|
-
import { GtkBox, GtkButton, GtkEntry, GtkGrid, GtkLabel,
|
|
67
|
+
import { GtkBox, GtkButton, GtkEntry, GtkGrid, GtkLabel, x } from "@gtkx/react";
|
|
69
68
|
import { useState } from "react";
|
|
70
69
|
|
|
71
70
|
const LoginForm = () => {
|
|
@@ -78,21 +77,21 @@ const LoginForm = () => {
|
|
|
78
77
|
|
|
79
78
|
return (
|
|
80
79
|
<GtkGrid rowSpacing={12} columnSpacing={12}>
|
|
81
|
-
<GridChild column={0} row={0}>
|
|
80
|
+
<x.GridChild column={0} row={0}>
|
|
82
81
|
<GtkLabel label="Email:" halign={Gtk.Align.END} />
|
|
83
|
-
</GridChild>
|
|
84
|
-
<GridChild column={1} row={0}>
|
|
82
|
+
</x.GridChild>
|
|
83
|
+
<x.GridChild column={1} row={0}>
|
|
85
84
|
<GtkEntry text={email} onChanged={(e) => setEmail(e.getText())} hexpand />
|
|
86
|
-
</GridChild>
|
|
87
|
-
<GridChild column={0} row={1}>
|
|
85
|
+
</x.GridChild>
|
|
86
|
+
<x.GridChild column={0} row={1}>
|
|
88
87
|
<GtkLabel label="Password:" halign={Gtk.Align.END} />
|
|
89
|
-
</GridChild>
|
|
90
|
-
<GridChild column={1} row={1}>
|
|
88
|
+
</x.GridChild>
|
|
89
|
+
<x.GridChild column={1} row={1}>
|
|
91
90
|
<GtkEntry text={password} onChanged={(e) => setPassword(e.getText())} visibility={false} hexpand />
|
|
92
|
-
</GridChild>
|
|
93
|
-
<GridChild column={0} row={2} columnSpan={2}>
|
|
91
|
+
</x.GridChild>
|
|
92
|
+
<x.GridChild column={0} row={2} columnSpan={2}>
|
|
94
93
|
<GtkButton label="Login" onClicked={handleSubmit} cssClasses={["suggested-action"]} halign={Gtk.Align.END} />
|
|
95
|
-
</GridChild>
|
|
94
|
+
</x.GridChild>
|
|
96
95
|
</GtkGrid>
|
|
97
96
|
);
|
|
98
97
|
};
|
|
@@ -102,7 +101,7 @@ const LoginForm = () => {
|
|
|
102
101
|
|
|
103
102
|
```tsx
|
|
104
103
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
105
|
-
import { GtkBox, GtkButton, GtkEntry, GtkLabel, GtkScrolledWindow,
|
|
104
|
+
import { GtkBox, GtkButton, GtkEntry, GtkLabel, GtkScrolledWindow, x } from "@gtkx/react";
|
|
106
105
|
import { useCallback, useState } from "react";
|
|
107
106
|
|
|
108
107
|
interface Todo {
|
|
@@ -133,7 +132,7 @@ const TodoList = () => {
|
|
|
133
132
|
<GtkButton label="Add" onClicked={addTodo} cssClasses={["suggested-action"]} />
|
|
134
133
|
</GtkBox>
|
|
135
134
|
<GtkScrolledWindow vexpand cssClasses={["card"]}>
|
|
136
|
-
<ListView<Todo>
|
|
135
|
+
<x.ListView<Todo>
|
|
137
136
|
renderItem={(todo) => (
|
|
138
137
|
<GtkBox orientation={Gtk.Orientation.HORIZONTAL} spacing={8} marginStart={12} marginEnd={12} marginTop={8} marginBottom={8}>
|
|
139
138
|
<GtkLabel label={todo?.text ?? ""} hexpand halign={Gtk.Align.START} />
|
|
@@ -142,9 +141,9 @@ const TodoList = () => {
|
|
|
142
141
|
)}
|
|
143
142
|
>
|
|
144
143
|
{todos.map((todo) => (
|
|
145
|
-
<ListItem key={todo.id} id={todo.id} value={todo} />
|
|
144
|
+
<x.ListItem key={todo.id} id={todo.id} value={todo} />
|
|
146
145
|
))}
|
|
147
|
-
</ListView>
|
|
146
|
+
</x.ListView>
|
|
148
147
|
</GtkScrolledWindow>
|
|
149
148
|
</GtkBox>
|
|
150
149
|
);
|
|
@@ -159,7 +158,7 @@ const TodoList = () => {
|
|
|
159
158
|
|
|
160
159
|
```tsx
|
|
161
160
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
162
|
-
import { GtkBox, GtkLabel, GtkPaned, GtkScrolledWindow, GtkStack,
|
|
161
|
+
import { GtkBox, GtkLabel, GtkPaned, GtkScrolledWindow, GtkStack, x } from "@gtkx/react";
|
|
163
162
|
import { useState } from "react";
|
|
164
163
|
|
|
165
164
|
interface Page {
|
|
@@ -178,9 +177,9 @@ const SidebarNav = () => {
|
|
|
178
177
|
|
|
179
178
|
return (
|
|
180
179
|
<GtkPaned orientation={Gtk.Orientation.HORIZONTAL} position={200}>
|
|
181
|
-
<Slot for={GtkPaned} id="startChild">
|
|
180
|
+
<x.Slot for={GtkPaned} id="startChild">
|
|
182
181
|
<GtkScrolledWindow cssClasses={["sidebar"]}>
|
|
183
|
-
<ListView<Page>
|
|
182
|
+
<x.ListView<Page>
|
|
184
183
|
selected={[currentPage]}
|
|
185
184
|
selectionMode={Gtk.SelectionMode.SINGLE}
|
|
186
185
|
onSelectionChanged={(ids) => setCurrentPage(ids[0])}
|
|
@@ -189,18 +188,18 @@ const SidebarNav = () => {
|
|
|
189
188
|
)}
|
|
190
189
|
>
|
|
191
190
|
{pages.map((page) => (
|
|
192
|
-
<ListItem key={page.id} id={page.id} value={page} />
|
|
191
|
+
<x.ListItem key={page.id} id={page.id} value={page} />
|
|
193
192
|
))}
|
|
194
|
-
</ListView>
|
|
193
|
+
</x.ListView>
|
|
195
194
|
</GtkScrolledWindow>
|
|
196
|
-
</Slot>
|
|
197
|
-
<Slot for={GtkPaned} id="endChild">
|
|
198
|
-
<GtkStack
|
|
199
|
-
<StackPage
|
|
200
|
-
<StackPage
|
|
201
|
-
<StackPage
|
|
195
|
+
</x.Slot>
|
|
196
|
+
<x.Slot for={GtkPaned} id="endChild">
|
|
197
|
+
<GtkStack page={currentPage}>
|
|
198
|
+
<x.StackPage id="home"><GtkLabel label="Home Content" vexpand /></x.StackPage>
|
|
199
|
+
<x.StackPage id="settings"><GtkLabel label="Settings Content" vexpand /></x.StackPage>
|
|
200
|
+
<x.StackPage id="about"><GtkLabel label="About Content" vexpand /></x.StackPage>
|
|
202
201
|
</GtkStack>
|
|
203
|
-
</Slot>
|
|
202
|
+
</x.Slot>
|
|
204
203
|
</GtkPaned>
|
|
205
204
|
);
|
|
206
205
|
};
|
|
@@ -210,34 +209,34 @@ const SidebarNav = () => {
|
|
|
210
209
|
|
|
211
210
|
```tsx
|
|
212
211
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
213
|
-
import { GtkApplicationWindow, GtkBox, GtkButton, GtkHeaderBar, GtkLabel, GtkStack, GtkWindow,
|
|
212
|
+
import { GtkApplicationWindow, GtkBox, GtkButton, GtkHeaderBar, GtkLabel, GtkStack, GtkWindow, quit, x } from "@gtkx/react";
|
|
214
213
|
import { useState } from "react";
|
|
215
214
|
|
|
216
215
|
const AppWithNavigation = () => {
|
|
217
216
|
const [page, setPage] = useState("home");
|
|
218
217
|
|
|
219
218
|
return (
|
|
220
|
-
<GtkApplicationWindow title="App" defaultWidth={600} defaultHeight={400}
|
|
221
|
-
<Slot for={GtkWindow} id="titlebar">
|
|
219
|
+
<GtkApplicationWindow title="App" defaultWidth={600} defaultHeight={400} onClose={quit}>
|
|
220
|
+
<x.Slot for={GtkWindow} id="titlebar">
|
|
222
221
|
<GtkHeaderBar>
|
|
223
|
-
<
|
|
222
|
+
<x.PackStart>
|
|
224
223
|
{page !== "home" && <GtkButton iconName="go-previous-symbolic" onClicked={() => setPage("home")} />}
|
|
225
|
-
</
|
|
226
|
-
<Slot for={GtkHeaderBar} id="titleWidget">
|
|
224
|
+
</x.PackStart>
|
|
225
|
+
<x.Slot for={GtkHeaderBar} id="titleWidget">
|
|
227
226
|
<GtkLabel label={page === "home" ? "Home" : "Details"} cssClasses={["title"]} />
|
|
228
|
-
</Slot>
|
|
227
|
+
</x.Slot>
|
|
229
228
|
</GtkHeaderBar>
|
|
230
|
-
</Slot>
|
|
231
|
-
<GtkStack
|
|
232
|
-
<StackPage
|
|
229
|
+
</x.Slot>
|
|
230
|
+
<GtkStack page={page}>
|
|
231
|
+
<x.StackPage id="home">
|
|
233
232
|
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} vexpand halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
|
|
234
233
|
<GtkLabel label="Welcome" />
|
|
235
234
|
<GtkButton label="Go to Details" onClicked={() => setPage("details")} />
|
|
236
235
|
</GtkBox>
|
|
237
|
-
</StackPage>
|
|
238
|
-
<StackPage
|
|
236
|
+
</x.StackPage>
|
|
237
|
+
<x.StackPage id="details">
|
|
239
238
|
<GtkLabel label="Details Page Content" vexpand />
|
|
240
|
-
</StackPage>
|
|
239
|
+
</x.StackPage>
|
|
241
240
|
</GtkStack>
|
|
242
241
|
</GtkApplicationWindow>
|
|
243
242
|
);
|
|
@@ -259,7 +258,7 @@ import {
|
|
|
259
258
|
AdwEntryRow,
|
|
260
259
|
GtkImage,
|
|
261
260
|
GtkScrolledWindow,
|
|
262
|
-
|
|
261
|
+
x,
|
|
263
262
|
} from "@gtkx/react";
|
|
264
263
|
import { useState } from "react";
|
|
265
264
|
|
|
@@ -278,9 +277,9 @@ const SettingsPage = () => {
|
|
|
278
277
|
<AdwPreferencesGroup title="Account">
|
|
279
278
|
<AdwEntryRow title="Username" text={username} onChanged={(e) => setUsername(e.getText())} />
|
|
280
279
|
<AdwActionRow title="Profile" subtitle="Manage your profile">
|
|
281
|
-
<Slot for={AdwActionRow} id="activatableWidget">
|
|
280
|
+
<x.Slot for={AdwActionRow} id="activatableWidget">
|
|
282
281
|
<GtkImage iconName="go-next-symbolic" valign={Gtk.Align.CENTER} />
|
|
283
|
-
</Slot>
|
|
282
|
+
</x.Slot>
|
|
284
283
|
</AdwActionRow>
|
|
285
284
|
</AdwPreferencesGroup>
|
|
286
285
|
|
|
@@ -303,7 +302,7 @@ const SettingsPage = () => {
|
|
|
303
302
|
|
|
304
303
|
```tsx
|
|
305
304
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
306
|
-
import { GtkBox, GtkColumnView, GtkLabel, GtkScrolledWindow,
|
|
305
|
+
import { GtkBox, GtkColumnView, GtkLabel, GtkScrolledWindow, x } from "@gtkx/react";
|
|
307
306
|
import { useMemo, useState } from "react";
|
|
308
307
|
|
|
309
308
|
interface FileItem {
|
|
@@ -340,12 +339,12 @@ const FileTable = () => {
|
|
|
340
339
|
|
|
341
340
|
return (
|
|
342
341
|
<GtkScrolledWindow vexpand cssClasses={["card"]}>
|
|
343
|
-
<GtkColumnView sortColumn={sortColumn} sortOrder={sortOrder} onSortChange={handleSort}>
|
|
344
|
-
<ColumnViewColumn<FileItem> title="Name" id="name" expand sortable renderCell={(f) => <GtkLabel label={f?.name ?? ""} />} />
|
|
345
|
-
<ColumnViewColumn<FileItem> title="Size" id="size" fixedWidth={100} sortable renderCell={(f) => <GtkLabel label={`${f?.size ?? 0} KB`} />} />
|
|
346
|
-
<ColumnViewColumn<FileItem> title="Modified" id="modified" fixedWidth={120} sortable renderCell={(f) => <GtkLabel label={f?.modified ?? ""} />} />
|
|
342
|
+
<GtkColumnView estimatedRowHeight={48} sortColumn={sortColumn} sortOrder={sortOrder} onSortChange={handleSort}>
|
|
343
|
+
<x.ColumnViewColumn<FileItem> title="Name" id="name" expand sortable renderCell={(f) => <GtkLabel label={f?.name ?? ""} />} />
|
|
344
|
+
<x.ColumnViewColumn<FileItem> title="Size" id="size" fixedWidth={100} sortable renderCell={(f) => <GtkLabel label={`${f?.size ?? 0} KB`} />} />
|
|
345
|
+
<x.ColumnViewColumn<FileItem> title="Modified" id="modified" fixedWidth={120} sortable renderCell={(f) => <GtkLabel label={f?.modified ?? ""} />} />
|
|
347
346
|
{sortedFiles.map((file) => (
|
|
348
|
-
<ListItem key={file.id} id={file.id} value={file} />
|
|
347
|
+
<x.ListItem key={file.id} id={file.id} value={file} />
|
|
349
348
|
))}
|
|
350
349
|
</GtkColumnView>
|
|
351
350
|
</GtkScrolledWindow>
|
|
@@ -359,7 +358,7 @@ const FileTable = () => {
|
|
|
359
358
|
|
|
360
359
|
```tsx
|
|
361
360
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
362
|
-
import { GtkBox, GtkLabel, GtkMenuButton, GtkPopoverMenu,
|
|
361
|
+
import { GtkBox, GtkLabel, GtkMenuButton, GtkPopoverMenu, quit, x } from "@gtkx/react";
|
|
363
362
|
import { useState } from "react";
|
|
364
363
|
|
|
365
364
|
const MenuDemo = () => {
|
|
@@ -368,24 +367,24 @@ const MenuDemo = () => {
|
|
|
368
367
|
return (
|
|
369
368
|
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} marginStart={16} marginEnd={16} marginTop={16} marginBottom={16}>
|
|
370
369
|
<GtkMenuButton label="File" halign={Gtk.Align.START}>
|
|
371
|
-
<Slot for={GtkMenuButton} id="popover">
|
|
370
|
+
<x.Slot for={GtkMenuButton} id="popover">
|
|
372
371
|
<GtkPopoverMenu>
|
|
373
|
-
<
|
|
374
|
-
<
|
|
375
|
-
<
|
|
376
|
-
<
|
|
377
|
-
</
|
|
378
|
-
<
|
|
379
|
-
<
|
|
380
|
-
<
|
|
381
|
-
<
|
|
382
|
-
</
|
|
383
|
-
</
|
|
384
|
-
<
|
|
385
|
-
<
|
|
386
|
-
</
|
|
372
|
+
<x.MenuSection>
|
|
373
|
+
<x.MenuItem id="new" label="New" onActivate={() => setLastAction("New")} accels="<Control>n" />
|
|
374
|
+
<x.MenuItem id="open" label="Open" onActivate={() => setLastAction("Open")} accels="<Control>o" />
|
|
375
|
+
<x.MenuItem id="save" label="Save" onActivate={() => setLastAction("Save")} accels="<Control>s" />
|
|
376
|
+
</x.MenuSection>
|
|
377
|
+
<x.MenuSection>
|
|
378
|
+
<x.MenuSubmenu label="Export">
|
|
379
|
+
<x.MenuItem id="pdf" label="As PDF" onActivate={() => setLastAction("Export PDF")} />
|
|
380
|
+
<x.MenuItem id="csv" label="As CSV" onActivate={() => setLastAction("Export CSV")} />
|
|
381
|
+
</x.MenuSubmenu>
|
|
382
|
+
</x.MenuSection>
|
|
383
|
+
<x.MenuSection>
|
|
384
|
+
<x.MenuItem id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
|
|
385
|
+
</x.MenuSection>
|
|
387
386
|
</GtkPopoverMenu>
|
|
388
|
-
</Slot>
|
|
387
|
+
</x.Slot>
|
|
389
388
|
</GtkMenuButton>
|
|
390
389
|
<GtkLabel label={`Last action: ${lastAction ?? "(none)"}`} />
|
|
391
390
|
</GtkBox>
|
|
@@ -399,7 +398,7 @@ const MenuDemo = () => {
|
|
|
399
398
|
|
|
400
399
|
```tsx
|
|
401
400
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
402
|
-
import { AdwSpinner, GtkBox, GtkLabel, GtkScrolledWindow,
|
|
401
|
+
import { AdwSpinner, GtkBox, GtkLabel, GtkScrolledWindow, x } from "@gtkx/react";
|
|
403
402
|
import { useEffect, useState } from "react";
|
|
404
403
|
|
|
405
404
|
interface User {
|
|
@@ -442,7 +441,7 @@ const AsyncList = () => {
|
|
|
442
441
|
|
|
443
442
|
return (
|
|
444
443
|
<GtkScrolledWindow vexpand>
|
|
445
|
-
<ListView<User>
|
|
444
|
+
<x.ListView<User>
|
|
446
445
|
renderItem={(user) => (
|
|
447
446
|
<GtkBox orientation={Gtk.Orientation.VERTICAL} marginStart={12} marginTop={8} marginBottom={8}>
|
|
448
447
|
<GtkLabel label={user?.name ?? ""} halign={Gtk.Align.START} cssClasses={["heading"]} />
|
|
@@ -451,9 +450,9 @@ const AsyncList = () => {
|
|
|
451
450
|
)}
|
|
452
451
|
>
|
|
453
452
|
{users.map((user) => (
|
|
454
|
-
<ListItem key={user.id} id={user.id} value={user} />
|
|
453
|
+
<x.ListItem key={user.id} id={user.id} value={user} />
|
|
455
454
|
))}
|
|
456
|
-
</ListView>
|
|
455
|
+
</x.ListView>
|
|
457
456
|
</GtkScrolledWindow>
|
|
458
457
|
);
|
|
459
458
|
};
|
|
@@ -498,3 +497,144 @@ const CardDemo = () => (
|
|
|
498
497
|
</GtkBox>
|
|
499
498
|
);
|
|
500
499
|
```
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
## Navigation with AdwNavigationView
|
|
504
|
+
|
|
505
|
+
```tsx
|
|
506
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
507
|
+
import {
|
|
508
|
+
AdwApplicationWindow,
|
|
509
|
+
AdwHeaderBar,
|
|
510
|
+
AdwNavigationView,
|
|
511
|
+
AdwToolbarView,
|
|
512
|
+
GtkBox,
|
|
513
|
+
GtkButton,
|
|
514
|
+
GtkLabel,
|
|
515
|
+
quit,
|
|
516
|
+
x,
|
|
517
|
+
} from "@gtkx/react";
|
|
518
|
+
import { useState } from "react";
|
|
519
|
+
|
|
520
|
+
const NavigationDemo = () => {
|
|
521
|
+
const [history, setHistory] = useState(["home"]);
|
|
522
|
+
|
|
523
|
+
const push = (page: string) => setHistory([...history, page]);
|
|
524
|
+
const pop = () => setHistory(history.slice(0, -1));
|
|
525
|
+
|
|
526
|
+
return (
|
|
527
|
+
<AdwApplicationWindow title="Navigation Demo" defaultWidth={600} defaultHeight={400} onClose={quit}>
|
|
528
|
+
<AdwNavigationView history={history} onHistoryChanged={setHistory}>
|
|
529
|
+
<x.NavigationPage id="home" title="Home">
|
|
530
|
+
<AdwToolbarView>
|
|
531
|
+
<x.ToolbarTop><AdwHeaderBar /></x.ToolbarTop>
|
|
532
|
+
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12} halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
|
|
533
|
+
<GtkLabel label="Welcome!" cssClasses={["title-1"]} />
|
|
534
|
+
<GtkButton label="Go to Settings" onClicked={() => push("settings")} cssClasses={["suggested-action"]} />
|
|
535
|
+
</GtkBox>
|
|
536
|
+
</AdwToolbarView>
|
|
537
|
+
</x.NavigationPage>
|
|
538
|
+
<x.NavigationPage id="settings" title="Settings" canPop>
|
|
539
|
+
<AdwToolbarView>
|
|
540
|
+
<x.ToolbarTop><AdwHeaderBar /></x.ToolbarTop>
|
|
541
|
+
<GtkLabel label="Settings page content" vexpand />
|
|
542
|
+
</AdwToolbarView>
|
|
543
|
+
</x.NavigationPage>
|
|
544
|
+
</AdwNavigationView>
|
|
545
|
+
</AdwApplicationWindow>
|
|
546
|
+
);
|
|
547
|
+
};
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## File Browser with TreeListView
|
|
553
|
+
|
|
554
|
+
```tsx
|
|
555
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
556
|
+
import { GtkBox, GtkImage, GtkLabel, GtkScrolledWindow, x } from "@gtkx/react";
|
|
557
|
+
import { useState } from "react";
|
|
558
|
+
|
|
559
|
+
interface FileNode {
|
|
560
|
+
id: string;
|
|
561
|
+
name: string;
|
|
562
|
+
isDirectory: boolean;
|
|
563
|
+
children?: FileNode[];
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const files: FileNode[] = [
|
|
567
|
+
{
|
|
568
|
+
id: "src",
|
|
569
|
+
name: "src",
|
|
570
|
+
isDirectory: true,
|
|
571
|
+
children: [
|
|
572
|
+
{ id: "src/app.tsx", name: "app.tsx", isDirectory: false },
|
|
573
|
+
{ id: "src/index.tsx", name: "index.tsx", isDirectory: false },
|
|
574
|
+
],
|
|
575
|
+
},
|
|
576
|
+
{ id: "package.json", name: "package.json", isDirectory: false },
|
|
577
|
+
];
|
|
578
|
+
|
|
579
|
+
const FileBrowser = () => {
|
|
580
|
+
const [selected, setSelected] = useState<string | null>(null);
|
|
581
|
+
|
|
582
|
+
return (
|
|
583
|
+
<GtkScrolledWindow vexpand cssClasses={["card"]}>
|
|
584
|
+
<x.TreeListView<FileNode>
|
|
585
|
+
estimatedItemHeight={36}
|
|
586
|
+
vexpand
|
|
587
|
+
autoexpand={false}
|
|
588
|
+
selectionMode={Gtk.SelectionMode.SINGLE}
|
|
589
|
+
selected={selected ? [selected] : []}
|
|
590
|
+
onSelectionChanged={(ids) => setSelected(ids[0] ?? null)}
|
|
591
|
+
renderItem={(item) => (
|
|
592
|
+
<GtkBox orientation={Gtk.Orientation.HORIZONTAL} spacing={8}>
|
|
593
|
+
<GtkImage iconName={item?.isDirectory ? "folder-symbolic" : "text-x-generic-symbolic"} />
|
|
594
|
+
<GtkLabel label={item?.name ?? ""} halign={Gtk.Align.START} />
|
|
595
|
+
</GtkBox>
|
|
596
|
+
)}
|
|
597
|
+
>
|
|
598
|
+
{files.map((file) => (
|
|
599
|
+
<x.TreeListItem key={file.id} id={file.id} value={file}>
|
|
600
|
+
{file.children?.map((child) => (
|
|
601
|
+
<x.TreeListItem key={child.id} id={child.id} value={child} />
|
|
602
|
+
))}
|
|
603
|
+
</x.TreeListItem>
|
|
604
|
+
))}
|
|
605
|
+
</x.TreeListView>
|
|
606
|
+
</GtkScrolledWindow>
|
|
607
|
+
);
|
|
608
|
+
};
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## Toggle Group View Switcher
|
|
614
|
+
|
|
615
|
+
```tsx
|
|
616
|
+
import * as Gtk from "@gtkx/ffi/gtk";
|
|
617
|
+
import { AdwToggleGroup, GtkBox, GtkLabel, GtkStack, x } from "@gtkx/react";
|
|
618
|
+
import { useState } from "react";
|
|
619
|
+
|
|
620
|
+
const ViewSwitcher = () => {
|
|
621
|
+
const [view, setView] = useState("list");
|
|
622
|
+
|
|
623
|
+
return (
|
|
624
|
+
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
|
|
625
|
+
<AdwToggleGroup active={view} onToggled={setView} halign={Gtk.Align.CENTER}>
|
|
626
|
+
<x.Toggle id="list" iconName="view-list-symbolic" tooltip="List view" />
|
|
627
|
+
<x.Toggle id="grid" iconName="view-grid-symbolic" tooltip="Grid view" />
|
|
628
|
+
</AdwToggleGroup>
|
|
629
|
+
<GtkStack page={view} vexpand>
|
|
630
|
+
<x.StackPage id="list">
|
|
631
|
+
<GtkLabel label="List View Content" />
|
|
632
|
+
</x.StackPage>
|
|
633
|
+
<x.StackPage id="grid">
|
|
634
|
+
<GtkLabel label="Grid View Content" />
|
|
635
|
+
</x.StackPage>
|
|
636
|
+
</GtkStack>
|
|
637
|
+
</GtkBox>
|
|
638
|
+
);
|
|
639
|
+
};
|
|
640
|
+
```
|
|
@@ -14,7 +14,7 @@ import { GtkApplicationWindow, GtkBox, GtkButton, render, quit } from "@gtkx/rea
|
|
|
14
14
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
15
15
|
|
|
16
16
|
const App = () => (
|
|
17
|
-
<GtkApplicationWindow title="My App" defaultWidth={800} defaultHeight={600}
|
|
17
|
+
<GtkApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onClose={quit}>
|
|
18
18
|
<GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
|
|
19
19
|
Hello, GTKX!
|
|
20
20
|
<GtkButton label="Quit" onClicked={quit} />
|
|
@@ -53,8 +53,8 @@ Some widgets require children in specific slots:
|
|
|
53
53
|
|
|
54
54
|
```tsx
|
|
55
55
|
<GtkPaned orientation={Gtk.Orientation.HORIZONTAL}>
|
|
56
|
-
<Slot for={GtkPaned} id="startChild"><Sidebar /></Slot>
|
|
57
|
-
<Slot for={GtkPaned} id="endChild"><Content /></Slot>
|
|
56
|
+
<x.Slot for={GtkPaned} id="startChild"><Sidebar /></x.Slot>
|
|
57
|
+
<x.Slot for={GtkPaned} id="endChild"><Content /></x.Slot>
|
|
58
58
|
</GtkPaned>
|
|
59
59
|
```
|
|
60
60
|
|
|
@@ -62,8 +62,8 @@ Some widgets require children in specific slots:
|
|
|
62
62
|
|
|
63
63
|
```tsx
|
|
64
64
|
<GtkHeaderBar>
|
|
65
|
-
<
|
|
66
|
-
<
|
|
65
|
+
<x.PackStart><GtkButton iconName="go-previous-symbolic" /></x.PackStart>
|
|
66
|
+
<x.PackEnd><GtkMenuButton iconName="open-menu-symbolic" /></x.PackEnd>
|
|
67
67
|
</GtkHeaderBar>
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -30,9 +30,9 @@ Linear layout (horizontal or vertical).
|
|
|
30
30
|
|
|
31
31
|
```tsx
|
|
32
32
|
<GtkGrid rowSpacing={8} columnSpacing={12}>
|
|
33
|
-
<GridChild column={0} row={0}><GtkLabel label="Name:" /></GridChild>
|
|
34
|
-
<GridChild column={1} row={0}><GtkEntry hexpand /></GridChild>
|
|
35
|
-
<GridChild column={0} row={1} columnSpan={2}><GtkButton label="Submit" /></GridChild>
|
|
33
|
+
<x.GridChild column={0} row={0}><GtkLabel label="Name:" /></x.GridChild>
|
|
34
|
+
<x.GridChild column={1} row={0}><GtkEntry hexpand /></x.GridChild>
|
|
35
|
+
<x.GridChild column={0} row={1} columnSpan={2}><GtkButton label="Submit" /></x.GridChild>
|
|
36
36
|
</GtkGrid>
|
|
37
37
|
```
|
|
38
38
|
|
|
@@ -42,39 +42,39 @@ Linear layout (horizontal or vertical).
|
|
|
42
42
|
Page container, shows one child at a time.
|
|
43
43
|
|
|
44
44
|
```tsx
|
|
45
|
-
<GtkStack
|
|
46
|
-
<StackPage
|
|
45
|
+
<GtkStack page="page1" transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT}>
|
|
46
|
+
<x.StackPage id="page1" title="First" iconName="document-new">
|
|
47
47
|
<Content1 />
|
|
48
|
-
</StackPage>
|
|
49
|
-
<StackPage
|
|
48
|
+
</x.StackPage>
|
|
49
|
+
<x.StackPage id="page2" title="Second">
|
|
50
50
|
<Content2 />
|
|
51
|
-
</StackPage>
|
|
51
|
+
</x.StackPage>
|
|
52
52
|
</GtkStack>
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
**StackPage props:** `
|
|
55
|
+
**StackPage props:** `id` (optional), `title`, `iconName`, `needsAttention`, `badgeNumber` (AdwViewStack only)
|
|
56
56
|
|
|
57
57
|
### GtkNotebook
|
|
58
58
|
Tabbed container with visible tabs.
|
|
59
59
|
|
|
60
60
|
```tsx
|
|
61
61
|
<GtkNotebook>
|
|
62
|
-
<
|
|
63
|
-
<
|
|
62
|
+
<x.NotebookPage label="Tab 1"><Content1 /></x.NotebookPage>
|
|
63
|
+
<x.NotebookPage label="Tab 2"><Content2 /></x.NotebookPage>
|
|
64
64
|
</GtkNotebook>
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
Custom tab widget:
|
|
68
68
|
```tsx
|
|
69
|
-
<
|
|
70
|
-
<
|
|
69
|
+
<x.NotebookPage>
|
|
70
|
+
<x.NotebookPageTab>
|
|
71
71
|
<GtkBox orientation={Gtk.Orientation.HORIZONTAL} spacing={4}>
|
|
72
72
|
<GtkImage iconName="folder-symbolic" />
|
|
73
73
|
<GtkLabel label="Files" />
|
|
74
74
|
</GtkBox>
|
|
75
|
-
</
|
|
75
|
+
</x.NotebookPageTab>
|
|
76
76
|
<Content />
|
|
77
|
-
</
|
|
77
|
+
</x.NotebookPage>
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
### GtkPaned
|
|
@@ -82,8 +82,8 @@ Resizable split with draggable divider. **Requires Slot components.**
|
|
|
82
82
|
|
|
83
83
|
```tsx
|
|
84
84
|
<GtkPaned orientation={Gtk.Orientation.HORIZONTAL} position={280} shrinkStartChild={false}>
|
|
85
|
-
<Slot for={GtkPaned} id="startChild"><Sidebar /></Slot>
|
|
86
|
-
<Slot for={GtkPaned} id="endChild"><MainContent /></Slot>
|
|
85
|
+
<x.Slot for={GtkPaned} id="startChild"><Sidebar /></x.Slot>
|
|
86
|
+
<x.Slot for={GtkPaned} id="endChild"><MainContent /></x.Slot>
|
|
87
87
|
</GtkPaned>
|
|
88
88
|
```
|
|
89
89
|
|
|
@@ -93,9 +93,9 @@ Stack widgets on top of each other. First child is base layer, additional childr
|
|
|
93
93
|
```tsx
|
|
94
94
|
<GtkOverlay>
|
|
95
95
|
<GtkButton label="Notifications" />
|
|
96
|
-
<OverlayChild>
|
|
96
|
+
<x.OverlayChild>
|
|
97
97
|
<GtkLabel label="3" cssClasses={["badge"]} halign={Gtk.Align.END} valign={Gtk.Align.START} />
|
|
98
|
-
</OverlayChild>
|
|
98
|
+
</x.OverlayChild>
|
|
99
99
|
</GtkOverlay>
|
|
100
100
|
```
|
|
101
101
|
|
|
@@ -118,22 +118,24 @@ All virtual list widgets use `ListItem` children and a `renderItem` function.
|
|
|
118
118
|
High-performance scrollable list with selection.
|
|
119
119
|
|
|
120
120
|
```tsx
|
|
121
|
-
<ListView<Item>
|
|
121
|
+
<x.ListView<Item>
|
|
122
|
+
estimatedItemHeight={48}
|
|
122
123
|
vexpand
|
|
123
124
|
selected={selectedId ? [selectedId] : []}
|
|
124
125
|
selectionMode={Gtk.SelectionMode.SINGLE}
|
|
125
126
|
onSelectionChanged={(ids) => setSelectedId(ids[0])}
|
|
126
127
|
renderItem={(item) => <GtkLabel label={item?.name ?? ""} />}
|
|
127
128
|
>
|
|
128
|
-
{items.map(item => <ListItem key={item.id} id={item.id} value={item} />)}
|
|
129
|
-
</ListView>
|
|
129
|
+
{items.map(item => <x.ListItem key={item.id} id={item.id} value={item} />)}
|
|
130
|
+
</x.ListView>
|
|
130
131
|
```
|
|
131
132
|
|
|
132
133
|
### GridView
|
|
133
134
|
Grid-based virtual scrolling.
|
|
134
135
|
|
|
135
136
|
```tsx
|
|
136
|
-
<GridView<Item>
|
|
137
|
+
<x.GridView<Item>
|
|
138
|
+
estimatedItemHeight={100}
|
|
137
139
|
minColumns={2}
|
|
138
140
|
maxColumns={4}
|
|
139
141
|
renderItem={(item) => (
|
|
@@ -143,16 +145,16 @@ Grid-based virtual scrolling.
|
|
|
143
145
|
</GtkBox>
|
|
144
146
|
)}
|
|
145
147
|
>
|
|
146
|
-
{items.map(item => <ListItem key={item.id} id={item.id} value={item} />)}
|
|
147
|
-
</GridView>
|
|
148
|
+
{items.map(item => <x.ListItem key={item.id} id={item.id} value={item} />)}
|
|
149
|
+
</x.GridView>
|
|
148
150
|
```
|
|
149
151
|
|
|
150
152
|
### GtkColumnView
|
|
151
153
|
Table with sortable columns.
|
|
152
154
|
|
|
153
155
|
```tsx
|
|
154
|
-
<GtkColumnView sortColumn="name" sortOrder={Gtk.SortType.ASCENDING} onSortChange={handleSort}>
|
|
155
|
-
<ColumnViewColumn<Item>
|
|
156
|
+
<GtkColumnView estimatedRowHeight={48} sortColumn="name" sortOrder={Gtk.SortType.ASCENDING} onSortChange={handleSort}>
|
|
157
|
+
<x.ColumnViewColumn<Item>
|
|
156
158
|
title="Name"
|
|
157
159
|
id="name"
|
|
158
160
|
expand
|
|
@@ -160,13 +162,13 @@ Table with sortable columns.
|
|
|
160
162
|
sortable
|
|
161
163
|
renderCell={(item) => <GtkLabel label={item?.name ?? ""} />}
|
|
162
164
|
/>
|
|
163
|
-
<ColumnViewColumn<Item>
|
|
165
|
+
<x.ColumnViewColumn<Item>
|
|
164
166
|
title="Size"
|
|
165
167
|
id="size"
|
|
166
168
|
fixedWidth={100}
|
|
167
169
|
renderCell={(item) => <GtkLabel label={item?.size ?? ""} />}
|
|
168
170
|
/>
|
|
169
|
-
{items.map(item => <ListItem key={item.id} id={item.id} value={item} />)}
|
|
171
|
+
{items.map(item => <x.ListItem key={item.id} id={item.id} value={item} />)}
|
|
170
172
|
</GtkColumnView>
|
|
171
173
|
```
|
|
172
174
|
|
|
@@ -175,10 +177,40 @@ Selection dropdown.
|
|
|
175
177
|
|
|
176
178
|
```tsx
|
|
177
179
|
<GtkDropDown selectedId={selectedId} onSelectionChanged={setSelectedId}>
|
|
178
|
-
{options.map(opt => <SimpleListItem key={opt.id} id={opt.id} value={opt.label} />)}
|
|
180
|
+
{options.map(opt => <x.SimpleListItem key={opt.id} id={opt.id} value={opt.label} />)}
|
|
179
181
|
</GtkDropDown>
|
|
180
182
|
```
|
|
181
183
|
|
|
184
|
+
### TreeListView
|
|
185
|
+
Hierarchical tree with expand/collapse.
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
<x.TreeListView<FileNode>
|
|
189
|
+
estimatedItemHeight={48}
|
|
190
|
+
vexpand
|
|
191
|
+
autoexpand={false}
|
|
192
|
+
selectionMode={Gtk.SelectionMode.SINGLE}
|
|
193
|
+
selected={selectedId ? [selectedId] : []}
|
|
194
|
+
onSelectionChanged={(ids) => setSelectedId(ids[0])}
|
|
195
|
+
renderItem={(item, row) => (
|
|
196
|
+
<GtkBox orientation={Gtk.Orientation.HORIZONTAL} spacing={8}>
|
|
197
|
+
<GtkImage iconName={item?.isDirectory ? "folder-symbolic" : "text-x-generic-symbolic"} />
|
|
198
|
+
<GtkLabel label={item?.name ?? ""} />
|
|
199
|
+
</GtkBox>
|
|
200
|
+
)}
|
|
201
|
+
>
|
|
202
|
+
{files.map(file => (
|
|
203
|
+
<x.TreeListItem key={file.id} id={file.id} value={file}>
|
|
204
|
+
{file.children?.map(child => (
|
|
205
|
+
<x.TreeListItem key={child.id} id={child.id} value={child} />
|
|
206
|
+
))}
|
|
207
|
+
</x.TreeListItem>
|
|
208
|
+
))}
|
|
209
|
+
</x.TreeListView>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**TreeListItem props:** `id`, `value`, `indentForDepth`, `indentForIcon`, `hideExpander`, nested `children`
|
|
213
|
+
|
|
182
214
|
---
|
|
183
215
|
|
|
184
216
|
## Inputs
|
|
@@ -219,6 +251,44 @@ Numeric input with increment/decrement.
|
|
|
219
251
|
<GtkSpinButton value={count} onValueChanged={(sb) => setCount(sb.getValue())} />
|
|
220
252
|
```
|
|
221
253
|
|
|
254
|
+
### GtkScale
|
|
255
|
+
Slider with optional marks.
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
<GtkScale
|
|
259
|
+
orientation={Gtk.Orientation.HORIZONTAL}
|
|
260
|
+
drawValue
|
|
261
|
+
valuePos={Gtk.PositionType.TOP}
|
|
262
|
+
>
|
|
263
|
+
<x.ScaleMark value={0} label="Min" />
|
|
264
|
+
<x.ScaleMark value={50} />
|
|
265
|
+
<x.ScaleMark value={100} label="Max" />
|
|
266
|
+
</GtkScale>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**ScaleMark props:** `value` (required), `position`, `label`
|
|
270
|
+
|
|
271
|
+
### GtkCalendar
|
|
272
|
+
Date picker with markable days.
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
<GtkCalendar onDaySelected={(cal) => setDate(cal.getDate())}>
|
|
276
|
+
<x.CalendarMark day={15} />
|
|
277
|
+
<x.CalendarMark day={20} />
|
|
278
|
+
</GtkCalendar>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### GtkLevelBar
|
|
282
|
+
Progress/level indicator with customizable thresholds.
|
|
283
|
+
|
|
284
|
+
```tsx
|
|
285
|
+
<GtkLevelBar value={0.6}>
|
|
286
|
+
<x.LevelBarOffset id="low" value={0.25} />
|
|
287
|
+
<x.LevelBarOffset id="high" value={0.75} />
|
|
288
|
+
<x.LevelBarOffset id="full" value={1.0} />
|
|
289
|
+
</GtkLevelBar>
|
|
290
|
+
```
|
|
291
|
+
|
|
222
292
|
---
|
|
223
293
|
|
|
224
294
|
## Display
|
|
@@ -247,11 +317,11 @@ Title bar with packed widgets. Use `Pack.Start`, `Pack.End`, and `Slot` for titl
|
|
|
247
317
|
|
|
248
318
|
```tsx
|
|
249
319
|
<GtkHeaderBar>
|
|
250
|
-
<
|
|
251
|
-
<Slot for={GtkHeaderBar} id="titleWidget">
|
|
320
|
+
<x.PackStart><GtkButton iconName="go-previous-symbolic" /></x.PackStart>
|
|
321
|
+
<x.Slot for={GtkHeaderBar} id="titleWidget">
|
|
252
322
|
<GtkLabel label="Title" cssClasses={["title"]} />
|
|
253
|
-
</Slot>
|
|
254
|
-
<
|
|
323
|
+
</x.Slot>
|
|
324
|
+
<x.PackEnd><GtkMenuButton iconName="open-menu-symbolic" /></x.PackEnd>
|
|
255
325
|
</GtkHeaderBar>
|
|
256
326
|
```
|
|
257
327
|
|
|
@@ -260,8 +330,8 @@ Bottom action bar.
|
|
|
260
330
|
|
|
261
331
|
```tsx
|
|
262
332
|
<GtkActionBar>
|
|
263
|
-
<
|
|
264
|
-
<
|
|
333
|
+
<x.PackStart><GtkButton label="Cancel" /></x.PackStart>
|
|
334
|
+
<x.PackEnd><GtkButton label="Save" cssClasses={["suggested-action"]} /></x.PackEnd>
|
|
265
335
|
</GtkActionBar>
|
|
266
336
|
```
|
|
267
337
|
|
|
@@ -273,23 +343,23 @@ Bottom action bar.
|
|
|
273
343
|
|
|
274
344
|
```tsx
|
|
275
345
|
<GtkMenuButton iconName="open-menu-symbolic">
|
|
276
|
-
<Slot for={GtkMenuButton} id="popover">
|
|
346
|
+
<x.Slot for={GtkMenuButton} id="popover">
|
|
277
347
|
<GtkPopoverMenu>
|
|
278
|
-
<
|
|
279
|
-
<
|
|
280
|
-
<
|
|
281
|
-
</
|
|
282
|
-
<
|
|
283
|
-
<
|
|
284
|
-
<
|
|
285
|
-
<
|
|
286
|
-
</
|
|
287
|
-
</
|
|
288
|
-
<
|
|
289
|
-
<
|
|
290
|
-
</
|
|
348
|
+
<x.MenuSection>
|
|
349
|
+
<x.MenuItem id="new" label="New" onActivate={handleNew} accels="<Control>n" />
|
|
350
|
+
<x.MenuItem id="open" label="Open" onActivate={handleOpen} />
|
|
351
|
+
</x.MenuSection>
|
|
352
|
+
<x.MenuSection>
|
|
353
|
+
<x.MenuSubmenu label="Export">
|
|
354
|
+
<x.MenuItem id="pdf" label="PDF" onActivate={exportPdf} />
|
|
355
|
+
<x.MenuItem id="csv" label="CSV" onActivate={exportCsv} />
|
|
356
|
+
</x.MenuSubmenu>
|
|
357
|
+
</x.MenuSection>
|
|
358
|
+
<x.MenuSection>
|
|
359
|
+
<x.MenuItem id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
|
|
360
|
+
</x.MenuSection>
|
|
291
361
|
</GtkPopoverMenu>
|
|
292
|
-
</Slot>
|
|
362
|
+
</x.Slot>
|
|
293
363
|
</GtkMenuButton>
|
|
294
364
|
```
|
|
295
365
|
|
|
@@ -306,7 +376,7 @@ Bottom action bar.
|
|
|
306
376
|
title="App"
|
|
307
377
|
defaultWidth={800}
|
|
308
378
|
defaultHeight={600}
|
|
309
|
-
|
|
379
|
+
onClose={quit}
|
|
310
380
|
>
|
|
311
381
|
<Content />
|
|
312
382
|
</GtkApplicationWindow>
|
|
@@ -315,9 +385,9 @@ Bottom action bar.
|
|
|
315
385
|
Custom titlebar:
|
|
316
386
|
```tsx
|
|
317
387
|
<GtkApplicationWindow ...>
|
|
318
|
-
<Slot for={GtkWindow} id="titlebar">
|
|
388
|
+
<x.Slot for={GtkWindow} id="titlebar">
|
|
319
389
|
<GtkHeaderBar />
|
|
320
|
-
</Slot>
|
|
390
|
+
</x.Slot>
|
|
321
391
|
<Content />
|
|
322
392
|
</GtkApplicationWindow>
|
|
323
393
|
```
|
|
@@ -332,19 +402,19 @@ Import: `import * as Adw from "@gtkx/ffi/adw";`
|
|
|
332
402
|
Modern app structure.
|
|
333
403
|
|
|
334
404
|
```tsx
|
|
335
|
-
<AdwApplicationWindow title="App" defaultWidth={800} defaultHeight={600}
|
|
405
|
+
<AdwApplicationWindow title="App" defaultWidth={800} defaultHeight={600} onClose={quit}>
|
|
336
406
|
<AdwToolbarView>
|
|
337
|
-
<
|
|
407
|
+
<x.ToolbarTop>
|
|
338
408
|
<AdwHeaderBar>
|
|
339
|
-
<Slot for={AdwHeaderBar} id="titleWidget">
|
|
409
|
+
<x.Slot for={AdwHeaderBar} id="titleWidget">
|
|
340
410
|
<AdwWindowTitle title="App" subtitle="Description" />
|
|
341
|
-
</Slot>
|
|
411
|
+
</x.Slot>
|
|
342
412
|
</AdwHeaderBar>
|
|
343
|
-
</
|
|
413
|
+
</x.ToolbarTop>
|
|
344
414
|
<MainContent />
|
|
345
|
-
<
|
|
415
|
+
<x.ToolbarBottom>
|
|
346
416
|
<GtkActionBar />
|
|
347
|
-
</
|
|
417
|
+
</x.ToolbarBottom>
|
|
348
418
|
</AdwToolbarView>
|
|
349
419
|
</AdwApplicationWindow>
|
|
350
420
|
```
|
|
@@ -373,24 +443,36 @@ Settings UI.
|
|
|
373
443
|
<AdwPreferencesGroup title="Appearance" description="Customize look">
|
|
374
444
|
<AdwSwitchRow title="Dark Mode" active={dark} onActivated={() => setDark(!dark)} />
|
|
375
445
|
<AdwActionRow title="Theme" subtitle="Select color">
|
|
376
|
-
<
|
|
446
|
+
<x.ActionRowPrefix>
|
|
447
|
+
<GtkImage iconName="preferences-color-symbolic" />
|
|
448
|
+
</x.ActionRowPrefix>
|
|
449
|
+
<x.ActionRowSuffix>
|
|
377
450
|
<GtkImage iconName="go-next-symbolic" valign={Gtk.Align.CENTER} />
|
|
378
|
-
</
|
|
451
|
+
</x.ActionRowSuffix>
|
|
379
452
|
</AdwActionRow>
|
|
380
453
|
</AdwPreferencesGroup>
|
|
381
454
|
</AdwPreferencesPage>
|
|
382
455
|
```
|
|
383
456
|
|
|
457
|
+
**ActionRow children:** Use `x.ActionRowPrefix` for left widgets, `x.ActionRowSuffix` for right widgets, or `x.Slot for={AdwActionRow} id="activatableWidget"` for clickable suffix.
|
|
458
|
+
|
|
384
459
|
### AdwExpanderRow
|
|
385
|
-
Expandable settings row.
|
|
460
|
+
Expandable settings row with optional action widget.
|
|
386
461
|
|
|
387
462
|
```tsx
|
|
388
463
|
<AdwExpanderRow title="Advanced" subtitle="More options">
|
|
389
|
-
<
|
|
390
|
-
|
|
464
|
+
<x.ExpanderRowAction>
|
|
465
|
+
<GtkButton iconName="emblem-system-symbolic" cssClasses={["flat"]} />
|
|
466
|
+
</x.ExpanderRowAction>
|
|
467
|
+
<x.ExpanderRowRow>
|
|
468
|
+
<AdwSwitchRow title="Option 1" active />
|
|
469
|
+
<AdwSwitchRow title="Option 2" active={false} />
|
|
470
|
+
</x.ExpanderRowRow>
|
|
391
471
|
</AdwExpanderRow>
|
|
392
472
|
```
|
|
393
473
|
|
|
474
|
+
**ExpanderRow slots:** `x.ExpanderRowRow` for nested rows, `x.ExpanderRowAction` for header action widget. Direct children also work for simple cases.
|
|
475
|
+
|
|
394
476
|
### AdwEntryRow / AdwPasswordEntryRow
|
|
395
477
|
Input in list row.
|
|
396
478
|
|
|
@@ -399,6 +481,37 @@ Input in list row.
|
|
|
399
481
|
<AdwPasswordEntryRow title="Password" />
|
|
400
482
|
```
|
|
401
483
|
|
|
484
|
+
### AdwToggleGroup
|
|
485
|
+
Segmented button group for mutually exclusive options.
|
|
486
|
+
|
|
487
|
+
```tsx
|
|
488
|
+
<AdwToggleGroup active={viewMode} onToggled={setViewMode}>
|
|
489
|
+
<x.Toggle id="list" iconName="view-list-symbolic" tooltip="List view" />
|
|
490
|
+
<x.Toggle id="grid" iconName="view-grid-symbolic" tooltip="Grid view" />
|
|
491
|
+
<x.Toggle id="flow" label="Flow" />
|
|
492
|
+
</AdwToggleGroup>
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Toggle props:** `id` (optional), `label`, `iconName`, `tooltip`, `enabled`
|
|
496
|
+
|
|
497
|
+
### AdwNavigationView
|
|
498
|
+
Stack-based navigation with history.
|
|
499
|
+
|
|
500
|
+
```tsx
|
|
501
|
+
const [history, setHistory] = useState(["home"]);
|
|
502
|
+
|
|
503
|
+
<AdwNavigationView history={history} onHistoryChanged={setHistory}>
|
|
504
|
+
<x.NavigationPage id="home" title="Home">
|
|
505
|
+
<GtkButton label="Go to Details" onClicked={() => setHistory([...history, "details"])} />
|
|
506
|
+
</x.NavigationPage>
|
|
507
|
+
<x.NavigationPage id="details" title="Details" canPop>
|
|
508
|
+
<GtkLabel label="Details content" />
|
|
509
|
+
</x.NavigationPage>
|
|
510
|
+
</AdwNavigationView>
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
**NavigationPage props:** `id` (required), `title`, `canPop`. Control navigation via `history` array.
|
|
514
|
+
|
|
402
515
|
### Other Adwaita Widgets
|
|
403
516
|
|
|
404
517
|
| Widget | Description |
|
|
@@ -1,17 +1,32 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
1
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
3
2
|
import { GtkApplicationWindow, GtkBox, GtkButton, GtkLabel, quit } from "@gtkx/react";
|
|
3
|
+
import { useState } from "react";
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export const App = () => {
|
|
6
6
|
const [count, setCount] = useState(0);
|
|
7
7
|
|
|
8
8
|
return (
|
|
9
|
-
<GtkApplicationWindow title="<%= title %>" defaultWidth={400} defaultHeight={300}
|
|
10
|
-
<GtkBox
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
<GtkApplicationWindow title="<%= title %>" defaultWidth={400} defaultHeight={300} onClose={quit}>
|
|
10
|
+
<GtkBox
|
|
11
|
+
orientation={Gtk.Orientation.VERTICAL}
|
|
12
|
+
spacing={20}
|
|
13
|
+
marginTop={40}
|
|
14
|
+
marginBottom={40}
|
|
15
|
+
marginStart={40}
|
|
16
|
+
marginEnd={40}
|
|
17
|
+
valign={Gtk.Align.CENTER}
|
|
18
|
+
halign={Gtk.Align.CENTER}
|
|
19
|
+
>
|
|
20
|
+
<GtkLabel label="Welcome to GTKX!" cssClasses={["title-1"]} />
|
|
21
|
+
<GtkLabel label={`Count: ${count}`} cssClasses={["title-2"]} />
|
|
22
|
+
<GtkButton
|
|
23
|
+
label="Increment"
|
|
24
|
+
onClicked={() => setCount((c) => c + 1)}
|
|
25
|
+
cssClasses={["suggested-action", "pill"]}
|
|
26
|
+
/>
|
|
14
27
|
</GtkBox>
|
|
15
28
|
</GtkApplicationWindow>
|
|
16
29
|
);
|
|
17
|
-
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default App;
|