]> gitweb.factorcode.org Git - factor.git/commitdiff
multiple selection in table views
authorSam Anklesaria <sam@Tintin.local>
Wed, 13 May 2009 21:10:04 +0000 (16:10 -0500)
committerSam Anklesaria <sam@Tintin.local>
Wed, 13 May 2009 21:10:04 +0000 (16:10 -0500)
basis/ui/gadgets/tables/tables-docs.factor
basis/ui/gadgets/tables/tables.factor
extra/ui/frp/frp.factor

index c064a80ee4bb6649f8a60e287ac6725229801e73..4f016caa8a5217fcdaa3fa0e8a943acf97e4ccc9 100644 (file)
@@ -20,13 +20,15 @@ ARTICLE: "ui.gadgets.tables.selection" "Table row selection"
 $nl
 "A few slots in the table gadget concern row selection:"
 { $table
-  { { $slot "selected-value" } { " - if set to a model, the currently selected row's value, as determined by a " { $link row-value } " call to the renderer, is stored in this model. See " { $link "models" } "." } }
-  { { $slot "selected-index" } " - the index of the currently selected row." }
+  { { $slot "selected-values" } { " - if set to a model, an array of the currently selected rows' values, as determined by a " { $link row-value } " call to the renderer, is stored in this model. See " { $link "models" } "." } }
+  { { $slot "selected-indices" } " - the indices of the currently selected rows." }
   { { $slot "selection-required?" } { " - if set to a true value, the table ensures that some row is always selected, if the model is non-empty. If set to " { $link f } ", a state where nothing is selected is permitted to occur. The default is " { $link f } "." } }
+  { { $slot "multiple-selection?" } { " - if set to a true value, users are allowed to select more than one value." } }
+
 }
 "Some words for row selection:"
-{ $subsection selected-row }
-{ $subsection (selected-row) } ;
+{ $subsection selected-rows }
+{ $subsection (selected-rows) } ;
 
 ARTICLE: "ui.gadgets.tables.actions" "Table row actions"
 "When the user double-clicks on a row, or presses " { $command table "row" row-action } " while a row is selected, optional action and hook quotations are invoked. The action receives the row value and the hook receives the table gadget itself. These quotations are stored in the " { $slot "action" } " and " { $snippet "hook" } " slots of a table, respectively."
index ba3b5a2f789bba08637e2392e6ad49e02d80df14..80f2ca400f9567327c962bdedc8f2a6ec21f8ae1 100644 (file)
@@ -1,12 +1,12 @@
 ! Copyright (C) 2008, 2009 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: accessors arrays colors colors.constants fry kernel math
-math.functions math.rectangles math.order math.vectors namespaces
-opengl sequences ui.gadgets ui.gadgets.scrollers ui.gadgets.status-bar
-ui.gadgets.worlds ui.gestures ui.render ui.pens.solid ui.text
-ui.commands ui.images ui.gadgets.menus ui.gadgets.line-support
+math.functions math.ranges math.rectangles math.order math.vectors models.arrow
+namespaces opengl sequences ui.gadgets ui.gadgets.scrollers
+ui.gadgets.status-bar ui.gadgets.worlds ui.gestures ui.render ui.pens.solid
+ui.text ui.commands ui.images ui.gadgets.menus ui.gadgets.line-support
 math.rectangles models math.ranges sequences combinators
-combinators.short-circuit fonts locals strings ;
+combinators.short-circuit fonts locals strings vectors ;
 IN: ui.gadgets.tables
 
 ! Row rendererer protocol
@@ -41,16 +41,33 @@ focus-border-color
 { mouse-color initial: COLOR: black }
 column-line-color
 selection-required?
-selected-index selected-value
+selected-indices selected-values
 mouse-index
 { takes-focus? initial: t }
-focused? ;
+focused?
+multiple-selection? ;
+
+IN: accessors
+GENERIC: selected-value>> ( table -- n )
+GENERIC: selected-index>> ( table -- n )
+GENERIC: (>>selected-index) ( n table -- )
+GENERIC: (>>selected-value) ( val table -- )
+: >>selected-index ( table n -- table ) over (>>selected-index) ;
+: >>selected-value ( table val -- table ) over (>>selected-value) ;
+
+M: table selected-value>> selected-values>> [ [ peek ] [ f ] if* ] <arrow> ;
+M: table (>>selected-value) [ [ 1vector ] <arrow> ] dip (>>selected-values) ;
+M: table selected-index>> selected-indices>> [ peek ] [ f ] if* ;
+M: table (>>selected-index) [ 1vector ] dip (>>selected-indices) ;
+
+IN: ui.gadgets.tables
+: push-selected-index ( table n -- table ) over selected-indices>> push ;
 
 : new-table ( rows renderer class -- table )
     new-line-gadget
         swap >>renderer
         swap >>model
