]> gitweb.factorcode.org Git - factor.git/blob - basis/ui/ui.factor
22abfc8f21b2c6fa45d5009c07e6910a99fdf523
[factor.git] / basis / ui / ui.factor
1 ! Copyright (C) 2006, 2007 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: arrays assocs io kernel math models namespaces
4 prettyprint dlists deques sequences threads sequences words
5 debugger ui.gadgets ui.gadgets.worlds ui.gadgets.tracks
6 ui.gestures ui.backend ui.render continuations init combinators
7 hashtables concurrency.flags sets accessors ;
8 IN: ui
9
10 ! Assoc mapping aliens to gadgets
11 SYMBOL: windows
12
13 SYMBOL: stop-after-last-window?
14
15 : event-loop? ( -- ? )
16     {
17         { [ stop-after-last-window? get not ] [ t ] }
18         { [ graft-queue deque-empty? not ] [ t ] }
19         { [ windows get-global empty? not ] [ t ] }
20         [ f ]
21     } cond ;
22
23 : event-loop ( -- ) [ event-loop? ] [ do-events ] [ ] while ;
24
25 : window ( handle -- world ) windows get-global at ;
26
27 : window-focus ( handle -- gadget ) window world-focus ;
28
29 : register-window ( world handle -- )
30     #! Add the new window just below the topmost window. Why?
31     #! So that if the new window doesn't actually receive focus
32     #! (eg, we're using focus follows mouse and the mouse is not
33     #! in the new window when it appears) Factor doesn't get
34     #! confused and send workspace operations to the new window,
35     #! etc.
36     swap 2array windows get-global push
37     windows get-global dup length 1 >
38     [ [ length 1- dup 1- ] keep exchange ] [ drop ] if ;
39
40 : unregister-window ( handle -- )
41     windows global [ [ first = not ] with filter ] change-at ;
42
43 : raised-window ( world -- )
44     windows get-global
45     [ [ second eq? ] with find drop ] keep
46     [ nth ] [ delete-nth ] [ nip ] 2tri push ;
47
48 : focus-gestures ( new old -- )
49     drop-prefix <reversed>
50     T{ lose-focus } swap each-gesture
51     T{ gain-focus } swap each-gesture ;
52
53 : focus-world ( world -- )
54     t over (>>focused?)
55     dup raised-window
56     focus-path f focus-gestures ;
57
58 : unfocus-world ( world -- )
59     f over (>>focused?)
60     focus-path f swap focus-gestures ;
61
62 M: world graft*
63     dup (open-window)
64     dup title>> over set-title
65     request-focus ;
66
67 : reset-world ( world -- )
68     #! This is used when a window is being closed, but also
69     #! when restoring saved worlds on image startup.
70     dup fonts>> clear-assoc
71     dup unfocus-world
72     f swap (>>handle) ;
73
74 M: world ungraft*
75     dup free-fonts
76     dup hand-clicked close-global
77     dup hand-gadget close-global
78     dup handle>> (close-window)
79     reset-world ;
80
81 : find-window ( quot -- world )
82     windows get values
83     [ gadget-child swap call ] with find-last nip ; inline
84
85 SYMBOL: ui-hook
86
87 : init-ui ( -- )
88     <dlist> \ graft-queue set-global
89     <dlist> \ layout-queue set-global
90     V{ } clone windows set-global ;
91
92 : restore-gadget-later ( gadget -- )
93     dup graft-state>> {
94         { { f f } [ ] }
95         { { f t } [ ] }
96         { { t t } [
97             { f f } over (>>graft-state)
98         ] }
99         { { t f } [
100             dup unqueue-graft
101             { f f } over (>>graft-state)
102         ] }
103     } case graft-later ;
104
105 : restore-gadget ( gadget -- )
106     dup restore-gadget-later
107     children>> [ restore-gadget ] each ;
108
109 : restore-world ( world -- )
110     dup reset-world restore-gadget ;
111
112 : restore-windows ( -- )
113     windows get [ values ] keep delete-all
114     [ restore-world ] each
115     forget-rollover ;
116
117 : restore-windows? ( -- ? )
118     windows get empty? not ;
119
120 : update-hand ( world -- )
121     dup hand-world get-global eq?
122     [ hand-loc get-global swap move-hand ] [ drop ] if ;
123
124 : layout-queued ( -- seq )
125     [
126         in-layout? on
127         layout-queue [
128             dup layout find-world [ , ] when*
129         ] slurp-deque
130     ] { } make prune ;
131
132 : redraw-worlds ( seq -- )
133     [ dup update-hand draw-world ] each ;
134
135 : notify ( gadget -- )
136     dup graft-state>>
137     dup first { f f } { t t } ?
138     pick (>>graft-state) {
139         { { f t } [ dup activate-control graft* ] }
140         { { t f } [ dup deactivate-control ungraft* ] }
141     } case ;
142
143 : notify-queued ( -- )
144     graft-queue [ notify ] slurp-deque ;
145
146 : update-ui ( -- )
147     [ notify-queued layout-queued redraw-worlds ] assert-depth ;
148
149 : ui-wait ( -- )
150     10 sleep ;
151
152 : ui-try ( quot -- ) [ ui-error ] recover ;
153
154 SYMBOL: ui-thread
155
156 : ui-running ( quot -- )
157     t \ ui-running set-global
158     [ f \ ui-running set-global ] [ ] cleanup ; inline
159
160 : ui-running? ( -- ? )
161     \ ui-running get-global ;
162
163 : update-ui-loop ( -- )
164     ui-running? ui-thread get-global self eq? and [
165         ui-notify-flag get lower-flag
166         [ update-ui ] ui-try
167         update-ui-loop
168     ] when ;
169
170 : start-ui-thread ( -- )
171     [ self ui-thread set-global update-ui-loop ]
172     "UI update" spawn drop ;
173
174 : open-world-window ( world -- )
175     dup pref-dim over (>>dim) dup relayout graft ;
176
177 : open-window ( gadget title -- )
178     f <world> open-world-window ;
179
180 : set-fullscreen? ( ? gadget -- )
181     find-world set-fullscreen* ;
182
183 : fullscreen? ( gadget -- ? )
184     find-world fullscreen* ;
185
186 : raise-window ( gadget -- )
187     find-world raise-window* ;
188
189 HOOK: close-window ui-backend ( gadget -- )
190
191 M: object close-window
192     find-world [ ungraft ] when* ;
193
194 : start-ui ( -- )
195     restore-windows? [
196         restore-windows
197     ] [
198         init-ui ui-hook get call
199     ] if
200     notify-ui-thread start-ui-thread ;
201
202 [
203     f \ ui-running set-global
204     <flag> ui-notify-flag set-global
205 ] "ui" add-init-hook
206
207 HOOK: ui ui-backend ( -- )
208
209 MAIN: ui
210
211 : with-ui ( quot -- )
212     ui-running? [
213         call
214     ] [
215         f windows set-global
216         [
217             ui-hook set
218             stop-after-last-window? on
219             ui
220         ] with-scope
221     ] if ;