]> gitweb.factorcode.org Git - factor.git/blob - basis/ui/gadgets/panes/panes.factor
e2cf8b43144b6092ae3c614d890d63f55d5b41ba
[factor.git] / basis / ui / gadgets / panes / panes.factor
1 ! Copyright (C) 2005, 2010 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors assocs classes combinators destructors
4 documents.private fonts io io.styles kernel math math.rectangles
5 math.vectors models namespaces sequences sets sorting splitting
6 strings ui.baseline-alignment ui.clipboards ui.gadgets
7 ui.gadgets.borders ui.gadgets.grid-lines ui.gadgets.grids
8 ui.gadgets.icons ui.gadgets.incremental ui.gadgets.labels
9 ui.gadgets.menus ui.gadgets.packs ui.gadgets.paragraphs
10 ui.gadgets.presentations ui.gadgets.private ui.gadgets.scrollers
11 ui.gadgets.tracks ui.gestures ui.images ui.pens.solid ui.render
12 ui.theme ui.traverse unicode ;
13 FROM: io.styles => foreground background ;
14 FROM: ui.gadgets.wrappers => <wrapper> ;
15 IN: ui.gadgets.panes
16
17 TUPLE: pane < track
18     output current input last-line prototype scrolls?
19     selection-color caret mark selecting? ;
20
21 TUPLE: pane-stream pane parent ;
22 INSTANCE: pane-stream output-stream
23
24 : <pane-stream> ( pane -- pane-stream )
25     f pane-stream boa ;
26
27 M: pane-stream stream-element-type drop +character+ ;
28
29 DEFER: write-gadget
30
31 <PRIVATE
32
33 : clear-selection ( pane -- pane )
34     f >>caret f >>mark ; inline
35
36 : prepare-last-line ( pane -- )
37     [ last-line>> ] keep
38     [ current>> f track-add ]
39     [ input>> [ 1 track-add ] when* ] bi
40     drop ; inline
41
42 : init-current ( pane -- pane )
43     dup prototype>> clone >>current ; inline
44
45 : focus-input ( pane -- )
46     input>> [ request-focus ] when* ;
47
48 : next-line ( pane -- )
49     clear-selection
50     [ input>> unparent ]
51     [ init-current prepare-last-line ]
52     [ focus-input ] tri ;
53
54 : pane-caret&mark ( pane -- caret mark )
55     [ caret>> ] [ mark>> ] bi ; inline
56
57 : selected-subtree ( pane -- seq )
58     [ pane-caret&mark sort-pair ] keep gadget-subtree ;
59
60 M: pane gadget-selection? pane-caret&mark and ;
61
62 M: pane gadget-selection selected-subtree gadget-text ;
63
64 : init-prototype ( pane -- pane )
65     <shelf> +baseline+ >>align >>prototype ; inline
66
67 : init-output ( pane -- pane )
68     <incremental> [ >>output ] [ f track-add ] bi ; inline
69
70 : pane-theme ( pane -- pane )
71     1 >>fill
72     selection-color >>selection-color ; inline
73
74 : init-last-line ( pane -- pane )
75     horizontal <track> 0 >>fill +baseline+ >>align
76     [ >>last-line ] [ 1 track-add ] bi
77     dup prepare-last-line ; inline
78
79 M: pane selected-children
80     dup gadget-selection? [
81         [ selected-subtree leaves ]
82         [ selection-color>> ]
83         bi
84     ] [ drop f f ] if ;
85
86 : scroll-pane ( pane -- )
87     dup scrolls?>> [ scroll>bottom ] [ drop ] if ;
88
89 : smash-line ( current -- gadget )
90     dup children>> {
91         { [ dup empty? ] [ 2drop "" <label> ] }
92         { [ dup length 1 = ] [ nip first ] }
93         [ drop ]
94     } cond ;
95
96 : pane-nl ( pane -- )
97     [
98         [ current>> [ unparent ] [ smash-line ] bi ] [ output>> ] bi
99         add-incremental
100     ] [ next-line ] bi ;
101
102 GENERIC: smash-pane ( pane -- gadget )
103
104 M: pane smash-pane
105     [ pane-nl ] [ output>> smash-line ] bi ;
106
107 GENERIC: pane-line ( str style gadget -- )
108
109 : pane-format ( lines style pane -- )
110     [ nip pane-nl ] [ current>> pane-line ]
111     bi-curry bi-curry interleave ;
112
113 : pane-write ( lines pane -- )
114     H{ } swap pane-format ;
115
116 : pane-write1 ( char pane -- )
117     [ 1string H{ } ] dip current>> pane-line ;
118
119 : do-pane-stream ( pane-stream quot -- )
120     [ pane>> ] dip keep scroll-pane ; inline
121
122 M: pane-stream stream-nl
123     [ pane-nl ] do-pane-stream ;
124
125 M: pane-stream stream-write1
126     [ pane-write1 ] do-pane-stream ;
127
128 : split-pane ( str quot: ( str -- ) -- )
129     '[
130         dup length 3639 >
131         [ 3639 over last-grapheme-from cut-slice ] [ f ] if
132         swap "" like split-lines @ dup
133     ] loop drop ; inline
134
135 M: pane-stream stream-write
136     [ '[ _ pane-write ] split-pane ] do-pane-stream ;
137
138 M: pane-stream stream-format
139     [ '[ _ _ pane-format ] split-pane ] do-pane-stream ;
140
141 M: pane-stream dispose
142     dup parent>> [
143         [ pane>> smash-pane ] dip write-gadget
144     ] [ drop ] if* ;
145
146 ! M: pane-stream dispose drop ;
147
148 M: pane-stream stream-flush drop ;
149
150 M: pane-stream make-span-stream
151     swap <style-stream> <ignore-close-stream> ;
152
153 PRIVATE>
154
155 : new-pane ( input class -- pane )
156     [ vertical ] dip new-track
157         swap >>input
158         pane-theme
159         init-prototype
160         init-output
161         init-current
162         init-last-line ; inline
163
164 : <pane> ( -- pane ) f pane new-pane ;
165
166 GENERIC: gadget-alt-text ( gadget -- string )
167
168 M: object gadget-alt-text
169     class-of name>> "( " " )" surround ;
170
171 GENERIC: write-gadget ( gadget stream -- )
172
173 M: object write-gadget
174     [ gadget-alt-text ] dip stream-write ;
175
176 M: filter-writer write-gadget
177     stream>> write-gadget ;
178
179 M: pane-stream write-gadget
180     pane>> current>> swap add-gadget drop ;
181
182 : print-gadget ( gadget stream -- )
183     [ write-gadget ] [ nip stream-nl ] 2bi ;
184
185 : gadget. ( gadget -- )
186     output-stream get print-gadget ;
187
188 : clear-pane ( pane -- )
189     clear-selection
190     [ output>> clear-incremental ]
191     [ current>> clear-gadget ]
192     bi ;
193
194 : with-pane ( pane quot -- )
195     [ [ scroll>top ] [ clear-pane ] [ <pane-stream> ] tri ] dip
196     with-output-stream* ; inline
197
198 : make-pane ( quot -- gadget )
199     [ <pane> ] dip '[ _ with-pane ] keep smash-pane ; inline
200
201 TUPLE: pane-control < pane quot ;
202
203 M: pane-control model-changed
204     [ value>> ] [ dup quot>> ] bi*
205     '[ _ call( value -- ) ] with-pane ;
206
207 : <pane-control> ( model quot -- pane )
208     f pane-control new-pane
209         swap >>quot
210         swap >>model ;
211
212 <PRIVATE
213
214 ! Character styles
215
216 MEMO:: specified-font ( name style size foreground background -- font )
217     ! We memoize here to avoid creating lots of duplicate font objects.
218     monospace-font
219         name [ >>name ] when*
220         style {
221             { f [ ] }
222             { plain [ ] }
223             { bold [ t >>bold? ] }
224             { italic [ t >>italic? ] }
225             { bold-italic [ t >>bold? t >>italic? ] }
226         } case
227         size [ >>size ] when*
228         foreground [ >>foreground ] when*
229         background [ >>background ] when* ;
230
231 : apply-font-style ( style gadget -- style gadget )
232     over {
233         [ font-name of ]
234         [ font-style of ]
235         [ font-size of ]
236         [ foreground of ]
237         [ background of ]
238     } cleave specified-font >>font ;
239
240 : apply-style ( style gadget key quot -- style gadget )
241     [ pick at ] dip when* ; inline
242
243 : apply-presentation-style ( style gadget -- style gadget )
244     presented [ <presentation> ] apply-style ;
245
246 : apply-image-style ( style gadget -- style gadget )
247     image-style [ nip <image-name> <icon> ] apply-style ;
248
249 : apply-background-style ( style gadget -- style gadget )
250     background [ <solid> >>interior ] apply-style ;
251
252 : apply-character-style ( style gadget -- gadget )
253     apply-font-style
254     apply-background-style
255     apply-image-style
256     apply-presentation-style
257     nip ; inline
258
259 ! Paragraph styles
260
261 : apply-wrap-style ( style pane -- style pane )
262     wrap-margin [
263         2dup <paragraph> >>prototype drop
264         <paragraph> >>current
265     ] apply-style ;
266
267 : apply-border-color-style ( style gadget -- style gadget )
268     border-color [ <solid> >>boundary ] apply-style ;
269
270 : apply-page-color-style ( style gadget -- style gadget )
271     page-color [ <solid> >>interior ] apply-style ;
272
273 : apply-inset-style ( style gadget -- style gadget )
274     inset [ <border> ] apply-style ;
275
276 : apply-paragraph-style ( style pane -- pane )
277     apply-inset-style
278     apply-border-color-style
279     apply-page-color-style
280     apply-presentation-style
281     nip ;
282
283 : remove-paragraph-styles ( style -- style' )
284     [
285         drop HS{
286             wrap-margin border-color page-color inset presented
287         } in?
288     ] assoc-reject ;
289
290 TUPLE: styled-pane < pane style ;
291
292 : <styled-pane> ( style -- pane )
293     f styled-pane new-pane apply-wrap-style swap >>style ;
294
295 M: styled-pane smash-pane
296     [ style>> ] [ call-next-method apply-paragraph-style ] bi ;
297
298 : <styled-pane-stream> ( style pane-stream -- styled-stream )
299     over
300     [ <styled-pane> ]
301     [ pane-stream boa ]
302     [ remove-paragraph-styles <style-stream> ] tri* ;
303
304 : make-styled-pane ( style quot -- gadget )
305     [ <styled-pane> ] dip '[ _ with-pane ] keep smash-pane ; inline
306
307 M: pane-stream make-block-stream
308     <styled-pane-stream> ;
309
310 ! Tables
311
312 : apply-table-gap-style ( style grid -- style grid )
313     table-gap [ >>gap ] apply-style ;
314
315 : apply-table-border-style ( style grid -- style grid )
316     table-border [ <grid-lines> >>boundary ] apply-style ;
317
318 : <styled-grid> ( style grid -- grid )
319     <grid>
320     f >>fill?
321     apply-table-gap-style
322     apply-table-border-style
323     apply-paragraph-style ;
324
325 M: pane-stream make-cell-stream
326     drop f <styled-pane-stream> ;
327
328 M: pane-stream stream-write-table
329     [
330         swap [ [ stream>> pane>> smash-pane ] map ] map
331         <styled-grid>
332     ] dip write-gadget ;
333
334 ! Stream utilities
335
336 : pane-bl ( style gadget -- )
337     swap " " <word-break-gadget> apply-character-style add-gadget drop ;
338
339 : <styled-label> ( style text -- gadget )
340     <label>
341     apply-font-style
342     apply-background-style
343     apply-image-style
344     apply-presentation-style
345     nip ;
346
347 : pane-text ( string style gadget -- )
348     [ swap <styled-label> ] [ swap add-gadget drop ] bi* ;
349
350 M: pack pane-line pane-text ;
351
352 M: paragraph pane-line
353     { presented image-style } pick '[ _ key? ] any? [
354         pane-text
355     ] [
356         [ " " split ] 2dip
357         [ pane-bl ] [ pane-text ] bi-curry bi-curry
358         interleave
359     ] if ;
360
361 : caret>mark ( pane -- )
362     dup caret>> >>mark relayout-1 ;
363
364 GENERIC: sloppy-pick-up* ( loc gadget -- n )
365
366 M: pack sloppy-pick-up*
367     [ orientation>> ] [ children>> ] bi
368     [ loc>> ] (fast-children-on) ;
369
370 M: gadget sloppy-pick-up*
371     children>> [ contains-point? ] with find-last drop ;
372
373 M: f sloppy-pick-up*
374     2drop f ;
375
376 : wet-and-sloppy ( loc gadget n -- newloc newgadget )
377     swap nth-gadget [ loc>> v- ] keep ;
378
379 : sloppy-pick-up ( loc gadget -- path )
380     2dup sloppy-pick-up* dup
381     [ [ wet-and-sloppy sloppy-pick-up ] keep prefix ]
382     [ 3drop { } ]
383     if ;
384
385 : move-caret ( pane loc -- )
386     over screen-loc v- over sloppy-pick-up >>caret
387     relayout-1 ;
388
389 : begin-selection ( pane -- )
390     f >>selecting?
391     dup hand-loc get-global move-caret
392     f >>mark
393     drop ;
394
395 : extend-selection ( pane -- )
396     hand-moved? [
397         [
398             dup selecting?>> [
399                 hand-loc get-global move-caret
400             ] [
401                 dup hand-clicked get-global child? [
402                     t >>selecting?
403                     [ hand-clicked set-global ]
404                     [ hand-click-loc get-global move-caret ]
405                     [ caret>mark ]
406                     tri
407                 ] [ drop ] if
408             ] if
409         ] [ dup caret>> gadget-at-path scroll>gadget ] bi
410     ] [ drop ] if ;
411
412 : end-selection ( pane -- )
413     dup selecting?>> hand-moved? or
414     [ f >>selecting? ] dip
415     [ [ com-copy-selection ] [ request-focus ] bi ]
416     [ [ relayout-1 ] [ focus-input ] bi ]
417     if ;
418
419 : select-to-caret ( pane -- )
420     t >>selecting?
421     [ dup mark>> [ dup caret>mark ] unless hand-loc get-global move-caret ]
422     [ com-copy-selection ]
423     [ request-focus ]
424     tri ;
425
426 : pane-menu ( pane -- ) { com-copy } show-commands-menu ;
427
428 PRIVATE>
429
430 pane H{
431     { T{ button-down } [ begin-selection ] }
432     { T{ button-down f { S+ } 1 } [ select-to-caret ] }
433     { T{ button-up f { S+ } 1 } [ end-selection ] }
434     { T{ button-up } [ end-selection ] }
435     { T{ drag { # 1 } } [ extend-selection ] }
436     { copy-action [ com-copy ] }
437     { T{ button-down f f 3 } [ pane-menu ] }
438 } set-gestures
439
440 GENERIC: content-gadget ( object -- gadget/f )
441
442 M: object content-gadget drop f ;