@gtkx/cli 0.11.0 → 0.12.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.
@@ -1,16 +1,16 @@
1
1
  ---
2
2
  name: developing-gtkx-apps
3
- description: Build GTK4 desktop applications with GTKX React framework. Use when creating GTKX components, working with GTK widgets, handling signals, or building Linux desktop UIs with React.
3
+ description: Build GTK4 desktop applications with GTKX React framework. Use when creating React components that render as native GTK widgets, working with GTK4/Libadwaita UI, handling signals, virtual lists, menus, or building Linux desktop UIs.
4
4
  ---
5
5
 
6
6
  # Developing GTKX Applications
7
7
 
8
- GTKX is a React framework for building native GTK4 desktop applications on Linux. It uses a custom React reconciler to render React components as native GTK widgets.
8
+ GTKX renders React components as native GTK4 widgets through a Rust FFI bridge.
9
9
 
10
10
  ## Quick Start
11
11
 
12
12
  ```tsx
13
- import { GtkApplicationWindow, GtkBox, GtkButton, GtkLabel, render, quit } from "@gtkx/react";
13
+ import { GtkApplicationWindow, GtkBox, GtkButton, render, quit } from "@gtkx/react";
14
14
  import * as Gtk from "@gtkx/ffi/gtk";
15
15
 
16
16
  const App = () => (
@@ -25,346 +25,56 @@ const App = () => (
25
25
  render(<App />, "com.example.myapp");
26
26
  ```
27
27
 
28
- ## Widget Patterns
28
+ ## Essential Patterns
29
29
 
30
- ### Container Widgets
30
+ ### Layout
31
31
 
32
- **GtkBox** - Linear layout:
33
32
  ```tsx
34
33
  <GtkBox orientation={Gtk.Orientation.VERTICAL} spacing={12}>
35
- First
36
- Second
34
+ <GtkLabel label="Title" />
35
+ <GtkButton label="Click" onClicked={handleClick} />
37
36
  </GtkBox>
38
37
  ```
39
38
 
40
- **GtkGrid** - 2D positioning:
41
- ```tsx
42
- <GtkGrid rowSpacing={10} columnSpacing={10}>
43
- <GridChild column={0} row={0}>Top-left</GridChild>
44
- <GridChild column={1} row={0} columnSpan={2}>Spans 2 columns</GridChild>
45
- </GtkGrid>
46
- ```
47
-
48
- **GtkStack** - Page-based container:
49
- ```tsx
50
- <GtkStack visibleChildName="page1">
51
- <StackPage name="page1" title="Page 1">Content 1</StackPage>
52
- <StackPage name="page2" title="Page 2">Content 2</StackPage>
53
- </GtkStack>
54
- ```
55
-
56
- **GtkNotebook** - Tabbed container:
57
- ```tsx
58
- <GtkNotebook>
59
- <Notebook.Page label="Tab 1">
60
- <Content1 />
61
- </Notebook.Page>
62
- <Notebook.Page label="Tab 2">
63
- <Content2 />
64
- </Notebook.Page>
65
- </GtkNotebook>
66
- ```
67
-
68
- **GtkPaned** - Resizable split:
69
- ```tsx
70
- <GtkPaned orientation={Gtk.Orientation.HORIZONTAL} position={280}>
71
- <Slot for={GtkPaned} id="startChild">
72
- <SideBar />
73
- </Slot>
74
- <Slot for={GtkPaned} id="endChild">
75
- <MainContent />
76
- </Slot>
77
- </GtkPaned>
78
- ```
79
-
80
- ### Virtual Scrolling Lists
81
-
82
- **ListView** - High-performance scrollable list with selection:
83
- ```tsx
84
- <ListView<Item>
85
- vexpand
86
- selected={[selectedId]}
87
- selectionMode={Gtk.SelectionMode.SINGLE}
88
- onSelectionChanged={(ids) => setSelectedId(ids[0])}
89
- renderItem={(item) => (
90
- <GtkLabel label={item?.text ?? ""} />
91
- )}
92
- >
93
- {items.map(item => (
94
- <ListItem key={item.id} id={item.id} value={item} />
95
- ))}
96
- </ListView>
97
- ```
98
-
99
- **GridView** - Grid-based virtual scrolling:
100
- ```tsx
101
- <GridView<Item>
102
- vexpand
103
- minColumns={2}
104
- maxColumns={4}
105
- renderItem={(item) => (
106
- <GtkBox orientation={Gtk.Orientation.VERTICAL}>
107
- <GtkImage iconName={item?.icon ?? "image-missing"} />
108
- <GtkLabel label={item?.name ?? ""} />
109
- </GtkBox>
110
- )}
111
- >
112
- {items.map(item => (
113
- <ListItem key={item.id} id={item.id} value={item} />
114
- ))}
115
- </GridView>
116
- ```
117
-
118
- **GtkColumnView** - Table with sortable columns:
119
- ```tsx
120
- <GtkColumnView
121
- sortColumn="name"
122
- sortOrder={Gtk.SortType.ASCENDING}
123
- onSortChange={handleSort}
124
- >
125
- <ColumnViewColumn<Item>
126
- title="Name"
127
- id="name"
128
- expand
129
- sortable
130
- renderCell={(item) => (
131
- <GtkLabel label={item?.name ?? ""} />
132
- )}
133
- />
134
- {items.map(item => (
135
- <ListItem key={item.id} id={item.id} value={item} />
136
- ))}
137
- </GtkColumnView>
138
- ```
139
-
140
- **GtkDropDown** - String selection dropdown with controlled state:
141
- ```tsx
142
- <GtkDropDown selectedId={selectedId} onSelectionChanged={setSelectedId}>
143
- {options.map(opt => (
144
- <SimpleListItem key={opt.id} id={opt.id} value={opt.label} />
145
- ))}
146
- </GtkDropDown>
147
- ```
148
-
149
- **GtkOverlay** - Stack widgets on top of each other (first child is base):
150
- ```tsx
151
- <GtkOverlay>
152
- <GtkButton label="Notifications" />
153
- <GtkLabel
154
- label="3"
155
- cssClasses={["badge"]}
156
- halign={Gtk.Align.END}
157
- valign={Gtk.Align.START}
158
- />
159
- </GtkOverlay>
160
- ```
161
-
162
- ### GtkHeaderBar
163
-
164
- Pack widgets at start and end of the title bar using Slot or Pack components:
165
- ```tsx
166
- <GtkHeaderBar>
167
- <Pack.Start>
168
- <GtkButton iconName="go-previous-symbolic" />
169
- </Pack.Start>
170
- <Slot for={GtkHeaderBar} id="titleWidget">
171
- <GtkLabel label="My App" cssClasses={["title"]} />
172
- </Slot>
173
- <Pack.End>
174
- <GtkMenuButton iconName="open-menu-symbolic" />
175
- </Pack.End>
176
- </GtkHeaderBar>
177
- ```
178
-
179
- ### GtkActionBar
180
-
181
- Bottom bar with packed widgets:
182
- ```tsx
183
- <GtkActionBar>
184
- <Pack.Start>
185
- <GtkButton label="Cancel" />
186
- </Pack.Start>
187
- <Pack.End>
188
- <GtkButton label="Save" cssClasses={["suggested-action"]} />
189
- </Pack.End>
190
- </GtkActionBar>
191
- ```
192
-
193
39
  ### Controlled Input
