-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwidgetkit.scrbl
More file actions
388 lines (291 loc) · 13 KB
/
Copy pathwidgetkit.scrbl
File metadata and controls
388 lines (291 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
#lang scribble/manual
@(require (for-label racket/base
racket/class
racket/gui/base
widgetkit))
@title{widgetkit}
@author{turinglambdaai}
@defmodule[widgetkit]
widgetkit is a curated collection of GUI widgets for Racket. It gathers the
controls that almost every @racketmodname[racket/gui] application wants but
that the core toolkit leaves you to build yourself — tooltips and placeholder
text, grid layout, date entry, virtualized lists, status bars, spinners and
steppers — behind a single @racket[(require widgetkit)], with one manual and a
runnable example per widget.
The design follows two rules:
@itemize[
@item{@bold{Curated, not redundant.} Every widget fills a gap that core
@racketmodname[racket/gui] leaves open. If the toolkit already does the
job well, the widget is not here.}
@item{@bold{Reuse over rewrite.} When a mature upstream package already does
the job, widgetkit depends on it and re-exports it; it does not fork. New
code is written only where no good solution exists.}
]
The collection has two layers:
@itemize[
@item{@defterm{Gap-filling widgets} (new, MIT, in this repo):
@racket[status-bar%], @racket[spinner%], @racket[stepper%] (plus the
@racket[clamp] helper).}
@item{@defterm{Aggregated widgets} (re-exported from mature upstream
packages): the @racket[cue-mixin], @racket[tooltip-mixin] and
@racket[validate-mixin] text-field enhancers, @racket[table-panel%],
@racket[canvas-list%] and @racket[date-text-field%].}
]
Larger, heavier-dependency controls (maps, sortable data grids, plots, a web
view, a tree) are listed in @secref["companions"] — install them separately on
demand.
@section[#:tag "install"]{Installation}
From a clone of this repository:
@racketblock[
(raco pkg install)
]
Then, in any module:
@racketblock[
(require widgetkit)
]
@section{A quick tour}
Open @filepath{examples/showcase.rkt} for a single window that demonstrates
every widget, or run it directly:
@commandline{racket examples/showcase.rkt}
@section{Gap-filling widgets}
@subsection[#:tag "status-bar"]{status-bar%}
A compact bottom-of-window bar: a status message plus an optional determinate
progress gauge. Core @racket[gui] ships @racket[message%] and @racket[gauge%]
but no combined status bar, so this is boilerplate every app rewrites. It
subclasses @racket[horizontal-panel%], so extra children (e.g. a Cancel
button) can be appended.
Constructor:
@racketblock[
(new status-bar% [parent parent]
[initial-message ""]
[show-progress #f])
]
Methods: @racket[(send bar set-message text)],
@racket[(send bar set-progress percentage)] (0--100, ignored if the bar has no
gauge), @racket[(send bar get-message)], @racket[(send bar clear)].
@racketblock[
(define bar (new status-bar% [parent f] [show-progress #t]
[initial-message "Ready."]))
(send bar set-message "Working...")
(send bar set-progress 75)
]
@subsection[#:tag "spinner"]{spinner%}
An indeterminate circular activity indicator. Core @racket[gui] only ships the
determinate @racket[gauge%]; there is no ``busy, unknown duration'' control.
@racket[spinner%] draws a rotating arc on a canvas driven by a timer.
Constructor:
@racketblock[
(new spinner% [parent parent]
[diameter 24] [color "dodgerblue"]
[track-color "lightgray"] [interval 60])
]
Methods: @racket[(send sp start)], @racket[(send sp stop)],
@racket[(send sp spinning?)].
@racketblock[
(define sp (new spinner% [parent f] [diameter 36]))
(send sp start) ; while work is in progress
;; ...later...
(send sp stop)
]
@subsection[#:tag "stepper"]{stepper%}
A compact @litchar{[-] value [+] } numeric stepper. Core @racket[gui] has
@racket[slider%] for picking from a range but no small +/- control for numeric
tweaks.
Constructor:
@racketblock[
(new stepper% [parent parent]
[min-value 0] [max-value 100] [step 1] [initial 0]
[callback (λ (self) (void))] [show-value #t])
]
Methods: @racket[(send st get-value)], @racket[(send st set-value v)],
@racket[(send st increment)], @racket[(send st decrement)]. Values are clamped
to @racket[[min-value, max-value]] using the exported @racket[clamp] helper.
@racketblock[
(new stepper% [parent f] [min-value 0] [max-value 12] [initial 1]
[callback (λ (self) (printf "qty: ~a\n" (send self get-value)))])
]
@subsection[#:tag "disclosure"]{disclosure%}
A collapsible section: a header button toggles the visibility of a content
panel. Add the collapsible children to the panel returned by @racket[get-content].
@racketblock[
(define d (new disclosure% [parent f] [label "Advanced options"] [expanded? #f]))
(new check-box% [parent (send d get-content)] [label "Verbose logging"])
]
Methods: @racket[(send d get-content)], @racket[(send d is-expanded?)],
@racket[(send d set-expanded! bool)].
@subsection[#:tag "image-view"]{image-view%}
A canvas that displays a @racket[bitmap%], centered and scaled to fit (or at a
fixed numeric scale). core @racket[gui] has @racket[canvas%] but no ready-made
widget to just show an image.
@racketblock[
(new image-view% [parent f] [bitmap some-bitmap%] [scale 'fit])
(define iv (new image-view% [parent f]))
(send iv load-file "photo.png")
]
Methods: @racket[(send iv set-bitmap b)], @racket[(send iv load-file path)],
@racket[(send iv get-bitmap)].
@subsection[#:tag "progress-dialog"]{progress-dialog%}
A modal dialog showing a message and a determinate gauge, with an optional
Cancel button. Drive it from a worker thread while @racket[(send pd show #t)]
runs the modal event loop; update the UI via @racket[queue-callback] and close
with @racket[(send pd show #f)]. See @filepath{examples/progress-dialog-demo.rkt}
for the full pattern.
@racketblock[
(define pd (new progress-dialog% [parent f] [label "Working..."]))
(void (thread
(λ ()
... (queue-callback (λ () (send pd set-progress n))) ...
(queue-callback (λ () (send pd show #f))))))
(send pd show #t)
]
Methods: @racket[(send pd set-progress n)], @racket[(send pd set-message s)],
@racket[(send pd cancelled?)].
@subsection[#:tag "notification-banner"]{notification-banner%}
A transient, dismissible message strip (a ``toast''/banner) with a severity
(info/success/warning/error), pinned to the top of a window. It collapses when
dismissed (click near its right edge) or after an auto-dismiss timeout. Use it
instead of a modal @racket[message-box] when you just want to flash a
non-blocking result.
@racketblock[
(define nb (new notification-banner% [parent f]))
(send nb show-message "Saved." 'success 3000)
(send nb show-message "Check input." 'warning #f)
]
Methods: @racket[(send nb show-message text severity auto-dismiss-ms)] (pass
@racket[#f] for @racket[auto-dismiss-ms] to keep it up), @racket[(send nb hide)],
@racket[(send nb current-message)].
@subsection[#:tag "log-view"]{log-view%}
A scrolling, read-only, monospace log/console output that stretches to fill its
parent, auto-scrolls to the newest line, accepts @racket[append-line] while
staying read-only, and trims old lines past @racket[max-lines]. Building this
from raw @racket[editor-canvas%] + @racket[text%] is where most people get
stuck (the canvas does not stretch inside a @racket[pane%]; auto-scroll and
read-only-with-programmatic-appends both need care).
@racketblock[
(define log (new log-view% [parent f] [max-lines 5000]))
(send log append-line "[boot] ready")
]
Methods: @racket[(send log append-line s)], @racket[(send log clear)],
@racket[(send log get-text)], @racket[(send log scroll-to-bottom)].
@subsection[#:tag "split-view"]{split-view%}
Two panes separated by a draggable divider (Qt's QSplitter / GTK's GtkPaned).
Add children to @racket[(send sv get-first)] and @racket[(send sv get-second)];
the divider is mouse-draggable; @racket[set-fraction] sets the first pane's
share in @racket[0..1].
@racketblock[
(new split-view% [parent f] [orientation 'horizontal] [fraction 0.4])
]
@subsection[#:tag "toolbar"]{toolbar%}
A fixed-height row of action buttons with separators. Callbacks are
no-argument thunks; @racket[add-button] returns the created @racket[button%]
and @racket[add-separator] the separator canvas. Any other widget can be added
with @racket[[parent tb]].
@racketblock[
(send tb add-button "Open" (λ () ...))
(send tb add-separator)
]
@subsection[#:tag "search-field"]{search-field%}
A ``Search…'' box firing a one-argument @racket[(λ (query) ...)] callback on
every keystroke and on clear.
@racketblock[
(new search-field% [parent f] [callback (λ (q) (filter-items q))])
]
@subsection[#:tag "stack"]{stack%}
Shows one of several pages at a time (QStackedWidget). Pair it with a
@racket[choice%] or @racket[tab-panel%] for working switched content; this
sidesteps the @racket[tab-panel%]-has-no-callback trap.
@racketblock[
(define pages (new stack% [parent f]))
(define p0 (send pages add-page))
(send pages show-page 0)
]
@section{Aggregated widgets}
These are re-exported from their upstream packages; see each package's own
documentation for the full API.
@subsection{Tooltips & cue text}
From the @hyperlink["https://github.com/alex-hhh/gui-widget-mixins"]{gui-widget-mixins}
package (Apache-2.0 OR MIT). Core @racket[gui] has no tooltips and no
placeholder text for @racket[text-field%].
@racketblock[
(new (cue-mixin "" (tooltip-mixin text-field%))
[parent f] [label "Name:"]
[cue "Enter your name"]
[tooltip "Your full name"])
]
@racket[cue-mixin] takes a default cue string and a base class;
@racket[tooltip-mixin] takes a base class. @racket[validate-mixin] adds a
validation callback. @racket[decorate-mixin] / @racket[decorate-with] compose
several enhancements.
@subsection{table-panel%}
From the @hyperlink["https://github.com/spdegabrielle/table-panel"]{table-panel}
package (LGPL-2.1). A panel that aligns its children to a grid — core
@racket[gui] has only horizontal/vertical panels.
@racketblock[
(define g (new table-panel% [parent f] [dimensions '(4 2)]))
(for ([l '("Name:" "Value:" "Unit:" "Note:")])
(new message% [parent g] [label l])
(new text-field% [parent g] [label #f]))
]
@subsection{canvas-list%}
From the @hyperlink["https://github.com/massung/racket-canvas-list"]{canvas-list}
package (MIT). A fast, single-selection, virtualized list that renders only the
visible rows and supports custom per-item drawing. Core @racket[list-box%]
cannot virtualize very large lists or custom-draw items.
@racketblock[
(new canvas-list%
[parent f]
[items (for/vector ([i (in-range 1 2000)]) (format "Item ~a" i))]
[item-height 22]
[action-callback (λ (canvas item event) (printf "picked ~a\n" item))])
]
@subsection{date-text-field%}
From the @hyperlink["https://github.com/Kalimehtar/text-date"]{text-date}
package (MIT). A @racket[text-field%] for entering dates (@litchar{dd.mm.yyyy}):
shows today's date as faded cue text when empty and filters input to digits and
dots. Core @racket[gui] has no date entry widget.
@racketblock[
(new date-text-field% [parent f] [label "Date:"])
]
@section{Consistency wrappers}
These wrap the aggregated widgets to hide their API footguns behind a single,
consistent class.
@subsection{labeled-field%}
A @racket[text-field%] with cue (placeholder) and tooltip already mixed in, so
you do not have to remember that @racket[cue-mixin] takes two arguments and
must be composed with @racket[tooltip-mixin].
@racketblock[
(new labeled-field% [parent f] [label "Name:"]
[cue "Enter your name"] [tooltip "Your full name"])
]
@subsection{text-list%}
A @racket[canvas-list%] for a list of items rendered as text, with a
one-argument action callback instead of the underlying three-argument one.
@racketblock[
(new text-list% [parent f]
[items (vector "a" "b" "c")]
[action (λ (item) (printf "picked ~a\n" item))])
]
@section[#:tag "companions"]{Recommended companions (install separately)}
These heavier controls are deliberately @italic{not} hard dependencies, to
keep @racket[(require widgetkit)] light. Install the ones you need:
@tabular[#:style 'boxed
#:column-properties '(left left)
#:row-properties '(bottom-border)
(list (list @bold{Control} @bold{Install})
(list @elem{Interactive OSM map}
@racketblock[(raco pkg install map-widget)])
(list @elem{Sortable multi-column data grid}
@racketblock[(raco pkg install qresults-list)])
(list @elem{Spreadsheet editor}
@racketblock[(raco pkg install spreadsheet-editor)])
(list @elem{Embed @racket[plot] snips in a window}
@racketblock[(raco pkg install plot-container)])
(list @elem{Web view (Chromium / native)}
@racketblock[(raco pkg install racket-webview)]))]
A tree / outline view already ships with Racket as
@racketlink[mrlib/hierlist]{mrlib/hierlist} — no install needed.
@section{Roadmap}
Planned future additions (only where no mature solution exists): a collapsible
``disclosure'' section, a draggable split view, a calendar, a dedicated color
picker, a segmented control, and a small toolbar helper.
@index["gui widgets"]{}