-        f <model> >>selected-value
+        f <model> >>selected-values
         sans-serif-font >>font
         focus-border-color >>focus-border-color
         transparent >>column-line-color ; inline
@@ -131,12 +148,12 @@ M: table layout*
 : row-bounds ( table row -- loc dim )
     row-rect rect-bounds ; inline
 
-: draw-selected-row ( table -- )
+: draw-selected-rows ( table -- )
     {
-        { [ dup selected-index>> not ] [ drop ] }
+        { [ dup selected-indices>> not ] [ drop ] }
         [
-            [ ] [ selected-index>> ] [ selection-color>> gl-color ] tri
-            row-bounds gl-fill-rect
+            [ selected-indices>> ] [ selection-color>> gl-color ] [ ] tri
+            [ swap row-bounds gl-fill-rect ] curry each
         ]
     } cond ;
 
@@ -189,10 +206,10 @@ M: table layout*
     dup renderer>> column-alignment
     [ ] [ column-widths>> length 0 <repetition> ] ?if ;
 
-:: row-font ( row index table -- font )
+:: row-font ( row ind table -- font )
     table font>> clone
     row table renderer>> row-color [ >>foreground ] when*
-    index table selected-index>> = [ table selection-color>> >>background ] when ;
+    ind table selected-indices>> index [ table selection-color>> >>background ] when ;
 
 : draw-columns ( columns widths alignment font gap -- )
     '[ [ _ ] 3dip _ draw-column ] 3each ;