194
40
 
195
- GtkEntry requires two-way binding:
196
41
  ```tsx
197
42
  const [text, setText] = useState("");
198
-
199
- <GtkEntry
200
- text={text}
201
- onChanged={(entry) => setText(entry.getText())}
202
- placeholderText="Type here..."
203
- />
43
+ <GtkEntry text={text} onChanged={(e) => setText(e.getText())} />
204
44
  ```
205
45
 
206
- ### Declarative Menus
46
+ ### Signals
207
47
 
208
- ```tsx
209
- <GtkPopoverMenu>
210
- <Menu.Section>
211
- <Menu.Item
212
- id="new"
213
- label="New"
214
- onActivate={handleNew}
215
- accels="<Control>n"
216
- />
217
- </Menu.Section>
218
- <Menu.Section>
219
- <Menu.Submenu label="Export">
220
- <Menu.Item id="pdf" label="PDF" onActivate={handleExportPdf} />
221
- <Menu.Item id="csv" label="CSV" onActivate={handleExportCsv} />
222
- </Menu.Submenu>
223
- </Menu.Section>
224
- <Menu.Section>
225
- <Menu.Item id="quit" label="Quit" onActivate={quit} accels="<Control>q" />
226
- </Menu.Section>
227
- </GtkPopoverMenu>
228
- ```
229
-
230
- ## Signal Handling
48
+ GTK signals map to `on<SignalName>` props: `clicked` → `onClicked`, `toggled` → `onToggled`.
231
49
 
232
- GTK signals map to `on<SignalName>` props:
233
- - `clicked` → `onClicked`
234
- - `toggled` → `onToggled`
235
- - `changed` → `onChanged`
236
- - `notify::selected` → `onNotify` (with property name arg)
50
+ ### Widget Slots
237
51
 
238
- ## Widget References
52
+ Some widgets require children in specific slots:
239
53
 
