@gtkx/cli 0.9.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,364 @@
1
+ # GTKX Code Examples
2
+
3
+ ## Application Structure
4
+
5
+ ### Basic App with State
6
+
7
+ ```tsx
8
+ import * as Gtk from "@gtkx/ffi/gtk";
9
+ import { GtkApplicationWindow, GtkBox, GtkLabel, quit } from "@gtkx/react";
10
+ import { useCallback, useState } from "react";
11
+
12
+ interface Todo {
13
+ id: number;
14
+ text: string;
15
+ completed: boolean;
16
+ }
17
+
18
+ let nextId = 1;
19
+
20
+ export const App = () => {
21
+ const [todos, setTodos] = useState<Todo[]>([]);
22
+
23
+ const addTodo = useCallback((text: string) => {
24
+ setTodos((prev) => [...prev, { id: nextId++, text, completed: false }]);
25
+ }, []);
26
+
27
+ const toggleTodo = useCallback((id: number) => {
28
+ setTodos((prev) =>
29
+ prev.map((todo) =>
30
+ todo.id === id ? { ...todo, completed: !todo.completed } : todo
31
+ )
32
+ );
33
+ }, []);
34
+
35
+ return (
36
+ <GtkApplicationWindow title="Todo App" defaultWidth={400} defaultHeight={500} onCloseRequest={quit}>
37
+ <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={16} marginTop={16} marginStart={16} marginEnd={16}>
38
+ Todo App
39
+ </GtkBox>
40
+ </GtkApplicationWindow>
41
+ );
42
+ };
43
+
44
+ export const appId = "com.gtkx.todo";
45
+ ```
46
+
47
+ ## Layout Patterns
48
+
49
+ ### Grid for Forms
50
+
51
+ ```tsx
52
+ import * as Gtk from "@gtkx/ffi/gtk";
53
+ import { GtkButton, GtkEntry, GtkGrid, GtkLabel, GridChild } from "@gtkx/react";
54
+ import { useState } from "react";
55
+
56
+ const FormLayout = () => {
57
+ const [name, setName] = useState("");
58
+ const [email, setEmail] = useState("");
59
+
60
+ return (
61
+ <GtkGrid rowSpacing={8} columnSpacing={12}>
62
+ <GridChild column={0} row={0}>
63
+ <GtkLabel label="Name:" halign={Gtk.Align.END} />
64
+ </GridChild>
65
+ <GridChild column={1} row={0}>
66
+ <GtkEntry text={name} onChanged={(e) => setName(e.getText())} hexpand />
67
+ </GridChild>
68
+ <GridChild column={0} row={1}>
69
+ <GtkLabel label="Email:" halign={Gtk.Align.END} />
70
+ </GridChild>
71
+ <GridChild column={1} row={1}>
72
+ <GtkEntry text={email} onChanged={(e) => setEmail(e.getText())} hexpand />
73
+ </GridChild>
74
+ <GridChild column={0} row={2} columnSpan={2}>
75
+ <GtkButton label="Submit" halign={Gtk.Align.END} marginTop={8} />
76
+ </GridChild>
77
+ </GtkGrid>
78
+ );
79
+ };
80
+ ```
81
+
82
+ ### Stack with StackSwitcher
83
+
84
+ ```tsx
85
+ import * as Gtk from "@gtkx/ffi/gtk";
86
+ import { GtkBox, GtkLabel, GtkStack, GtkStackSwitcher, StackPage } from "@gtkx/react";
87
+
88
+ const TabContainer = () => (
89
+ <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={8}>
90
+ <GtkStackSwitcher
91
+ ref={(switcher: Gtk.StackSwitcher | null) => {
92
+ if (switcher) {
93
+ const stack = switcher.getParent()?.getLastChild() as Gtk.Stack | null;
94
+ if (stack) switcher.setStack(stack);
95
+ }
96
+ }}
97
+ />
98
+ <GtkStack transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT} transitionDuration={200}>
99
+ <StackPage name="page1" title="First">First Page Content</StackPage>
100
+ <StackPage name="page2" title="Second">Second Page Content</StackPage>
101
+ </GtkStack>
102
+ </GtkBox>
103
+ );
104
+ ```
105
+
106
+ ## Virtual Scrolling Lists
107
+
108
+ ### ListView with Selection
109
+
110
+ ```tsx
111
+ import * as Gtk from "@gtkx/ffi/gtk";
112
+ import { GtkBox, GtkLabel, GtkScrolledWindow, ListView, ListItem } from "@gtkx/react";
113
+ import { useState } from "react";
114
+
115
+ interface Task {
116
+ id: string;
117
+ title: string;
118
+ completed: boolean;
119
+ }
120
+
121
+ const tasks: Task[] = [
122
+ { id: "1", title: "Learn GTK4", completed: true },
123
+ { id: "2", title: "Build React app", completed: false },
124
+ ];
125
+
126
+ const TaskList = () => {
127
+ const [selectedId, setSelectedId] = useState<string | undefined>();
128
+
129
+ return (
130
+ <GtkBox cssClasses={["card"]} heightRequest={250}>
131
+ <GtkScrolledWindow vexpand>
132
+ <ListView<Task>
133
+ vexpand
134
+ selected={selectedId ? [selectedId] : []}
135
+ onSelectionChanged={(ids) => setSelectedId(ids[0])}
136
+ renderItem={(task) => (
137
+ <GtkLabel
138
+ label={task?.title ?? ""}
139
+ cssClasses={task?.completed ? ["dim-label"] : []}
140
+ halign={Gtk.Align.START}
141
+ marginStart={12}
142
+ marginTop={8}
143
+ marginBottom={8}
144
+ />
145
+ )}
146
+ >
147
+ {tasks.map((task) => (
148
+ <ListItem key={task.id} id={task.id} value={task} />
149
+ ))}
150
+ </ListView>
151
+ </GtkScrolledWindow>
152
+ </GtkBox>
153
+ );
154
+ };
155
+ ```
156
+
157
+ ### HeaderBar with Navigation
158
+
159
+ ```tsx
160
+ import * as Gtk from "@gtkx/ffi/gtk";
161
+ import { GtkApplicationWindow, GtkBox, GtkButton, GtkHeaderBar, GtkLabel, GtkWindow, Pack, Slot, quit } from "@gtkx/react";
162
+ import { useState } from "react";
163
+
164
+ const AppWithHeaderBar = () => {
165
+ const [page, setPage] = useState("home");
166
+
167
+ return (
168
+ <GtkApplicationWindow title="My App" defaultWidth={600} defaultHeight={400} onCloseRequest={quit}>
169
+ <Slot for={GtkWindow} id="titlebar">
170
+ <GtkHeaderBar>
171
+ <Pack.Start>
172
+ {page !== "home" && (
173
+ <GtkButton iconName="go-previous-symbolic" onClicked={() => setPage("home")} />
174
+ )}
175
+ </Pack.Start>
176
+ <Pack.End>
177
+ <GtkButton iconName="emblem-system-symbolic" onClicked={() => setPage("settings")} />
178
+ </Pack.End>
179
+ </GtkHeaderBar>
180
+ </Slot>
181
+ <GtkLabel label={page === "home" ? "Home Page" : "Settings Page"} vexpand />
182
+ </GtkApplicationWindow>
183
+ );
184
+ };
185
+ ```
186
+
187
+ ## Menus
188
+
189
+ ### MenuButton with PopoverMenu
190
+
191
+ ```tsx
192
+ import * as Gtk from "@gtkx/ffi/gtk";
193
+ import { GtkBox, GtkLabel, GtkMenuButton, GtkPopoverMenu, Menu, Slot } from "@gtkx/react";
194
+ import { useState } from "react";
195
+
196
+ const MenuDemo = () => {
197
+ const [lastAction, setLastAction] = useState<string | null>(null);
198
+
199
+ return (
200
+ <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
201
+ <GtkLabel label={`Last action: ${lastAction ?? "(none)"}`} />
202
+ <GtkMenuButton label="Actions">
203
+ <Slot for={GtkMenuButton} id="popover">
204
+ <GtkPopoverMenu>
205
+ <Menu.Item id="new" label="New" onActivate={() => setLastAction("New")} accels="<Control>n" />
206
+ <Menu.Item id="open" label="Open" onActivate={() => setLastAction("Open")} accels="<Control>o" />
207
+ <Menu.Item id="save" label="Save" onActivate={() => setLastAction("Save")} accels="<Control>s" />
208
+ </GtkPopoverMenu>
209
+ </Slot>
210
+ </GtkMenuButton>
211
+ </GtkBox>
212
+ );
213
+ };
214
+ ```
215
+
216
+ ## Component Props Pattern
217
+
218
+ ### List Item Component
219
+
220
+ ```tsx
221
+ import * as Gtk from "@gtkx/ffi/gtk";
222
+ import { GtkBox, GtkButton, GtkCheckButton, GtkLabel } from "@gtkx/react";
223
+
224
+ interface Todo {
225
+ id: number;
226
+ text: string;
227
+ completed: boolean;
228
+ }
229
+
230
+ interface TodoItemProps {
231
+ todo: Todo;
232
+ onToggle: (id: number) => void;
233
+ onDelete: (id: number) => void;
234
+ }
235
+
236
+ export const TodoItem = ({ todo, onToggle, onDelete }: TodoItemProps) => (
237
+ <GtkBox orientation={Gtk.Orientation.HORIZONTAL} spacing={8}>
238
+ <GtkCheckButton active={todo.completed} onToggled={() => onToggle(todo.id)} />
239
+ <GtkLabel label={todo.text} hexpand cssClasses={todo.completed ? ["dim-label"] : []} />
240
+ <GtkButton iconName="edit-delete-symbolic" onClicked={() => onDelete(todo.id)} cssClasses={["flat"]} />
241
+ </GtkBox>
242
+ );
243
+ ```
244
+
245
+ ## Adwaita App Structure
246
+
247
+ ### Modern Adwaita App
248
+
249
+ ```tsx
250
+ import * as Gtk from "@gtkx/ffi/gtk";
251
+ import {
252
+ AdwApplicationWindow,
253
+ AdwHeaderBar,
254
+ AdwToolbarView,
255
+ AdwWindowTitle,
256
+ AdwStatusPage,
257
+ AdwBanner,
258
+ GtkButton,
259
+ Slot,
260
+ Toolbar,
261
+ quit,
262
+ } from "@gtkx/react";
263
+ import { useState } from "react";
264
+
265
+ export const App = () => {
266
+ const [showBanner, setShowBanner] = useState(true);
267
+
268
+ return (
269
+ <AdwApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onCloseRequest={quit}>
270
+ <Slot for={AdwApplicationWindow} id="content">
271
+ <AdwToolbarView>
272
+ <Toolbar.Top>
273
+ <AdwHeaderBar>
274
+ <Slot for={AdwHeaderBar} id="titleWidget">
275
+ <AdwWindowTitle title="My App" subtitle="Welcome" />
276
+ </Slot>
277
+ </AdwHeaderBar>
278
+ </Toolbar.Top>
279
+ <AdwBanner
280
+ title="Welcome to the app!"
281
+ buttonLabel="Dismiss"
282
+ revealed={showBanner}
283
+ onButtonClicked={() => setShowBanner(false)}
284
+ />
285
+ <AdwStatusPage
286
+ iconName="applications-system-symbolic"
287
+ title="Welcome"
288
+ description="Get started with your new GTKX app"
289
+ vexpand
290
+ >
291
+ <GtkButton
292
+ label="Get Started"
293
+ cssClasses={["suggested-action", "pill"]}
294
+ halign={Gtk.Align.CENTER}
295
+ />
296
+ </AdwStatusPage>
297
+ </AdwToolbarView>
298
+ </Slot>
299
+ </AdwApplicationWindow>
300
+ );
301
+ };
302
+
303
+ export const appId = "com.example.myapp";
304
+ ```
305
+
306
+ ### Settings Page with Preferences
307
+
308
+ ```tsx
309
+ import * as Gtk from "@gtkx/ffi/gtk";
310
+ import {
311
+ AdwPreferencesPage,
312
+ AdwPreferencesGroup,
313
+ AdwActionRow,
314
+ AdwSwitchRow,
315
+ AdwExpanderRow,
316
+ AdwEntryRow,
317
+ GtkImage,
318
+ GtkScrolledWindow,
319
+ Slot,
320
+ } from "@gtkx/react";
321
+ import { useState } from "react";
322
+
323
+ const SettingsPage = () => {
324
+ const [darkMode, setDarkMode] = useState(false);
325
+ const [notifications, setNotifications] = useState(true);
326
+ const [username, setUsername] = useState("");
327
+
328
+ return (
329
+ <GtkScrolledWindow vexpand>
330
+ <AdwPreferencesPage title="Settings">
331
+ <AdwPreferencesGroup title="Appearance" description="Customize the look and feel">
332
+ <AdwSwitchRow
333
+ title="Dark Mode"
334
+ subtitle="Use dark color scheme"
335
+ active={darkMode}
336
+ onActivate={() => setDarkMode(!darkMode)}
337
+ />
338
+ </AdwPreferencesGroup>
339
+
340
+ <AdwPreferencesGroup title="Account">
341
+ <AdwEntryRow
342
+ title="Username"
343
+ text={username}
344
+ onChanged={(e) => setUsername(e.getText())}
345
+ />
346
+ <AdwActionRow title="Profile" subtitle="Manage your profile">
347
+ <Slot for={AdwActionRow} id="activatableWidget">
348
+ <GtkImage iconName="go-next-symbolic" valign={Gtk.Align.CENTER} />
349
+ </Slot>
350
+ </AdwActionRow>
351
+ </AdwPreferencesGroup>
352
+
353
+ <AdwPreferencesGroup title="Notifications">
354
+ <AdwExpanderRow title="Notification Settings" subtitle="Configure alerts">
355
+ <AdwSwitchRow title="Sound" active />
356
+ <AdwSwitchRow title="Badges" active />
357
+ <AdwSwitchRow title="Lock Screen" active={false} />
358
+ </AdwExpanderRow>
359
+ </AdwPreferencesGroup>
360
+ </AdwPreferencesPage>
361
+ </GtkScrolledWindow>
362
+ );
363
+ };
364
+ ```