@@ -213,7 +230,7 @@ M: table draw-gadget*
     dup control-value empty? [ drop ] [
         dup line-height \ line-height [
             {
-                [ draw-selected-row ]
+                [ draw-selected-rows ]
                 [ draw-lines ]
                 [ draw-column-lines ]
                 [ draw-focused-row ]
@@ -236,17 +253,19 @@ M: table pref-dim*
 
 PRIVATE>
 
-: (selected-row) ( table -- value/f ? )
-    [ selected-index>> ] keep nth-row ;
+: (selected-rows) ( table -- {row} )
+    [ selected-indices>> ] keep
+    [ nth-row [ 1array ] [ drop { } ] if ] curry map concat ;
+
+: selected-rows ( table -- {value} )
+    [ (selected-rows) ] [ renderer>> ] bi [ row-value ] curry map ;
 
-: selected-row ( table -- value/f ? )
-    [ (selected-row) ] keep
-    swap [ renderer>> row-value t ] [ 2drop f f ] if ;
+: selected-row ( table -- value ? ) selected-rows [ f f ] [ peek t ] if-empty ;
 
 <PRIVATE
 
-: update-selected-value ( table -- )
-    [ selected-row drop ] [ selected-value>> ] bi set-model ;
+: update-selected-values ( table -- )
+    [ selected-rows ] [ selected-values>> ] bi set-model ;
 
 : show-row-summary ( table n -- )
     over nth-row
@@ -260,54 +279,68 @@ PRIVATE>
 : find-row-index ( value table -- n/f )
     [ model>> value>> ] [ renderer>> '[ _ row-value ] map index ] bi ;
 
-: initial-selected-index ( table -- n/f )
+: initial-selected-indices ( table -- n/f )
     {
         [ model>> value>> empty? not ]
         [ selection-required?>> ]
-        [ drop 0 ]
+        [ drop V{ 0 } ]
     } 1&& ;
 
-: (update-selected-index) ( table -- n/f )
-    [ selected-value>> value>> ] keep over
-    [ find-row-index ] [ 2drop f ] if ;
+: (update-selected-indices) ( table -- {n}/f )
+    [ selected-values>> value>> ] keep
+    [ find-row-index ] curry map [ ] filter [ f ] when-empty ;
 
-: update-selected-index ( table -- n/f )
+: update-selected-indices ( table -- {n}/f )
     {
-        [ (update-selected-index) ]
-        [ initial-selected-index ]
+        [ (update-selected-indices) ]
+        [ initial-selected-indices ]
     } 1|| ;
 
 M: table model-changed
-    nip dup update-selected-index {
-        [ >>selected-index f >>mouse-index drop ]
-        [ show-row-summary ]
-        [ drop update-selected-value ]
+    nip dup update-selected-indices {
+        [ >>selected-indices f >>mouse-index drop ]
+        [ peek show-row-summary ]
+        [ drop update-selected-values ]
         [ drop relayout ]
     } 2cleave ;
 
 : thin-row-rect ( table row -- rect )
     row-rect [ { 0 1 } v* ] change-dim ;
 
+: scroll-to-row ( table n -- )
+    dup [ [ thin-row-rect ] [ drop ] 2bi scroll>rect ] [ 2drop ] if ;
+
+: add-selected-row ( table n -- )
+    [ scroll-to-row ]
+    [ push-selected-index relayout-1 ] 2bi ;
+
 : (select-row) ( table n -- )
-    [ dup [ [ thin-row-rect ] [ drop ] 2bi scroll>rect ] [ 2drop ] if ]
+    [ scroll-to-row ]
     [ >>selected-index relayout-1 ]
     2bi ;
 
 : mouse-row ( table -- n )
     [ hand-rel second ] keep y>line ;
 
-: if-mouse-row ( table true: ( table mouse-index -- ) false: ( table -- ) -- )
+: if-mouse-row ( table true: ( mouse-index table -- ) false: ( table -- ) -- )
     [ [ mouse-row ] keep 2dup valid-line? ]
     [ ] [ '[ nip @ ] ] tri* if ; inline
 
-: table-button-down ( table -- )
-    dup takes-focus?>> [ dup request-focus ] when
-    [ swap [ >>mouse-index ] [ (select-row) ] bi ] [ drop ] if-mouse-row ;
+: (table-button-down) ( quot table -- )
+    dup takes-focus?>> [ dup request-focus ] when swap
+   '[ swap [ >>mouse-index ] _ bi ] [ drop ] if-mouse-row ; inline
+
+: table-button-down ( table -- ) [ (select-row) ] swap (table-button-down) ;
+: continued-button-down ( table -- ) dup multiple-selection?>> [ [ add-selected-row ] swap (table-button-down) ] [ table-button-down ] if ;
+: thru-button-down ( table -- ) dup multiple-selection?>> [
+    [ over selected-index>> (a,b] over
+      [ swap push-selected-index drop ] curry each continued-button-down ]
+    swap (table-button-down) ] [ table-button-down ] if ;
 
 PRIVATE>
 
 : row-action ( table -- )
-    dup selected-row
+    dup [ selected-rows peek ]
     [ swap [ action>> call( value -- ) ] [ dup hook>> call( table -- ) ] bi ]
     [ 2drop ]
     if ;
@@ -319,14 +352,14 @@ PRIVATE>
 <PRIVATE
 
 : table-button-up ( table -- )
-    dup row-action? [ row-action ] [ update-selected-value ] if ;
+    dup row-action? [ row-action ] [ update-selected-values ] if ;
 
 PRIVATE>
 
 : select-row ( table n -- )
     over validate-line
     [ (select-row) ]
-    [ drop update-selected-value ]
+    [ drop update-selected-values ]
     [ show-row-summary ]
     2tri ;
 
@@ -385,6 +418,8 @@ table "sundry" f {
     { mouse-enter show-mouse-help }
     { mouse-leave hide-mouse-help }
     { motion show-mouse-help }
+    { T{ button-down f { A+ } 1 } continued-button-down }
+    { T{ button-down f { S+ } 1 } thru-button-down }
     { T{ button-down } table-button-down }
     { T{ button-up } table-button-up }
     { gain-focus focus-table }
index 68ae2132be56098791117d1553d9e3cdafc8fb07..82cf549ef77bbe81abc0fe61b2416ace2bbcf7f7 100644 (file)
@@ -60,7 +60,7 @@ M: frp-table row-color color-quot>> [ call( a -- b ) ]  [ drop f ] if* ;
 
 : <frp-table> ( model -- table )
     frp-table new-line-gadget dup >>renderer swap >>model
-    f basic-model new-model >>selected-value sans-serif-font >>font
+    f basic-model new-model >>selected-values sans-serif-font >>font
     focus-border-color >>focus-border-color
     transparent >>column-line-color ;
 : <frp-table*> ( -- table ) f <model> <frp-table> ;
@@ -74,7 +74,7 @@ TUPLE: layout gadget width ; C: <layout> layout
 
 GENERIC: output-model ( gadget -- model )
 M: gadget output-model model>> ;
-M: frp-table output-model selected-value>> ;
+M: table output-model dup multiple-selection?>> [ selected-values>> ] [ selected-value>> ] if ;
 M: model-field output-model field-model>> ;
 M: scroller output-model viewport>> children>> first output-model ;