240
54
  ```tsx
241
- import { useRef } from "react";
242
-
243
- const entryRef = useRef<Gtk.Entry | null>(null);
244
- <GtkEntry ref={entryRef} />
245
- ```
246
-
247
- ## Portals
248
-
249
- ```tsx
250
- import { createPortal } from "@gtkx/react";
251
-
252
- {createPortal(<GtkAboutDialog programName="My App" />)}
253
- ```
254
-
255
- ## Adwaita (Libadwaita) Widgets
256
-
257
- Import Adwaita widgets and enums:
258
- ```tsx
259
- import * as Adw from "@gtkx/ffi/adw";
260
- import { AdwApplicationWindow, AdwHeaderBar, AdwToolbarView, Toolbar } from "@gtkx/react";
261
- ```
262
-
263
- ### AdwToolbarView
264
- Modern app layout with top/bottom bars:
265
- ```tsx
266
- <AdwToolbarView>
267
- <Toolbar.Top>
268
- <AdwHeaderBar>
269
- <Slot for={AdwHeaderBar} id="titleWidget">
270
- <AdwWindowTitle title="App" subtitle="Description" />
271
- </Slot>
272
- </AdwHeaderBar>
273
- </Toolbar.Top>
274
- <MainContent />
275
- <Toolbar.Bottom>
276
- <GtkActionBar>...</GtkActionBar>
277
- </Toolbar.Bottom>
278
- </AdwToolbarView>
279
- ```
280
-
281
- ### AdwApplicationWindow
282
- Modern application window:
283
- ```tsx
284
- <AdwApplicationWindow title="My App" defaultWidth={800} defaultHeight={600} onCloseRequest={quit}>
285
- <AdwToolbarView>...</AdwToolbarView>
286
- </AdwApplicationWindow>
287
- ```
288
-
289
- ### AdwStatusPage
290
- Welcome or empty state pages:
291
- ```tsx
292
- <AdwStatusPage
293
- iconName="applications-system-symbolic"
294
- title="Welcome"
295
- description="Get started with your app"
296
- vexpand
297
- />
298
- ```
299
-
300
- ### AdwBanner
301
- Dismissable notification banner:
302
- ```tsx
303
- <AdwBanner
304
- title="Update available!"
305
- buttonLabel="Dismiss"
306
- revealed={showBanner}
307
- onButtonClicked={() => setShowBanner(false)}
308
- />
309
- ```
310
-
311
- ### AdwPreferencesPage / AdwPreferencesGroup
312
- Settings UI layout:
313
- ```tsx
314
- <AdwPreferencesPage title="Settings">
315
- <AdwPreferencesGroup title="Appearance" description="Customize the look">
316
- <AdwSwitchRow title="Dark Mode" active={dark} onActivate={() => setDark(!dark)} />
317
- <AdwActionRow title="Theme" subtitle="Select color theme">
318
- <Slot for={AdwActionRow} id="activatableWidget">
319
- <GtkImage iconName="go-next-symbolic" />
320
- </Slot>
321
- </AdwActionRow>
322
- </AdwPreferencesGroup>
323
- </AdwPreferencesPage>
324
- ```
325
-
326
- ### AdwExpanderRow
327
- Expandable row with nested content:
328
- ```tsx
329
- <AdwExpanderRow title="Advanced" subtitle="More options">
330
- <AdwSwitchRow title="Option 1" active />
331
- <AdwSwitchRow title="Option 2" active={false} />
332
- </AdwExpanderRow>
333
- ```
334
-
335
- ### AdwEntryRow / AdwPasswordEntryRow
336
- Input fields in list rows:
337
- ```tsx
338
- <AdwEntryRow title="Username" text={username} onChanged={(e) => setUsername(e.getText())} />
339
- <AdwPasswordEntryRow title="Password" />
55
+ <GtkPaned orientation={Gtk.Orientation.HORIZONTAL}>
56
+ <Slot for={GtkPaned} id="startChild"><Sidebar /></Slot>
57
+ <Slot for={GtkPaned} id="endChild"><Content /></Slot>
58
+ </GtkPaned>
340
59
  ```
341
60
 
342
- ### AdwClamp
343
- Limit content width for readability:
344
- ```tsx
345
- <AdwClamp maximumSize={600}>
346
- <GtkBox>...</GtkBox>
347
- </AdwClamp>
348
- ```
61
+ ### Packing (HeaderBar, ActionBar)
349
62
 
350
- ### AdwAvatar
351
- User avatar with initials fallback:
352
63
  ```tsx
353
- <AdwAvatar size={48} text="John Doe" showInitials />
64
+ <GtkHeaderBar>
65
+ <Pack.Start><GtkButton iconName="go-previous-symbolic" /></Pack.Start>
66
+ <Pack.End><GtkMenuButton iconName="open-menu-symbolic" /></Pack.End>
67
+ </GtkHeaderBar>
354
68
  ```
355
69
 
356
- ### AdwSpinner
357
- Loading indicator:
358
- ```tsx
359
- {isLoading && <AdwSpinner widthRequest={32} heightRequest={32} />}
360
- ```
70
+ ## Key Constraints
361
71
 
362
- ## Constraints
72
+ - GTK is single-threaded: all widget operations on main thread
73
+ - GtkEntry requires two-way binding with `onChanged`
74
+ - Virtual lists need stable object references (immutable data patterns)
75
+ - Use `quit` from `@gtkx/react` to close the application
363
76
 
364
- - **GTK is single-threaded**: All widget operations on main thread
365
- - **Virtual lists need immutable data**: Use stable object references
366
- - **GtkToggleButton auto-prevents feedback loops**: Safe for controlled state
367
- - **GtkEntry needs two-way binding**: Use `onChanged` to sync state
77
+ ## References
368
78
 
369
- For detailed widget reference, see [WIDGETS.md](WIDGETS.md).
370
- For code examples, see [EXAMPLES.md](EXAMPLES.md).
79
+ For complete widget API, see [WIDGETS.md](WIDGETS.md).
80
+ For code patterns and examples, see [EXAMPLES.md](EXAMPLES.md).