1 ! Copyright (C) 2008, 2009 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors arrays colors colors.constants fry kernel math
4 math.functions math.rectangles math.order math.vectors namespaces
5 opengl sequences ui.gadgets ui.gadgets.scrollers ui.gadgets.status-bar
6 ui.gadgets.worlds ui.gestures ui.render ui.pens.solid ui.text
7 ui.commands ui.images ui.gadgets.menus ui.gadgets.line-support
8 math.rectangles models math.ranges sequences combinators fonts locals
12 ! Row rendererer protocol
13 GENERIC: prototype-row ( renderer -- columns )
14 GENERIC: column-alignment ( renderer -- alignment )
15 GENERIC: filled-column ( renderer -- n )
16 GENERIC: column-titles ( renderer -- strings )
18 GENERIC: row-columns ( row renderer -- columns )
19 GENERIC: row-value ( row renderer -- object )
20 GENERIC: row-color ( row renderer -- color )
22 SINGLETON: trivial-renderer
24 M: object prototype-row drop { "" } ;
25 M: object column-alignment drop f ;
26 M: object filled-column drop f ;
27 M: object column-titles drop f ;
29 M: trivial-renderer row-columns drop ;
30 M: object row-value drop ;
31 M: object row-color 2drop f ;
33 TUPLE: table < line-gadget
34 { renderer initial: trivial-renderer }
35 { action initial: [ drop ] }
37 { hook initial: [ drop ] }
39 column-widths total-width
41 { mouse-color initial: COLOR: black }
44 selected-index selected-value
46 { takes-focus? initial: t }
49 : <table> ( rows renderer -- table )
53 f <model> >>selected-value
54 sans-serif-font >>font
55 focus-border-color >>focus-border-color
56 transparent >>column-line-color ;
60 GENERIC: cell-width ( font cell -- x )
61 GENERIC: cell-height ( font cell -- y )
62 GENERIC: draw-cell ( font cell -- )
64 M: string cell-width text-width ;
65 M: string cell-height text-height ceiling ;
66 M: string draw-cell draw-text ;
68 M: image-name cell-width nip image-dim first ;
69 M: image-name cell-height nip image-dim second ;
70 M: image-name draw-cell nip draw-image ;
72 : table-rows ( table -- rows )
73 [ control-value ] [ renderer>> ] bi '[ _ row-columns ] map ;
75 : column-offsets ( widths gap -- x xs )
76 [ 0 ] dip '[ _ + + ] accumulate ;
78 CONSTANT: column-title-background COLOR: light-gray
80 : column-title-font ( font -- font' )
81 column-title-background font-with-background t >>bold? ;
83 : initial-widths ( table rows -- widths )
84 over renderer>> column-titles dup
85 [ [ drop font>> ] dip [ text-width ] with map ]
86 [ drop nip first length 0 <repetition> ]
89 : row-column-widths ( table row -- widths )
90 [ font>> ] dip [ cell-width ] with map ;
92 : compute-total-width ( gap widths -- total )
93 swap [ column-offsets drop ] keep - ;
95 : compute-column-widths ( table -- total widths )
96 dup table-rows [ drop 0 { } ] [
97 [ drop gap>> ] [ initial-widths ] [ ] 2tri
98 [ row-column-widths vmax ] with each
99 [ compute-total-width ] keep
102 : update-cached-widths ( table -- )
103 dup compute-column-widths
104 [ >>total-width ] [ >>column-widths ] bi*
107 : filled-column-width ( table -- n )
108 [ dim>> first ] [ total-width>> ] bi [-] ;
110 : update-filled-column ( table -- )
111 [ filled-column-width ]
112 [ renderer>> filled-column ]
113 [ column-widths>> ] tri
115 [ [ + ] change-nth ] [ 3drop ] if ;
118 [ update-cached-widths ] [ update-filled-column ] bi ;
120 : row-rect ( table row -- rect )
121 [ [ line-height ] dip * 0 swap 2array ]
122 [ drop [ dim>> first ] [ line-height ] bi 2array ] 2bi <rect> ;
124 : highlight-row ( table row color quot -- )
125 [ [ row-rect rect-bounds ] dip gl-color ] dip
126 '[ _ @ ] with-translation ; inline
128 : draw-selected-row ( table -- )
130 { [ dup selected-index>> not ] [ drop ] }
132 [ ] [ selected-index>> ] [ selection-color>> ] tri
133 [ gl-fill-rect ] highlight-row
137 : draw-focused-row ( table -- )
139 { [ dup focused?>> not ] [ drop ] }
140 { [ dup selected-index>> not ] [ drop ] }
142 [ ] [ selected-index>> ] [ focus-border-color>> ] tri
143 [ gl-rect ] highlight-row
147 : draw-moused-row ( table -- )
148 dup mouse-index>> dup [
149 over mouse-color>> [ gl-rect ] highlight-row
152 : column-line-offsets ( table -- xs )
153 [ column-widths>> ] [ gap>> ] bi
154 [ column-offsets nip [ f ] ]
155 [ 2/ '[ rest-slice [ _ - ] map ] ]
158 : draw-column-lines ( table -- )
159 [ column-line-color>> gl-color ]
161 [ column-line-offsets ] [ dim>> second ] bi
162 '[ [ 0 2array ] [ _ 2array ] bi gl-line ] each
165 : column-loc ( font column width align -- loc )
166 [ [ cell-width ] dip swap - ] dip
167 * >integer 0 2array ;
169 : translate-column ( width gap -- )
170 + 0 2array gl-translate ;
172 : draw-column ( font column width align gap -- )
175 [ 2dup ] 2dip column-loc
176 [ draw-cell ] with-translation
178 ] dip translate-column ;
180 : table-column-alignment ( table -- seq )
181 dup renderer>> column-alignment
182 [ ] [ column-widths>> length 0 <repetition> ] ?if ;
184 :: row-font ( row index table -- font )
186 row table renderer>> row-color [ >>foreground ] when*
187 index table selected-index>> = [ table selection-color>> >>background ] when ;
189 : draw-columns ( columns widths alignment font gap -- )
190 '[ [ _ ] 3dip _ draw-column ] 3each ;
192 M: table draw-line ( row index table -- )
195 [ renderer>> row-columns ]
197 [ table-column-alignment ]
204 M: table draw-gadget*
205 dup control-value empty? [ drop ] [
207 [ draw-selected-row ]
209 [ draw-column-lines ]
215 M: table line-height ( table -- y )
216 [ font>> ] [ renderer>> prototype-row ] bi
217 [ cell-height ] with [ max ] map-reduce ;
220 [ compute-column-widths drop ] keep
221 [ line-height ] [ control-value length ] bi * 2array ;
223 : nth-row ( row table -- value/f ? )
224 over [ control-value nth t ] [ 2drop f f ] if ;
228 : (selected-row) ( table -- value/f ? )
229 [ selected-index>> ] keep nth-row ;
231 : selected-row ( table -- value/f ? )
232 [ (selected-row) ] keep
233 swap [ renderer>> row-value t ] [ 2drop f f ] if ;
237 : update-selected-value ( table -- )
238 [ selected-row drop ] [ selected-value>> ] bi set-model ;
240 : initial-selected-index ( model table -- n/f )
241 [ value>> length 1 >= ] [ selection-required?>> ] bi* and 0 f ? ;
243 : show-row-summary ( table n -- )
245 [ swap [ renderer>> row-value ] keep show-summary ]
249 : hide-mouse-help ( table -- )
250 f >>mouse-index [ hide-status ] [ relayout-1 ] bi ;
252 M: table model-changed
253 [ nip ] [ initial-selected-index ] 2bi {
254 [ >>selected-index f >>mouse-index drop ]
256 [ drop update-selected-value ]
260 : thin-row-rect ( table row -- rect )
261 row-rect [ { 0 1 } v* ] change-dim ;
263 : (select-row) ( table n -- )
264 [ dup [ [ thin-row-rect ] [ drop ] 2bi scroll>rect ] [ 2drop ] if ]
265 [ >>selected-index relayout-1 ]
268 : mouse-row ( table -- n )
269 [ hand-rel second ] keep y>line ;
271 : table-button-down ( table -- )
272 dup takes-focus?>> [ dup request-focus ] when
273 dup control-value empty? [ drop ] [
274 dup [ mouse-row ] keep validate-line
275 [ >>mouse-index ] [ (select-row) ] bi
280 : row-action ( table -- )
282 [ swap [ action>> call ] [ dup hook>> call ] bi ]
288 : table-button-up ( table -- )
289 dup single-click?>> hand-click# get 2 = or
290 [ row-action ] [ update-selected-value ] if ;
292 : select-row ( table n -- )
295 [ drop update-selected-value ]
299 : prev/next-row ( table n -- )
300 [ dup selected-index>> ] dip '[ _ + ] [ 0 ] if* select-row ;
302 : previous-row ( table -- )
305 : next-row ( table -- )
308 : first-row ( table -- )
311 : last-row ( table -- )
312 dup control-value length 1- select-row ;
314 : prev/next-page ( table n -- )
315 over visible-lines 1- * prev/next-row ;
317 : previous-page ( table -- )
320 : next-page ( table -- )
323 : valid-row? ( row table -- ? )
324 control-value length 1- 0 swap between? ;
326 : if-mouse-row ( table true false -- )
327 [ [ mouse-row ] keep 2dup valid-row? ]
328 [ ] [ '[ nip @ ] ] tri* if ; inline
330 : show-mouse-help ( table -- )
333 [ >>mouse-index relayout-1 ]
336 ] [ hide-mouse-help ] if-mouse-row ;
338 : show-table-menu ( table -- )
344 [ renderer>> row-value ]
349 ] [ drop ] if-mouse-row ;
351 : focus-table ( table -- ) t >>focused? drop ;
353 : unfocus-table ( table -- ) f >>focused? drop ;
356 { mouse-enter show-mouse-help }
357 { mouse-leave hide-mouse-help }
358 { motion show-mouse-help }
359 { T{ button-down } table-button-down }
360 { T{ button-up } table-button-up }
361 { gain-focus focus-table }
362 { lose-focus unfocus-table }
363 { T{ drag } table-button-down }
367 { T{ button-down f f 3 } show-table-menu }
368 { T{ key-down f f "RET" } row-action }
369 { T{ key-down f f "UP" } previous-row }
370 { T{ key-down f f "DOWN" } next-row }
371 { T{ key-down f f "HOME" } first-row }
372 { T{ key-down f f "END" } last-row }
373 { T{ key-down f f "PAGE_UP" } previous-page }
374 { T{ key-down f f "PAGE_DOWN" } next-page }
377 TUPLE: column-headers < gadget table ;
379 : <column-headers> ( table -- gadget )
382 column-title-background <solid> >>interior ;
384 : draw-column-titles ( table -- )
386 [ renderer>> column-titles ]
388 [ table-column-alignment ]
389 [ font>> column-title-font ]
394 M: column-headers draw-gadget*
395 table>> draw-column-titles ;
397 M: column-headers pref-dim*
398 table>> [ pref-dim first ] [ font>> "" text-height ] bi 2array ;
400 M: table viewport-column-header
401 dup renderer>> column-titles
402 [ <column-headers> ] [ drop f ] if ;