This repository has been archived by the owner on Apr 26, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
libui.scm
953 lines (770 loc) · 38.3 KB
/
libui.scm
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
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
(module libui
(init! uninit! main quit!
handler-set!
margined? padded?
widget? widget-id widget-type widget-handlers
new-window window-child-set! window-margined?-set!
new-button button-text button-text-set!
new-horizontal-box new-vertical-box box-append! box-padded?-set!
new-checkbox
new-entry new-password-entry new-search-entry entry-text entry-text-set! entry-read-only?-set!
new-label
new-tab tab-append! tab-margined?-set!
new-group group-child-set! group-margined?-set!
new-spinbox spinbox-value spinbox-value-set!
new-slider slider-value slider-value-set!
new-progress-bar progress-bar-value progress-bar-value-set!
new-horizontal-separator new-vertical-separator
new-combobox combobox-append!
new-editable-combobox editable-combobox-append!
new-radio-buttons radio-buttons-append!
new-date-time-picker new-date-picker new-time-picker
new-multiline-entry new-non-wrapping-multiline-entry multiline-entry-append! multiline-entry-read-only?-set!
new-area area-queue-redraw-all!
new-area-handler
mouse-event-x mouse-event-y mouse-event-area-width mouse-event-area-height
draw-params-context draw-params-area-width draw-params-area-height
new-font-button
new-color-button color-button-color color-button-color-set!
new-form form-append! form-padded?-set!
new-grid grid-append! grid-padded?-set!
->control control-destroy! control-show!
open-file save-file message-box message-box-error
widget-by-id widgets)
(import chicken scheme foreign)
(use srfi-69 matchable lolevel srfi-1
libui-draw)
;;; headers
#> #include "ui.h" <#
;; TODO: bindings to ui_<platform>.h
;; NOTE: requires the right include paths in libui.setup
;;; enums
;; uiAlign
(define uiAlignFill (foreign-value "uiAlignFill" unsigned-int))
(define uiAlignStart (foreign-value "uiAlignStart" unsigned-int))
(define uiAlignCenter (foreign-value "uiAlignCenter" unsigned-int))
(define uiAlignEnd (foreign-value "uiAlignEnd" unsigned-int))
(define (alignment->int alignment)
(case alignment
((fill) uiAlignFill)
((start) uiAlignStart)
((center) uiAlignCenter)
((end) uiAlignEnd)
(else
(abort (usage-error "Invalid alignment" 'alignment->int)))))
;;; typedefs
;; NOTE: uiInitOptions doesn't appear to be used in any way...
;; (define-foreign-type uiInitOptions*-or-null (c-pointer (struct "uiInitOptions")))
(define-foreign-type uiControl* (nonnull-c-pointer (struct "uiControl")))
(define-foreign-type uiWindow* (nonnull-c-pointer (struct "uiWindow")))
(define-foreign-type uiButton* (nonnull-c-pointer (struct "uiButton")))
(define-foreign-type uiBox* (nonnull-c-pointer (struct "uiBox")))
(define-foreign-type uiCheckbox* (nonnull-c-pointer (struct "uiCheckbox")))
(define-foreign-type uiEntry* (nonnull-c-pointer (struct "uiEntry")))
(define-foreign-type uiLabel* (nonnull-c-pointer (struct "uiLabel")))
(define-foreign-type uiTab* (nonnull-c-pointer (struct "uiTab")))
(define-foreign-type uiGroup* (nonnull-c-pointer (struct "uiGroup")))
(define-foreign-type uiSpinbox* (nonnull-c-pointer (struct "uiSpinbox")))
(define-foreign-type uiSlider* (nonnull-c-pointer (struct "uiSlider")))
(define-foreign-type uiProgressBar* (nonnull-c-pointer (struct "uiProgressBar")))
(define-foreign-type uiSeparator* (nonnull-c-pointer (struct "uiSeparator")))
(define-foreign-type uiCombobox* (nonnull-c-pointer (struct "uiCombobox")))
(define-foreign-type uiEditableCombobox* (nonnull-c-pointer (struct "uiEditableCombobox")))
(define-foreign-type uiRadioButtons* (nonnull-c-pointer (struct "uiRadioButtons")))
(define-foreign-type uiDateTimePicker* (nonnull-c-pointer (struct "uiDateTimePicker")))
(define-foreign-type uiMultilineEntry* (nonnull-c-pointer (struct "uiMultilineEntry")))
(define-foreign-type uiArea* (nonnull-c-pointer (struct "uiArea")))
(define-foreign-type uiAreaHandler* (nonnull-c-pointer (struct "uiAreaHandler")))
(define-foreign-type uiAreaDrawParams* (nonnull-c-pointer (struct "uiAreaDrawParams")))
(define-foreign-type uiAreaMouseEvent* (nonnull-c-pointer (struct "uiAreaMouseEvent")))
(define-foreign-type uiAreaKeyEvent* (nonnull-c-pointer (struct "uiAreaKeyEvent")))
(define-foreign-type uiDrawContext* (nonnull-c-pointer (struct "uiDrawContext")))
(define-foreign-type uiFontButton* (nonnull-c-pointer (struct "uiFontButton")))
(define-foreign-type uiColorButton* (nonnull-c-pointer (struct "uiColorButton")))
(define-foreign-type uiForm* (nonnull-c-pointer (struct "uiForm")))
(define-foreign-type uiGrid* (nonnull-c-pointer (struct "uiGrid")))
(define-foreign-type uiEnum unsigned-int)
(define-foreign-type double* (nonnull-c-pointer double))
;;; auxiliary records
(define-record control pointer)
(define-record widget id type pointer handlers)
(define-record-printer (widget w out)
(fprintf out "#<~a: ~a>" (widget-type w) (widget-id w)))
(define-record context pointer)
(define-record draw-params pointer)
(define-record mouse-event pointer)
(define-record key-event pointer)
;;; struct helpers
;; area handler
(define-record area-handler pointer draw mouse-event mouse-crossed drag-broken key-event)
(define uiAreaHandler-size (foreign-type-size (struct "uiAreaHandler")))
;; area mouse events and draw params
(define (mouse-event-x mouse-event)
(let ((mouse-event* (mouse-event-pointer mouse-event)))
((foreign-lambda* double ((uiAreaMouseEvent* e)) "C_return(e->X);") mouse-event*)))
(define (mouse-event-y mouse-event)
(let ((mouse-event* (mouse-event-pointer mouse-event)))
((foreign-lambda* double ((uiAreaMouseEvent* e)) "C_return(e->Y);") mouse-event*)))
(define (mouse-event-area-width mouse-event)
(let ((mouse-event* (mouse-event-pointer mouse-event)))
((foreign-lambda* double ((uiAreaMouseEvent* e)) "C_return(e->AreaWidth);") mouse-event*)))
(define (mouse-event-area-height mouse-event)
(let ((mouse-event* (mouse-event-pointer mouse-event)))
((foreign-lambda* double ((uiAreaMouseEvent* e)) "C_return(e->AreaHeight);") mouse-event*)))
(define (draw-params-context draw-params)
(let* ((draw-params* (draw-params-pointer draw-params))
(context* ((foreign-lambda* uiDrawContext* ((uiAreaDrawParams* params)) "C_return(params->Context);") draw-params*)))
(make-context context*)))
(define (draw-params-area-width draw-params)
(let ((draw-params* (draw-params-pointer draw-params)))
((foreign-lambda* double ((uiAreaDrawParams* params)) "C_return(params->AreaWidth);") draw-params*)))
(define (draw-params-area-height draw-params)
(let ((draw-params* (draw-params-pointer draw-params)))
((foreign-lambda* double ((uiAreaDrawParams* params)) "C_return(params->AreaHeight);") draw-params*)))
;;; foreign functions
(define uiInit (foreign-lambda* c-string* ()
"uiInitOptions options;"
"const char *msg = uiInit(&options);"
"if (msg) {"
" char *ret = strdup(msg);"
" uiFreeInitError(msg);"
" C_return(ret);"
"}"
"C_return(NULL);"))
(define uiUninit (foreign-lambda void "uiUninit"))
(define uiMain (foreign-safe-lambda void "uiMain"))
(define uiQuit (foreign-lambda void "uiQuit"))
(define uiOnShouldQuit (foreign-lambda void "uiOnShouldQuit" (function bool (c-pointer)) c-pointer))
(define uiControl (foreign-lambda uiControl* "uiControl" nonnull-c-pointer))
(define uiControlDestroy (foreign-lambda void "uiControlDestroy" uiControl*))
(define uiControlShow (foreign-lambda void "uiControlShow" uiControl*))
(define uiNewWindow (foreign-lambda uiWindow* "uiNewWindow" nonnull-c-string int int bool))
(define uiWindowOnClosing (foreign-lambda void "uiWindowOnClosing" uiWindow* (function bool (uiWindow* c-pointer)) c-pointer))
(define uiWindowSetChild (foreign-lambda void "uiWindowSetChild" uiWindow* uiControl*))
(define uiWindowSetMargined (foreign-lambda void "uiWindowSetMargined" uiWindow* bool))
(define uiNewButton (foreign-lambda uiButton* "uiNewButton" nonnull-c-string))
(define uiButtonText (foreign-lambda c-string* "uiButtonText" uiButton*))
(define uiButtonSetText (foreign-lambda void "uiButtonSetText" uiButton* nonnull-c-string))
(define uiButtonOnClicked (foreign-lambda void "uiButtonOnClicked" uiButton* (function void (uiButton* c-pointer)) c-pointer))
(define uiNewHorizontalBox (foreign-lambda uiBox* "uiNewHorizontalBox"))
(define uiNewVerticalBox (foreign-lambda uiBox* "uiNewVerticalBox"))
(define uiBoxAppend (foreign-lambda void "uiBoxAppend" uiBox* uiControl* bool))
(define uiBoxSetPadded (foreign-lambda void "uiBoxSetPadded" uiBox* bool))
(define uiNewCheckbox (foreign-lambda uiCheckbox* "uiNewCheckbox" nonnull-c-string))
(define uiNewEntry (foreign-lambda uiEntry* "uiNewEntry"))
(define uiNewPasswordEntry (foreign-lambda uiEntry* "uiNewPasswordEntry"))
(define uiNewSearchEntry (foreign-lambda uiEntry* "uiNewSearchEntry"))
(define uiEntryText (foreign-lambda c-string* "uiEntryText" uiEntry*))
(define uiEntrySetText (foreign-lambda void "uiEntrySetText" uiEntry* nonnull-c-string))
(define uiEntrySetReadOnly (foreign-lambda void "uiEntrySetReadOnly" uiEntry* bool))
(define uiNewLabel (foreign-lambda uiLabel* "uiNewLabel" nonnull-c-string))
(define uiNewTab (foreign-lambda uiTab* "uiNewTab"))
(define uiTabAppend (foreign-lambda void "uiTabAppend" uiTab* nonnull-c-string uiControl*))
(define uiTabNumPages (foreign-lambda int "uiTabNumPages" uiTab*))
(define uiTabSetMargined (foreign-lambda void "uiTabSetMargined" uiTab* int bool))
(define uiNewGroup (foreign-lambda uiGroup* "uiNewGroup" nonnull-c-string))
(define uiGroupSetChild (foreign-lambda void "uiGroupSetChild" uiGroup* uiControl*))
(define uiGroupSetMargined (foreign-lambda void "uiGroupSetMargined" uiGroup* bool))
(define uiNewSpinbox (foreign-lambda uiSpinbox* "uiNewSpinbox" int int))
(define uiSpinboxOnChanged (foreign-lambda void "uiSpinboxOnChanged" uiSpinbox* (function void (uiSpinbox* c-pointer)) c-pointer))
(define uiSpinboxValue (foreign-lambda int "uiSpinboxValue" uiSpinbox*))
(define uiSpinboxSetValue (foreign-lambda void "uiSpinboxSetValue" uiSpinbox* int))
(define uiNewSlider (foreign-lambda uiSlider* "uiNewSlider" int int))
(define uiSliderOnChanged (foreign-lambda void "uiSliderOnChanged" uiSlider* (function void (uiSlider* c-pointer)) c-pointer))
(define uiSliderValue (foreign-lambda int "uiSliderValue" uiSlider*))
(define uiSliderSetValue (foreign-lambda void "uiSliderSetValue" uiSlider* int))
(define uiNewProgressBar (foreign-lambda uiProgressBar* "uiNewProgressBar"))
(define uiProgressBarValue (foreign-lambda int "uiProgressBarValue" uiProgressBar*))
(define uiProgressBarSetValue (foreign-lambda void "uiProgressBarSetValue" uiProgressBar* int))
(define uiNewHorizontalSeparator (foreign-lambda uiSeparator* "uiNewHorizontalSeparator"))
(define uiNewVerticalSeparator (foreign-lambda uiSeparator* "uiNewVerticalSeparator"))
(define uiNewCombobox (foreign-lambda uiCombobox* "uiNewCombobox"))
(define uiComboboxAppend (foreign-lambda void "uiComboboxAppend" uiCombobox* nonnull-c-string))
(define uiNewEditableCombobox (foreign-lambda uiEditableCombobox* "uiNewEditableCombobox"))
(define uiEditableComboboxAppend (foreign-lambda void "uiEditableComboboxAppend" uiEditableCombobox* nonnull-c-string))
(define uiNewRadioButtons (foreign-lambda uiRadioButtons* "uiNewRadioButtons"))
(define uiRadioButtonsAppend (foreign-lambda void "uiRadioButtonsAppend" uiRadioButtons* nonnull-c-string))
(define uiNewDateTimePicker (foreign-lambda uiDateTimePicker* "uiNewDateTimePicker"))
(define uiNewDatePicker (foreign-lambda uiDateTimePicker* "uiNewDatePicker"))
(define uiNewTimePicker (foreign-lambda uiDateTimePicker* "uiNewTimePicker"))
(define uiNewMultilineEntry (foreign-lambda uiMultilineEntry* "uiNewMultilineEntry"))
(define uiNewNonWrappingMultilineEntry (foreign-lambda uiMultilineEntry* "uiNewNonWrappingMultilineEntry"))
(define uiMultilineEntryAppend (foreign-lambda void "uiMultilineEntryAppend" uiMultilineEntry* nonnull-c-string))
(define uiMultilineEntrySetReadOnly (foreign-lambda void "uiMultilineEntrySetReadOnly" uiMultilineEntry* bool))
(define uiNewArea (foreign-lambda uiArea* "uiNewArea" uiAreaHandler*))
(define uiAreaQueueRedrawAll (foreign-lambda void "uiAreaQueueRedrawAll" uiArea*))
(define uiNewFontButton (foreign-lambda uiFontButton* "uiNewFontButton"))
(define uiNewColorButton (foreign-lambda uiColorButton* "uiNewColorButton"))
(define uiColorButtonOnChanged (foreign-lambda void "uiColorButtonOnChanged" uiColorButton* (function void (uiColorButton* c-pointer)) c-pointer))
(define uiColorButtonColor (foreign-lambda void "uiColorButtonColor" uiColorButton* double* double* double* double*))
(define uiColorButtonSetColor (foreign-lambda void "uiColorButtonSetColor" uiColorButton* double double double double))
(define uiNewForm (foreign-lambda uiForm* "uiNewForm"))
(define uiFormAppend (foreign-lambda void "uiFormAppend" uiForm* nonnull-c-string uiControl* bool))
(define uiFormSetPadded (foreign-lambda void "uiFormSetPadded" uiForm* bool))
(define uiNewGrid (foreign-lambda uiGrid* "uiNewGrid"))
(define uiGridAppend (foreign-lambda void "uiGridAppend" uiGrid* uiControl* int int int int bool uiEnum bool uiEnum))
(define uiGridSetPadded (foreign-lambda void "uiGridSetPadded" uiGrid* bool))
#>
char *libuiFileDialog(uiWindow* parent, char *(*f)(uiWindow* parent)) {
char *filename = f(parent);
if (filename) {
char *ret = strdup(filename);
uiFreeText(filename);
C_return(ret);
}
C_return(NULL);
}
<#
(define uiOpenFile (foreign-lambda* c-string* ((uiWindow* parent)) "C_return(libuiFileDialog(parent, uiOpenFile));"))
(define uiSaveFile (foreign-lambda* c-string* ((uiWindow* parent)) "C_return(libuiFileDialog(parent, uiSaveFile));"))
(define uiMsgBox (foreign-lambda void "uiMsgBox" uiWindow* nonnull-c-string nonnull-c-string))
(define uiMsgBoxError (foreign-lambda void "uiMsgBoxError" uiWindow* nonnull-c-string nonnull-c-string))
;;; errors
(define (define-error location message #!rest condition)
(let ((base (make-property-condition 'exn 'location location 'message message))
(extra (apply make-property-condition condition)))
(make-composite-condition base extra)))
(define (libui-error message location)
(define-error location message 'libui))
(define (usage-error message location)
(define-error location message 'usage))
;;; generic handlers
;; special top-level handler that doesn't involve any widget
(define should-quit-handler (make-parameter #f))
(define-external (libui_ShouldQuitHandler (c-pointer data)) bool
(match-let (((handler args) (should-quit-handler)))
(apply handler args)))
;; widget-specific handlers
(define widget-table (make-hash-table))
(define (dispatch-event! widget* type)
(match-let* ((widget (hash-table-ref widget-table widget*))
(handlers (widget-handlers widget))
((handler . args) (hash-table-ref handlers type)))
(apply handler widget args)))
(define-external (libui_WindowClosingHandler (uiWindow* window*) (c-pointer data)) bool
(dispatch-event! window* 'closing))
(define-external (libui_ButtonClickedHandler (uiButton* button*) (c-pointer _data)) void
(dispatch-event! button* 'clicked))
(define-external (libui_SpinboxChangedHandler (uiSpinbox* spinbox*) (c-pointer _data)) void
(dispatch-event! spinbox* 'changed))
(define-external (libui_SliderChangedHandler (uiSlider* slider*) (c-pointer _data)) void
(dispatch-event! slider* 'changed))
(define-external (libui_ColorButtonChangedHandler (uiColorButton* color-button*) (c-pointer _data)) void
(dispatch-event! color-button* 'changed))
;; generic interface
(define (handler-set! widget type proc #!rest args)
(if (and (not widget) (eqv? type 'should-quit))
(begin
(uiOnShouldQuit (location libui_ShouldQuitHandler) #f)
(should-quit-handler (cons proc args)))
(let ((widget-type (widget-type widget))
(handlers (widget-handlers widget))
(widget* (widget-pointer widget))
(value (cons proc args)))
(cond
((and (eqv? widget-type 'window) (eqv? type 'closing))
(hash-table-set! handlers 'closing value)
(uiWindowOnClosing widget* (location libui_WindowClosingHandler) #f))
((and (eqv? widget-type 'button) (eqv? type 'clicked))
(hash-table-set! handlers 'clicked value)
(uiButtonOnClicked widget* (location libui_ButtonClickedHandler) #f))
((and (eqv? widget-type 'spinbox) (eqv? type 'changed))
(hash-table-set! handlers 'changed value)
(uiSpinboxOnChanged widget* (location libui_SpinboxChangedHandler) #f))
((and (eqv? widget-type 'slider) (eqv? type 'changed))
(hash-table-set! handlers 'changed value)
(uiSliderOnChanged widget* (location libui_SliderChangedHandler) #f))
((and (eqv? widget-type 'color-button) (eqv? type 'changed))
(hash-table-set! handlers 'changed value)
(uiColorButtonOnChanged widget* (location libui_ColorButtonChangedHandler) #f))
(else
(abort (usage-error "Unsupported widget/type combination" 'handler-set!)))))))
;; area handler
;; NOTE: this wouldn't work with locatives to blobs as they can change...
(define area-table (make-hash-table))
(define (dispatch-area-event! area-handler* area* accessor #!rest args)
(define (find-area area*)
(find
(lambda (widget)
(and (eqv? (widget-type widget) 'area)
(equal? (widget-pointer widget) area*)))
(hash-table-values widget-table)))
(let* ((area-handler (hash-table-ref area-table area-handler*))
(proc (accessor area-handler))
(area (find-area area*)))
(apply proc area-handler area args)))
(define-external (libui_AreaDrawHandler (uiAreaHandler* area-handler*) (uiArea* area*) (uiAreaDrawParams* draw-params*)) void
(dispatch-area-event! area-handler* area* area-handler-draw
(make-draw-params draw-params*)))
(define-external (libui_AreaMouseEventHandler (uiAreaHandler* area-handler*) (uiArea* area*) (uiAreaMouseEvent* mouse-event*)) void
(dispatch-area-event! area-handler* area* area-handler-mouse-event
(make-mouse-event mouse-event*)))
(define-external (libui_AreaMouseCrossedHandler (uiAreaHandler* area-handler*) (uiArea* area*) (bool left?)) void
(dispatch-area-event! area-handler* area* area-handler-mouse-crossed left?))
(define-external (libui_AreaDragBrokenHandler (uiAreaHandler* area-handler*) (uiArea* area*)) void
(dispatch-area-event! area-handler* area* area-handler-drag-broken))
(define-external (libui_AreaKeyEventHandler (uiAreaHandler* area-handler*) (uiArea* area*) (uiAreaKeyEvent* key-event*)) bool
(dispatch-area-event! area-handler* area* area-handler-key-event
(make-key-event key-event*)))
(define (new-area-handler draw-handler mouse-event-handler mouse-crossed-handler drag-broken-handler key-event-handler)
(let* ((area-handler* (allocate uiAreaHandler-size))
(_ ((foreign-lambda* void ((uiAreaHandler* handler))
"uiAreaHandler *h = handler;"
"h->Draw = libui_AreaDrawHandler;"
"h->MouseEvent = libui_AreaMouseEventHandler;"
"h->MouseCrossed = libui_AreaMouseCrossedHandler;"
"h->DragBroken = libui_AreaDragBrokenHandler;"
"h->KeyEvent = libui_AreaKeyEventHandler;")
area-handler*))
(area-handler (make-area-handler area-handler* draw-handler mouse-event-handler mouse-crossed-handler drag-broken-handler key-event-handler)))
(hash-table-set! area-table area-handler* area-handler)
(set-finalizer! area-handler area-handler-free!)))
(define (area-handler-free! area-handler)
(and-let* ((area-handler* (area-handler-pointer area-handler)))
(free area-handler)
(area-handler-pointer-set! area-handler #f)))
;;; API
;; setup and tear-down
(define (init!)
(let ((ret (uiInit)))
(when ret
(abort (libui-error ret 'init!)))))
(define (uninit!)
;; run all pending finalizers
(gc #t)
(uiUninit))
(define main uiMain)
(define quit! uiQuit)
;; widgets
(define margined? (make-parameter #f))
(define padded? (make-parameter #f))
(define (define-widget type constructor #!rest args)
(let* ((widget* (apply constructor args))
(handlers (make-hash-table eqv? eqv?-hash))
(widget (make-widget #f type widget* handlers)))
(hash-table-set! widget-table widget* widget)
widget))
(define (new-window title width height #!optional menubar?)
(let ((window (define-widget 'window uiNewWindow title width height menubar?)))
(window-margined?-set! window (margined?))
window))
(define (window-child-set! window child)
(let ((window* (widget-pointer window))
(child* (control-pointer child)))
(uiWindowSetChild window* child*)))
(define (window-margined?-set! window margined?)
(let ((window* (widget-pointer window)))
(uiWindowSetMargined window* margined?)))
(define (new-button text)
(define-widget 'button uiNewButton text))
(define (button-text button)
(let ((button* (widget-pointer button)))
(uiButtonText button*)))
(define (button-text-set! button text)
(let ((button* (widget-pointer button)))
(uiButtonSetText button* text)))
(define button-text (getter-with-setter button-text button-text-set!))
(define (new-checkbox text)
(define-widget 'checkbox uiNewCheckbox text))
(define (new-entry)
(define-widget 'entry uiNewEntry))
(define (new-password-entry)
(define-widget 'password-entry uiNewPasswordEntry))
(define (new-search-entry)
(define-widget 'search-entry uiNewSearchEntry))
(define (entry-text entry)
(let ((entry* (widget-pointer entry)))
(uiEntryText entry*)))
(define (entry-text-set! entry text)
(let ((entry* (widget-pointer entry)))
(uiEntrySetText entry* text)))
(define entry-text (getter-with-setter entry-text entry-text-set!))
(define (entry-read-only?-set! entry read-only?)
(let ((entry* (widget-pointer entry)))
(uiEntrySetReadOnly entry* read-only?)))
(define (new-label text)
(define-widget 'label uiNewLabel text))
(define (new-tab)
(define-widget 'tab uiNewTab))
(define (tab-append! tab text control)
(let ((tab* (widget-pointer tab))
(control* (control-pointer control)))
(uiTabAppend tab* text control*)
(tab-margined?-set! tab (sub1 (tab-pages-length tab)) (margined?))))
(define (tab-pages-length tab)
(let ((tab* (widget-pointer tab)))
(uiTabNumPages tab*)))
(define (tab-margined?-set! tab index margined?)
(let ((tab* (widget-pointer tab)))
(uiTabSetMargined tab* index margined?)))
(define (new-group text)
(let ((group (define-widget 'group uiNewGroup text)))
(group-margined?-set! group (margined?))
group))
(define (group-child-set! group child)
(let ((group* (widget-pointer group))
(child* (control-pointer child)))
(uiGroupSetChild group* child*)))
(define (group-margined?-set! group margined?)
(let ((group* (widget-pointer group)))
(uiGroupSetMargined group* margined?)))
(define (new-spinbox min max)
(define-widget 'spinbox uiNewSpinbox min max))
(define (spinbox-value spinbox)
(let ((spinbox* (widget-pointer spinbox)))
(uiSpinboxValue spinbox*)))
(define (spinbox-value-set! spinbox value)
(let ((spinbox* (widget-pointer spinbox)))
(uiSpinboxSetValue spinbox* value)))
(define spinbox-value (getter-with-setter spinbox-value spinbox-value-set!))
(define (new-slider min max)
(define-widget 'slider uiNewSlider min max))
(define (slider-value slider)
(let ((slider* (widget-pointer slider)))
(uiSliderValue slider*)))
(define (slider-value-set! slider value)
(let ((slider* (widget-pointer slider)))
(uiSliderSetValue slider* value)))
(define slider-value (getter-with-setter slider-value slider-value-set!))
(define (new-progress-bar)
(define-widget 'progress-bar uiNewProgressBar))
(define (progress-bar-value progress-bar)
(let ((progress-bar* (widget-pointer progress-bar)))
(uiProgressBarValue progress-bar*)))
(define (progress-bar-value-set! progress-bar value)
(let ((progress-bar* (widget-pointer progress-bar)))
(uiProgressBarSetValue progress-bar* value)))
(define progress-bar-value (getter-with-setter progress-bar-value progress-bar-value-set!))
(define (new-horizontal-separator)
(define-widget 'horizontal-separator uiNewHorizontalSeparator))
(define (new-vertical-separator)
(define-widget 'vertical-separator uiNewVerticalSeparator))
(define (new-combobox)
(define-widget 'combobox uiNewCombobox))
(define (combobox-append! combobox text)
(let ((combobox* (widget-pointer combobox)))
(uiComboboxAppend combobox* text)))
(define (new-editable-combobox)
(define-widget 'editable-combobox uiNewEditableCombobox))
(define (editable-combobox-append! editable-combobox text)
(let ((editable-combobox* (widget-pointer editable-combobox)))
(uiEditableComboboxAppend editable-combobox* text)))
(define (new-radio-buttons)
(define-widget 'radio-buttons uiNewRadioButtons))
(define (radio-buttons-append! radio-buttons text)
(let ((radio-buttons* (widget-pointer radio-buttons)))
(uiRadioButtonsAppend radio-buttons* text)))
(define (new-date-time-picker)
(define-widget 'date-time-picker uiNewDateTimePicker))
(define (new-date-picker)
(define-widget 'date-picker uiNewDatePicker))
(define (new-time-picker)
(define-widget 'time-picker uiNewTimePicker))
(define (new-multiline-entry)
(define-widget 'multiline-entry uiNewMultilineEntry))
(define (new-non-wrapping-multiline-entry)
(define-widget 'non-wrapping-multiline-entry uiNewNonWrappingMultilineEntry))
(define (multiline-entry-append! multiline-entry text)
(let ((multiline-entry* (widget-pointer multiline-entry)))
(uiMultilineEntryAppend multiline-entry* text)))
(define (multiline-entry-read-only?-set! multiline-entry read-only?)
(let ((multiline-entry* (widget-pointer multiline-entry)))
(uiMultilineEntrySetReadOnly multiline-entry* read-only?)))
(define (new-area area-handler)
(let ((area-handler* (area-handler-pointer area-handler)))
(define-widget 'area uiNewArea area-handler*)))
(define (area-queue-redraw-all! area)
(let ((area* (widget-pointer area)))
(uiAreaQueueRedrawAll area*)))
(define (new-font-button)
(define-widget 'font-button uiNewFontButton))
(define (new-color-button)
(define-widget 'color-button uiNewColorButton))
(define (color-button-color color-button)
(let ((color-button* (widget-pointer color-button)))
(let-location ((r double)
(g double)
(b double)
(a double))
(uiColorButtonColor color-button* (location r) (location g) (location b) (location a))
(list r g b a))))
(define (color-button-color-set! color-button r g b a)
(let ((color-button* (widget-pointer color-button)))
(uiColorButtonSetColor color-button* r g b a)))
(define color-button-color (getter-with-setter color-button-color color-button-color-set!))
(define (new-form)
(let ((form (define-widget 'form uiNewForm)))
(form-padded?-set! form (padded?))
form))
(define (form-append! form text control #!optional stretchy?)
(let ((form* (widget-pointer form))
(control* (control-pointer control)))
(uiFormAppend form* text control* stretchy?)))
(define (form-padded?-set! form padded?)
(let ((form* (widget-pointer form)))
(uiFormSetPadded form* padded?)))
(define (new-grid)
(let ((grid (define-widget 'grid uiNewGrid)))
(grid-padded?-set! grid (padded?))
grid))
(define (grid-append! grid control left top #!optional xspan yspan hexpand halign vexpand valign)
(let ((grid* (widget-pointer grid))
(control* (control-pointer control))
(halign (alignment->int (or halign 'fill)))
(valign (alignment->int (or valign 'fill))))
(uiGridAppend grid* control* left top (or xspan 1) (or yspan 1)
hexpand halign vexpand valign)))
(define (grid-padded?-set! grid padded?)
(let ((grid* (widget-pointer grid)))
(uiGridSetPadded grid* padded?)))
;; boxes
(define (new-horizontal-box)
(let ((box (define-widget 'horizontal-box uiNewHorizontalBox)))
(box-padded?-set! box (padded?))
box))
(define (new-vertical-box)
(let ((box (define-widget 'vertical-box uiNewVerticalBox)))
(box-padded?-set! box (padded?))
box))
(define (box-append! box control #!optional stretchy?)
(let ((box* (widget-pointer box))
(control* (control-pointer control)))
(uiBoxAppend box* control* stretchy?)))
(define (box-padded?-set! box padded?)
(let ((box* (widget-pointer box)))
(uiBoxSetPadded box* padded?)))
;; controls
(define (->control widget)
(when (not (widget? widget))
(abort (usage-error "Argument must be a widget" '->control)))
(make-control (uiControl (widget-pointer widget))))
(define (control-destroy! control)
(let ((control* (control-pointer control)))
(uiControlDestroy control*)))
(define (control-show! control)
(let ((control* (control-pointer control)))
(uiControlShow control*)))
;; dialogs
(define (open-file parent)
(let ((parent* (widget-pointer parent)))
(uiOpenFile parent*)))
(define (save-file parent)
(let ((parent* (widget-pointer parent)))
(uiSaveFile parent*)))
(define (message-box parent title description)
(let ((parent* (widget-pointer parent)))
(uiMsgBox parent* title description)))
(define (message-box-error parent title description)
(let ((parent* (widget-pointer parent)))
(uiMsgBoxError parent* title description)))
;; SXML interface
(define (widget-by-id id)
(find
(lambda (widget)
(eqv? (widget-id widget) id))
(hash-table-values widget-table)))
(define (widgets sxml)
(define (attribute-ref attribute attributes)
(let ((value (assv attribute attributes)))
(and value (cadr value))))
(define sxml-attributes
(match-lambda
((tag ('@ attributes ...) children ...)
attributes)
((tag children ...)
'())))
(define sxml-children
(match-lambda
((tag ('@ attributes ...) children ...)
children)
((tag children ...)
children)))
(define (find-handler-list attributes)
(find
(lambda (pair)
(memv (car pair) '(should-quit closing clicked changed)))
attributes))
(define (widget tag attributes children)
(let* ((id (attribute-ref 'id attributes))
(margined?-pair (assv 'margined? attributes))
(padded?-pair (assv 'padded? attributes))
(text (attribute-ref 'text attributes))
(value (attribute-ref 'value attributes))
(read-only? (attribute-ref 'read-only? attributes))
(min (attribute-ref 'min attributes))
(max (attribute-ref 'max attributes))
(handler-list (find-handler-list attributes))
(widget
(case tag
((window)
(let* ((title (attribute-ref 'title attributes))
(width (attribute-ref 'width attributes))
(height (attribute-ref 'height attributes))
(menubar? (attribute-ref 'menubar? attributes))
(window (new-window title width height menubar?))
(child (and (pair? children) (car children))))
(when margined?-pair
(window-margined?-set! window (cadr margined?-pair)))
(when child
(window-child-set! window (->control (widgets child))))
window))
((button)
(new-button text))
((font-button)
(new-font-button))
((color-button)
(let ((color (attribute-ref 'color attributes))
(color-button (new-color-button)))
(when color
(apply color-button-color-set! color))
color-button))
((checkbox)
(new-checkbox text))
((entry password-entry search-entry)
(let ((entry ((case tag
((entry) new-entry)
((password-entry) new-password-entry)
((search-entry) new-search-entry)))))
(when text
(entry-text-set! entry text))
(when read-only?
(entry-read-only?-set! entry read-only?))
entry))
((multiline-entry non-wrapping-multiline-entry)
(let ((entry ((case tag
((multiline-entry) new-multiline-entry)
((non-wrapping-multiline-entry) new-non-wrapping-multiline-entry)))))
(when read-only?
(multiline-entry-read-only?-set! entry read-only?))
entry))
((label)
(new-label text))
((spinbox)
(let ((spinbox (new-spinbox min max)))
(when value
(spinbox-value-set! spinbox value))
spinbox))
((slider)
(let ((slider (new-slider min max)))
(when value
(slider-value-set! slider value))
slider))
((progress-bar)
(let ((progress-bar (new-progress-bar)))
(when value
(progress-bar-value-set! progress-bar value))
progress-bar))
((horizontal-separator)
(new-horizontal-separator))
((vertical-separator)
(new-vertical-separator))
((combobox editable-combobox radio-buttons)
(let ((constructor (case tag
((combobox) new-combobox)
((editable-combobox) new-editable-combobox)
((radio-buttons) new-radio-buttons)))
(appender (case tag
((combobox) combobox-append!)
((editable-combobox) editable-combobox-append!)
((radio-buttons) radio-buttons-append!))))
(let ((widget (constructor)))
(for-each
(lambda (child)
(appender widget child))
children)
widget)))
((date-picker)
(new-date-picker))
((time-picker)
(new-time-picker))
((date-time-picker)
(new-date-time-picker))
((area)
(let ((handler (attribute-ref 'handler attributes)))
(new-area handler)))
((hbox vbox)
(let* ((horizontal? (if (eqv? tag 'hbox) #t #f))
(box (if horizontal? (new-horizontal-box) (new-vertical-box))))
(for-each
(lambda (child)
(let* ((attributes (sxml-attributes child))
(stretchy? (attribute-ref 'stretchy? attributes)))
(box-append! box (->control (widgets child)) stretchy?)))
children)
(when padded?-pair
(box-padded?-set! box (cadr padded?-pair)))
box))
((tab)
(let ((tab (new-tab)))
(for-each
(lambda (child index)
(let* ((attributes (sxml-attributes child))
(text (attribute-ref 'text attributes))
(margined?-pair (assv 'margined? attributes))
(children (sxml-children child))
(item (car children)))
(tab-append! tab text (->control (widgets item)))
(when margined?-pair
(tab-margined?-set! tab index (cadr margined?-pair)))))
children (iota (length children)))
tab))
((group)
(let ((group (new-group text))
(child (and (pair? children) (car children))))
(when margined?-pair
(group-margined?-set! group (cadr margined?-pair)))
(when child
(group-child-set! group (->control (widgets child))))
group))
((form)
(let ((form (new-form))
(padded?-pair (assv 'padded? attributes)))
(for-each
(lambda (child)
(let* ((attributes (sxml-attributes child))
(stretchy? (attribute-ref 'stretchy? attributes))
(text (attribute-ref 'text attributes))
(children (sxml-children child))
(item (car children)))
(form-append! form text (->control (widgets item)) stretchy?)))
children)
(when padded?-pair
(form-padded?-set! form (cadr padded?-pair)))
form))
((grid)
(let ((grid (new-grid))
(padded?-pair (assv 'padded? attributes)))
(for-each
(lambda (child)
(let* ((attributes (sxml-attributes child))
(left (attribute-ref 'left attributes))
(top (attribute-ref 'top attributes))
(xspan (or (attribute-ref 'xspan attributes) 1))
(yspan (or (attribute-ref 'yspan attributes) 1))
(hexpand (attribute-ref 'hexpand attributes))
(halign (or (attribute-ref 'halign attributes) 'fill))
(vexpand (attribute-ref 'vexpand attributes))
(valign (or (attribute-ref 'valign attributes) 'fill))
(children (sxml-children child))
(item (car children)))
(grid-append! grid (->control (widgets item)) left top
xspan yspan hexpand halign vexpand valign)))
children)
(when padded?-pair
(grid-padded?-set! grid (cadr padded?-pair)))
grid))
(else
(abort (usage-error (format "Unimplemented widget tag name: ~a" tag)
'widgets))))))
(when id
(widget-id-set! widget id))
(when handler-list
(apply handler-set! widget handler-list))
widget))
(match sxml
((tag ('@ attributes ...) children ...)
(widget tag attributes children))
((tag children ...)
(widget tag '() children))
(_ (abort (usage-error "Invalid SXML syntax" 'widgets)))))
)