]> gitweb.factorcode.org Git - factor.git/blob - basis/ui/gadgets/panes/panes.factor
Fix comments to be ! not #!.
[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 fonts
4 fry io io.styles kernel locals math.rectangles math.vectors
5 memoize models namespaces sequences sorting splitting strings
6 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.traverse ;
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 ;
22 INSTANCE: pane-stream output-stream
23
24 C: <pane-stream> pane-stream
25
26 M: pane-stream stream-element-type drop +character+ ;
27
28 <PRIVATE
29
30 : clear-selection ( pane -- pane )
31     f >>caret f >>mark ; inline
32
33 : prepare-last-line ( pane -- )
34     [ last-line>> ] keep
35     [ current>> f track-add ]
36     [ input>> [ 1 track-add ] when* ] bi
37     drop ; inline
38
39 : init-current ( pane -- pane )
40     dup prototype>> clone >>current ; inline
41
42 : focus-input ( pane -- )
43     input>> [ request-focus ] when* ;
44
45 : next-line ( pane -- )
46     clear-selection
47     [ input>> unparent ]
48     [ init-current prepare-last-line ]
49     [ focus-input ] tri ;
50
51 : pane-caret&mark ( pane -- caret mark )
52     [ caret>> ] [ mark>> ] bi ; inline
53
54 : selected-subtree ( pane -- seq )
55     [ pane-caret&mark sort-pair ] keep gadget-subtree ;
56
57 M: pane gadget-selection? pane-caret&mark and ;
58
59 M: pane gadget-selection ( pane -- string/f )
60     selected-subtree gadget-text ;
61
62 : init-prototype ( pane -- pane )
63     <shelf> +baseline+ >>align >>prototype ; inline
64
65 : init-output ( pane -- pane )
66     <incremental> [ >>output ] [ f track-add ] bi ; inline
67
68 : pane-theme ( pane -- pane )
69     1 >>fill
70     selection-color >>selection-color ; inline
71
72 : init-last-line ( pane -- pane )
73     horizontal <track> 0 >>fill +baseline+ >>align
74     [ >>last-line ] [ 1 track-add ] bi
75     dup prepare-last-line ; inline
76
77 M: pane selected-children
78     dup gadget-selection? [
79         [ selected-subtree leaves ]
80         [ selection-color>> ]
81         bi
82     ] [ drop f f ] if ;
83
84 : scroll-pane ( pane -- )
85     dup scrolls?>> [ scroll>bottom ] [ drop ] if ;
86
87 : smash-line ( current -- gadget )
88     dup children>> {
89         { [ dup empty? ] [ 2drop "" <label> ] }
90         { [ dup length 1 = ] [ nip first ] }
91         [ drop ]
92     } cond ;
93
94 : pane-nl ( pane -- )
95     [
96         [ current>> [ unparent ] [ smash-line ] bi ] [ output>> ] bi
97         add-incremental
98     ] [ next-line ] bi ;
99
100 : smash-pane ( pane -- gadget ) [ pane-nl ] [ output>> smash-line ] bi ;
101
102 : pane-write ( seq pane -- )
103     [ pane-nl ] [ current>> stream-write ]
104     bi-curry interleave ;
105
106 : pane-format ( seq style pane -- )
107     [ nip pane-nl ] [ current>> stream-format ]
108     bi-curry bi-curry interleave ;
109
110 : do-pane-stream ( pane-stream quot -- )
111     [ pane>> ] dip keep scroll-pane ; inline
112
113 M: pane-stream stream-nl
114     [ pane-nl ] do-pane-stream ;
115
116 M: pane-stream stream-write1
117     [ current>> stream-write1 ] do-pane-stream ;
118
119 M: pane-stream stream-write
120     [ [ string-lines ] dip pane-write ] do-pane-stream ;
121
122 M: pane-stream stream-format
123     [ [ string-lines ] 2dip pane-format ] do-pane-stream ;
124
125 M: pane-stream dispose drop ;
126
127 M: pane-stream stream-flush drop ;
128
129 M: pane-stream make-span-stream
130     swap <style-stream> <ignore-close-stream> ;
131
132 PRIVATE>
133
134 : new-pane ( input class -- pane )
135     [ vertical ] dip new-track
136         swap >>input
137         pane-theme
138         init-prototype
139         init-output
140         init-current
141         init-last-line ; inline
142
143 : <pane> ( -- pane ) f pane new-pane ;
144
145 GENERIC: gadget-alt-text ( gadget -- string )
146
147 M: object gadget-alt-text
148     class-of name>> "( " " )" surround ;
149
150 GENERIC: write-gadget ( gadget stream -- )
151
152 M: object write-gadget
153     [ gadget-alt-text ] dip stream-write ;
154
155 M: filter-writer write-gadget
156     stream>> write-gadget ;
157
158 M: pane-stream write-gadget ( gadget pane-stream -- )
159     pane>> current>> swap add-gadget drop ;
160
161 M: style-stream write-gadget
162     stream>> write-gadget ;
163
164 : print-gadget ( gadget stream -- )
165     [ write-gadget ] [ nip stream-nl ] 2bi ;
166
167 : gadget. ( gadget -- )
168     output-stream get print-gadget ;
169
170 : pane-clear ( pane -- )
171     clear-selection
172     [ output>> clear-incremental ]
173     [ current>> clear-gadget ]
174     bi ;
175
176 : with-pane ( pane quot -- )
177     [ [ scroll>top ] [ pane-clear ] [ <pane-stream> ] tri ] dip
178     with-output-stream* ; inline
179
180 : make-pane ( quot -- gadget )
181     [ <pane> ] dip [ with-pane ] [ drop smash-pane ] 2bi ; inline
182
183 TUPLE: pane-control < pane quot ;
184
185 M: pane-control model-changed ( model pane-control -- )
186     [ value>> ] [ dup quot>> ] bi*
187     '[ _ call( value -- ) ] with-pane ;
188
189 : <pane-control> ( model quot -- pane )
190     f pane-control new-pane
191         swap >>quot
192         swap >>model ;
193
194 <PRIVATE
195
196 ! Character styles
197
198 MEMO:: specified-font ( name style size foreground background -- font )
199     ! We memoize here to avoid creating lots of duplicate font objects.
200     monospace-font
201         name [ >>name ] when*
202         style {
203             { f [ ] }
204             { plain [ ] }
205             { bold [ t >>bold? ] }
206             { italic [ t >>italic? ] }
207             { bold-italic [ t >>bold? t >>italic? ] }
208         } case
209         size [ >>size ] when*
210         foreground [ >>foreground ] when*
211         background [ >>background ] when* ;
212
213 : apply-font-style ( style gadget -- style gadget )
214     over {
215         [ font-name of ]
216         [ font-style of ]
217         [ font-size of ]
218         [ foreground of ]
219         [ background of ]
220     } cleave specified-font >>font ;
221
222 : apply-style ( style gadget key quot -- style gadget )
223     [ pick at ] dip when* ; inline
224
225 : apply-presentation-style ( style gadget -- style gadget )
226     presented [ <presentation> ] apply-style ;
227
228 : apply-image-style ( style gadget -- style gadget )
229     image-style [ nip <image-name> <icon> ] apply-style ;
230
231 : apply-background-style ( style gadget -- style gadget )
232     background [ <solid> >>interior ] apply-style ;
233
234 : style-label ( style gadget -- gadget )
235     apply-font-style
236     apply-background-style
237     apply-presentation-style
238     apply-image-style
239     nip ; inline
240
241 : <styled-label> ( style text -- gadget )
242     <label> style-label ;
243
244 ! Paragraph styles
245
246 : apply-wrap-style ( style pane -- style pane )
247     wrap-margin [
248         2dup <paragraph> >>prototype drop
249         <paragraph> >>current
250     ] apply-style ;
251
252 : apply-border-color-style ( style gadget -- style gadget )
253     border-color [ <solid> >>boundary ] apply-style ;
254
255 : apply-page-color-style ( style gadget -- style gadget )
256     page-color [ <solid> >>interior ] apply-style ;
257
258 : apply-inset-style ( style gadget -- style gadget )
259     inset [ <border> ] apply-style ;
260
261 : style-pane ( style pane -- pane )
262     apply-inset-style
263     apply-border-color-style
264     apply-page-color-style
265     apply-presentation-style
266     nip ;
267
268 TUPLE: nested-pane-stream < pane-stream style parent ;
269
270 : new-nested-pane-stream ( style parent class -- stream )
271     new
272         swap >>parent
273         swap <pane> apply-wrap-style [ >>style ] [ >>pane ] bi* ; inline
274
275 : unnest-pane-stream ( stream -- child parent )
276     [ [ style>> ] [ pane>> smash-pane ] bi style-pane ] [ parent>> ] bi ;
277
278 TUPLE: pane-block-stream < nested-pane-stream ;
279
280 M: pane-block-stream dispose
281     unnest-pane-stream write-gadget ;
282
283 M: pane-stream make-block-stream
284     pane-block-stream new-nested-pane-stream ;
285
286 ! Tables
287
288 : apply-table-gap-style ( style grid -- style grid )
289     table-gap [ >>gap ] apply-style ;
290
291 : apply-table-border-style ( style grid -- style grid )
292     table-border [ <grid-lines> >>boundary ] apply-style ;
293
294 : styled-grid ( style grid -- grid )
295     <grid>
296     f >>fill?
297     apply-table-gap-style
298     apply-table-border-style
299     nip ;
300
301 TUPLE: pane-cell-stream < nested-pane-stream ;
302
303 M: pane-cell-stream dispose drop ;
304
305 M: pane-stream make-cell-stream
306     pane-cell-stream new-nested-pane-stream ;
307
308 M: pane-stream stream-write-table
309     [
310         swap [ [ pane>> smash-pane ] map ] map
311         styled-grid
312     ] dip write-gadget ;
313
314 ! Stream utilities
315 M: pack dispose drop ;
316
317 M: paragraph dispose drop ;
318
319 : gadget-write ( string gadget -- )
320     swap dup empty?
321     [ 2drop ] [ <label> text-theme add-gadget drop ] if ;
322
323 M: pack stream-write gadget-write ;
324
325 : gadget-bl ( style stream -- )
326     swap " " <word-break-gadget> style-label add-gadget drop ;
327
328 M: paragraph stream-write
329     swap " " split
330     [ H{ } over gadget-bl ] [ over gadget-write ] interleave
331     drop ;
332
333 : gadget-write1 ( char gadget -- )
334     [ 1string ] dip stream-write ;
335
336 M: pack stream-write1 gadget-write1 ;
337
338 M: paragraph stream-write1
339     over CHAR: \s =
340     [ H{ } swap gadget-bl drop ] [ gadget-write1 ] if ;
341
342 : empty-output? ( string style -- ? )
343     [ empty? ] [ image-style swap key? not ] bi* and ;
344
345 : gadget-format ( string style stream -- )
346     [ [ empty-output? ] 2keep ] dip
347     '[ _ _ swap <styled-label> _ swap add-gadget drop ] unless ;
348
349 M: pack stream-format
350     gadget-format ;
351
352 M: paragraph stream-format
353     over { presented image-style } [ swap key? ] with any? [
354         gadget-format
355     ] [
356         [ " " split ] 2dip
357         [ gadget-bl ] [ gadget-format ] 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* ( loc gadget -- n )
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 M: object content-gadget drop f ;
442
443 M: string content-gadget
444     '[ _ write ] make-pane <scroller>
445         { 450 100 } >>pref-dim
446     <wrapper> ;