split + renamed ui.frp for better integration with other libs

db4
Sam Anklesaria 2009-08-01 15:18:24 -05:00
parent 6de5f0964b
commit 7f33da63ce
23 changed files with 255 additions and 257 deletions

View File

@ -1,6 +1,6 @@
USING: accessors arrays delegate delegate.protocols USING: accessors arrays delegate delegate.protocols
io.pathnames kernel locals sequences io.pathnames kernel locals sequences
vectors make strings ui.frp.signals ui.frp.gadgets vectors make strings models.combinators ui.gadgets.controls
sequences.extras ; sequences.extras ;
IN: file-trees IN: file-trees
@ -44,6 +44,6 @@ DEFER: (tree-insert)
go-to-path ; go-to-path ;
: <dir-table> ( tree-model -- table ) : <dir-table> ( tree-model -- table )
<frp-list*> [ node>> 1array ] >>quot <list*> [ node>> 1array ] >>quot
[ selected-value>> [ file? not ] <filter> swap <switch> ] [ selected-value>> [ file? not ] filter-model swap switch-models ]
[ swap >>model ] bi ; [ swap >>model ] bi ;

View File

@ -0,0 +1,41 @@
USING: help.markup help.syntax models models.arrow sequences monads ;
IN: models.combinators
HELP: merge
{ $values { "models" "a list of models" } { "model" basic-model } }
{ $description "Creates a model that merges the updates of others" } ;
HELP: filter-model
{ $values { "model" model } { "quot" "quotation with stack effect ( a b -- c )" } { "filter-model" filter-model } }
{ $description "Creates a model that uses the updates of another model only when they satisfy a given predicate" } ;
HELP: fold
{ $values { "oldval" "starting value" } { "quot" "applied to update and previous values" } { "model" model } { "model" model } }
{ $description "Similar to " { $link reduce } " but works on models, applying a quotation to the previous and new values at each update" } ;
HELP: switch-models
{ $values { "model1" model } { "model2" model } { "model'" model } }
{ $description "Creates a model that starts with the behavior of model2 and switches to the behavior of model1 on its update" } ;
HELP: <mapped>
{ $values { "model" model } { "quot" "applied to model's value on updates" } { "model" model } }
{ $description "An expanded version of " { $link <arrow> } ". Use " { $link fmap } " instead." } ;
HELP: when-model
{ $values { "model" model } { "quot" "called on the model if the quot yields true" } { "cond" "a quotation called on the model's value, yielding a boolean value" } }
{ $description "Calls quot when model updates if its value meets the condition set in cond" } ;
HELP: with-self
{ $values { "quot" "quotation that recieves its own return value" } { "model" model } }
{ $description "Fixed points for models: the quot reacts to the same model to gives" } ;
HELP: #1
{ $values { "model" model } { "model'" model } }
{ $description "Moves a model to the top of its dependencies' connections, thus being notified before the others" } ;
ARTICLE: "models.combinators" "Extending models"
"The " { $vocab-link "models.combinators" } " library expands models to have discrete start and end times. "
"Also, it provides methods of manipulating and combining models, expecially useful when programming user interfaces: "
"The output models of some gadgets (see " { $vocab-link "ui.gadgets.controls" } " ) can be manipulated and used as the input models of others. " ;
ABOUT: "models.combinators"

View File

@ -1,7 +1,7 @@
USING: accessors arrays kernel models models.product monads USING: accessors arrays kernel models models.product monads
sequences sequences.extras ; sequences sequences.extras ;
FROM: syntax => >> ; FROM: syntax => >> ;
IN: ui.frp.signals IN: models.combinators
TUPLE: multi-model < model important? ; TUPLE: multi-model < model important? ;
GENERIC: (model-changed) ( model observer -- ) GENERIC: (model-changed) ( model observer -- )
@ -17,18 +17,18 @@ IN: models
dup connections>> dup [ dup multi-model? [ important?>> ] [ drop f ] if ] find-all dup connections>> dup [ dup multi-model? [ important?>> ] [ drop f ] if ] find-all
[ second tuck [ remove ] dip prefix ] each [ second tuck [ remove ] dip prefix ] each
[ model-changed ] with each ; [ model-changed ] with each ;
IN: ui.frp.signals IN: models.combinators
TUPLE: basic-model < multi-model ; TUPLE: basic-model < multi-model ;
M: basic-model (model-changed) [ value>> ] dip set-model ; M: basic-model (model-changed) [ value>> ] dip set-model ;
: <merge> ( models -- signal ) basic-model <multi-model> ; : merge ( models -- model ) basic-model <multi-model> ;
: <2merge> ( model1 model2 -- signal ) 2array <merge> ; : 2merge ( model1 model2 -- model ) 2array merge ;
: <basic> ( value -- signal ) basic-model new-model ; : <basic> ( value -- model ) basic-model new-model ;
TUPLE: filter-model < multi-model quot ; TUPLE: filter-model < multi-model quot ;
M: filter-model (model-changed) [ value>> ] dip 2dup quot>> call( a -- ? ) M: filter-model (model-changed) [ value>> ] dip 2dup quot>> call( a -- ? )
[ set-model ] [ 2drop ] if ; [ set-model ] [ 2drop ] if ;
: <filter> ( model quot -- filter-signal ) [ 1array filter-model <multi-model> ] dip >>quot ; : filter-model ( model quot -- filter-model ) [ 1array \ filter-model <multi-model> ] dip >>quot ;
TUPLE: fold-model < multi-model quot base values ; TUPLE: fold-model < multi-model quot base values ;
M: fold-model (model-changed) 2dup base>> = M: fold-model (model-changed) 2dup base>> =
@ -38,16 +38,16 @@ M: fold-model (model-changed) 2dup base>> =
] if ; ] if ;
M: fold-model model-activated drop ; M: fold-model model-activated drop ;
: new-fold-model ( deps -- model ) fold-model <multi-model> V{ } clone >>values ; : new-fold-model ( deps -- model ) fold-model <multi-model> V{ } clone >>values ;
: <fold> ( model oldval quot -- signal ) rot 1array new-fold-model swap >>quot : fold ( model oldval quot -- model ) rot 1array new-fold-model swap >>quot
swap >>value ; swap >>value ;
: <fold*> ( model oldmodel quot -- signal ) over [ [ 2array new-fold-model ] dip >>quot ] : fold* ( model oldmodel quot -- model ) over [ [ 2array new-fold-model ] dip >>quot ]
dip [ >>base ] [ value>> >>value ] bi ; dip [ >>base ] [ value>> >>value ] bi ;
TUPLE: updater-model < multi-model values updates ; TUPLE: updater-model < multi-model values updates ;
M: updater-model (model-changed) [ tuck updates>> = M: updater-model (model-changed) [ tuck updates>> =
[ [ values>> value>> ] keep set-model ] [ [ values>> value>> ] keep set-model ]
[ drop ] if ] keep f swap (>>value) ; [ drop ] if ] keep f swap (>>value) ;
: <updates> ( values updates -- signal ) [ 2array updater-model <multi-model> ] 2keep : updates ( values updates -- model ) [ 2array updater-model <multi-model> ] 2keep
[ >>values ] [ >>updates ] bi* ; [ >>values ] [ >>updates ] bi* ;
SYMBOL: switch SYMBOL: switch
@ -55,7 +55,7 @@ TUPLE: switch-model < multi-model original switcher on ;
M: switch-model (model-changed) 2dup switcher>> = M: switch-model (model-changed) 2dup switcher>> =
[ [ value>> ] dip over switch = [ nip [ original>> ] keep f >>on model-changed ] [ t >>on set-model ] if ] [ [ value>> ] dip over switch = [ nip [ original>> ] keep f >>on model-changed ] [ t >>on set-model ] if ]
[ dup on>> [ 2drop ] [ [ value>> ] dip over [ set-model ] [ 2drop ] if ] if ] if ; [ dup on>> [ 2drop ] [ [ value>> ] dip over [ set-model ] [ 2drop ] if ] if ] if ;
: <switch> ( signal1 signal2 -- signal' ) swap [ 2array switch-model <multi-model> ] 2keep : switch-models ( model1 model2 -- model' ) swap [ 2array switch-model <multi-model> ] 2keep
[ [ value>> >>value ] [ >>original ] bi ] [ >>switcher ] bi* ; [ [ value>> >>value ] [ >>original ] bi ] [ >>switcher ] bi* ;
M: switch-model model-activated [ original>> ] keep model-changed ; M: switch-model model-activated [ original>> ] keep model-changed ;
: >behavior ( event -- behavior ) t >>value ; : >behavior ( event -- behavior ) t >>value ;
@ -63,7 +63,7 @@ M: switch-model model-activated [ original>> ] keep model-changed ;
TUPLE: mapped-model < multi-model model quot ; TUPLE: mapped-model < multi-model model quot ;
: new-mapped-model ( model quot class -- mapped-model ) [ over 1array ] dip : new-mapped-model ( model quot class -- mapped-model ) [ over 1array ] dip
<multi-model> swap >>quot swap >>model ; <multi-model> swap >>quot swap >>model ;
: <mapped> ( model quot -- signal ) mapped-model new-mapped-model ; : <mapped> ( model quot -- model ) mapped-model new-mapped-model ;
M: mapped-model (model-changed) M: mapped-model (model-changed)
[ [ value>> ] [ quot>> ] bi* call( old -- new ) ] [ nip ] 2bi [ [ value>> ] [ quot>> ] bi* call( old -- new ) ] [ nip ] 2bi
set-model ; set-model ;
@ -81,7 +81,7 @@ M: action-value model-activated dup parent>> dup activate-model model-changed ;
TUPLE: action < multi-model quot ; TUPLE: action < multi-model quot ;
M: action (model-changed) [ [ value>> ] [ quot>> ] bi* call( a -- b ) ] keep value>> M: action (model-changed) [ [ value>> ] [ quot>> ] bi* call( a -- b ) ] keep value>>
[ swap add-connection ] 2keep model-changed ; [ swap add-connection ] 2keep model-changed ;
: <action> ( model quot -- action-signal ) [ 1array action <multi-model> ] dip >>quot dup f <action-value> >>value value>> ; : <action> ( model quot -- action-model ) [ 1array action <multi-model> ] dip >>quot dup f <action-value> >>value value>> ;
TUPLE: collection < multi-model ; TUPLE: collection < multi-model ;
: <collection> ( models -- product ) collection <multi-model> ; : <collection> ( models -- product ) collection <multi-model> ;
@ -93,13 +93,13 @@ M: collection (model-changed)
M: collection model-activated dup (model-changed) ; M: collection model-activated dup (model-changed) ;
! for side effects ! for side effects
TUPLE: (frp-when) < multi-model quot cond ; TUPLE: (when-model) < multi-model quot cond ;
: frp-when ( model quot cond -- model ) rot 1array (frp-when) <multi-model> swap >>cond swap >>quot ; : when-model ( model quot cond -- model ) rot 1array (when-model) <multi-model> swap >>cond swap >>quot ;
M: (frp-when) (model-changed) [ quot>> ] 2keep M: (when-model) (model-changed) [ quot>> ] 2keep
[ value>> ] [ cond>> ] bi* call( a -- ? ) [ call( model -- ) ] [ 2drop ] if ; [ value>> ] [ cond>> ] bi* call( a -- ? ) [ call( model -- ) ] [ 2drop ] if ;
! only used in construction ! only used in construction
: with-self ( quot: ( model -- model ) -- model ) [ f <basic> dup ] dip call swap [ add-dependency ] keep ; inline : with-self ( quot: ( model -- model ) -- model ) [ f <basic> dup ] dip call swap [ add-dependency ] keep ; inline
USE: ui.frp.signals.templates USE: models.combinators.templates
<< { "$>" "<$" "fmap" } [ fmaps ] each >> << { "$>" "<$" "fmap" } [ fmaps ] each >>

View File

@ -0,0 +1 @@
Model combination and manipulation

View File

@ -1,6 +1,6 @@
USING: kernel sequences functors fry macros generalizations ; USING: kernel sequences functors fry macros generalizations ;
IN: ui.frp.signals.templates IN: models.combinators.templates
FROM: ui.frp.signals => <collection> #1 ; FROM: models.combinators => <collection> #1 ;
FUNCTOR: fmaps ( W -- ) FUNCTOR: fmaps ( W -- )
W IS ${W} W IS ${W}
w-n DEFINES ${W}-n w-n DEFINES ${W}-n

View File

@ -25,12 +25,11 @@ SYMBOL: serving-vocabs serving-vocabs [ V{ } clone ] initialize
: register-loads-thread ( -- ) : register-loads-thread ( -- )
[ [ receive vocab ] keep reply-synchronous t ] "load-words" spawn-server "loads-thread" swap register-process ; [ [ receive vocab ] keep reply-synchronous t ] "load-words" spawn-server "loads-thread" swap register-process ;
: add-vocabs-hook ( -- ) PRIVATE>
[ 9012 start-node SYNTAX: service current-vocab name>> serving-vocabs get-global adjoin ;
[ 9012 start-node
register-gets-thread register-gets-thread
register-does-thread register-does-thread
register-loads-thread register-loads-thread
] "start-serving-vocabs" add-init-hook ; ] "start-serving-vocabs" add-init-hook
PRIVATE>
SYNTAX: service add-vocabs-hook
current-vocab name>> serving-vocabs get-global adjoin ;

View File

@ -1,14 +1,14 @@
USING: accessors arrays colors.constants combinators db.queries USING: accessors arrays colors.constants combinators db.queries
db.info db.tuples db.types kernel locals math db.sqlite db.tuples db.types kernel locals math
monads persistency sequences sequences.extras ui ui.frp.gadgets monads persistency sequences sequences.extras ui ui.gadgets.controls
ui.frp.layout ui.frp.signals ui.gadgets.labels ui.gadgets.layout models.combinators ui.gadgets.labels
ui.gadgets.scrollers ui.pens.solid ; ui.gadgets.scrollers ui.pens.solid io.files.temp ;
FROM: sets => prune ; FROM: sets => prune ;
IN: recipes IN: recipes
STORED-TUPLE: recipe { title { VARCHAR 100 } } { votes INTEGER } { txt TEXT } { genre { VARCHAR 100 } } ; STORED-TUPLE: recipe { title { VARCHAR 100 } } { votes INTEGER } { txt TEXT } { genre { VARCHAR 100 } } ;
: <recipe> ( title genre text -- recipe ) recipe new swap >>txt swap >>genre swap >>title 0 >>votes ; : <recipe> ( title genre text -- recipe ) recipe new swap >>txt swap >>genre swap >>title 0 >>votes ;
get-psql-info recipe define-db "recipes.db" temp-file <sqlite-db> recipe define-db
: top-recipes ( offset search -- recipes ) <query> T{ recipe } rot >>title >>tuple : top-recipes ( offset search -- recipes ) <query> T{ recipe } rot >>title >>tuple
"votes" >>order 30 >>limit swap >>offset get-tuples ; "votes" >>order 30 >>limit swap >>offset get-tuples ;
: top-genres ( -- genres ) f f top-recipes [ genre>> ] map prune 4 (head-slice) ; : top-genres ( -- genres ) f f top-recipes [ genre>> ] map prune 4 (head-slice) ;
@ -25,37 +25,37 @@ get-psql-info recipe define-db
$ BODY $ $ BODY $
$ BUTTON $ $ BUTTON $
] <vbox> , ] <vbox> ,
] <frp-book*> { 350 245 } >>pref-dim ; ] <book*> { 350 245 } >>pref-dim ;
:: recipe-browser ( -- ) [ [ :: recipe-browser ( -- ) [ [
interface interface
<frp-table*> :> tbl <table*> :> tbl
"okay" <frp-border-button> BUTTON -> :> ok "okay" <model-border-btn> BUTTON -> :> ok
IMG-FRP-BTN: submit [ store-tuple ] >>value TOOLBAR -> :> submit IMG-MODEL-BTN: submit [ store-tuple ] >>value TOOLBAR -> :> submit
IMG-FRP-BTN: love 1 >>value TOOLBAR -> IMG-MODEL-BTN: love 1 >>value TOOLBAR ->
IMG-FRP-BTN: hate -1 >>value -> 2array <merge> :> votes IMG-MODEL-BTN: hate -1 >>value -> 2array merge :> votes
IMG-FRP-BTN: back -> [ -30 ] <$ IMG-MODEL-BTN: back -> [ -30 ] <$
IMG-FRP-BTN: more -> [ 30 ] <$ 2array <merge> :> viewed IMG-MODEL-BTN: more -> [ 30 ] <$ 2array merge :> viewed
<spacer> <frp-field*> ->% 1 :> search <spacer> <model-field*> ->% 1 :> search
submit ok [ [ drop ] ] <$ 2array <merge> [ drop ] >>value :> quot submit ok [ [ drop ] ] <$ 2array merge [ drop ] >>value :> quot
viewed 0 [ + ] <fold> search ok t <basic> "all" <frp-button> ALL -> viewed 0 [ + ] fold search ok t <basic> "all" <model-btn> ALL ->
tbl selected-value>> votes [ [ + ] curry change-votes modify-tuple ] 2$> tbl selected-value>> votes [ [ + ] curry change-votes modify-tuple ] 2$>
4array <merge> 4array merge
[ drop [ f ] [ "%" dup surround <pattern> ] if-empty top-recipes ] 3fmap :> updates [ drop [ f ] [ "%" dup surround <pattern> ] if-empty top-recipes ] 3fmap :> ups
updates [ top-genres [ <frp-button> GENRES -> ] map <merge> ] bind* ups [ top-genres [ <model-btn> GENRES -> ] map merge ] bind*
[ text>> T{ recipe } swap >>genre get-tuples ] fmap [ text>> T{ recipe } swap >>genre get-tuples ] fmap
tbl swap updates 2array <merge> >>model tbl swap ups 2merge >>model
[ [ title>> ] [ genre>> ] bi 2array ] >>quot [ [ title>> ] [ genre>> ] bi 2array ] >>quot
{ "Title" "Genre" } >>column-titles dup <scroller> RECIPES ,% 1 actions>> { "Title" "Genre" } >>column-titles dup <scroller> RECIPES ,% 1 actions>>
submit [ "" dup dup <recipe> ] <$ 2array <merge> submit [ "" dup dup <recipe> ] <$ 2array merge
{ [ [ title>> ] fmap <frp-field> TITLE ->% .5 ] { [ [ title>> ] fmap <model-field> TITLE ->% .5 ]
[ [ genre>> ] fmap <frp-field> GENRE ->% .5 ] [ [ genre>> ] fmap <model-field> GENRE ->% .5 ]
[ [ txt>> ] fmap <frp-editor> BODY ->% 1 ] [ [ txt>> ] fmap <model-editor> BODY ->% 1 ]
} cleave } cleave
[ <recipe> ] 3fmap [ <recipe> ] 3fmap
[ [ 1 ] <$ ] [ [ 1 ] <$ ]
[ quot ok <updates> #1 [ call( recipe -- ) 0 ] 2fmap ] bi [ quot ok updates #1 [ call( recipe -- ) 0 ] 2fmap ] bi
2array <merge> 0 <basic> <switch> >>model 2merge 0 <basic> switch-models >>model
] with-interface "recipes" open-window ] with-ui ; ] with-interface "recipes" open-window ] with-ui ;
MAIN: recipe-browser MAIN: recipe-browser

View File

@ -1,8 +1,8 @@
USING: accessors arrays combinators.short-circuit grouping kernel lists USING: accessors arrays combinators.short-circuit grouping kernel lists
lists.lazy locals math math.functions math.parser math.ranges lists.lazy locals math math.functions math.parser math.ranges
models.product monads random sequences sets ui ui.frp.gadgets models.product monads random sequences sets ui ui.gadgets.controls
ui.frp.layout ui.frp.signals ui.gadgets.alerts vectors fry ui.gadgets.layout models.combinators ui.gadgets.alerts vectors fry
ui.gadgets.labels memoize ; ui.gadgets.labels ;
IN: sudokus IN: sudokus
: row ( index -- row ) 1 + 9 / ceiling ; : row ( index -- row ) 1 + 9 / ceiling ;
@ -11,28 +11,28 @@ IN: sudokus
: near ( a pos -- ? ) { [ [ row ] bi@ = ] [ [ col ] bi@ = ] [ [ sq ] bi@ = ] } 2|| ; : near ( a pos -- ? ) { [ [ row ] bi@ = ] [ [ col ] bi@ = ] [ [ sq ] bi@ = ] } 2|| ;
: nth-or-lower ( n seq -- elt ) [ length 1 - 2dup > [ nip ] [ drop ] if ] keep nth ; : nth-or-lower ( n seq -- elt ) [ length 1 - 2dup > [ nip ] [ drop ] if ] keep nth ;
MEMO:: solutions ( puzzle random? -- solutions ) :: solutions ( puzzle random? -- solutions )
f puzzle random? [ indices [ f ] [ random? swap nth-or-lower ] if-empty ] [ index ] if f puzzle random? [ indices [ f ] [ random? swap nth-or-lower ] if-empty ] [ index ] if
[ :> pos [ :> pos
1 9 [a,b] 80 iota [ pos near ] filter [ puzzle nth ] map prune diff 1 9 [a,b] 80 iota [ pos near ] filter [ puzzle nth ] map prune diff
[ 1array puzzle pos cut-slice rest surround ] map >list [ random? solutions ] bind [ 1array puzzle pos cut-slice rest surround ] map >list [ random? solutions ] bind
] [ puzzle list-monad return ] if* ; ] [ puzzle list-monad return ] if* ;
: solution ( puzzle random? -- solution ) dupd solutions dup +nil+ = [ drop "Unsolvable" alert* ] [ nip car ] if \ solutions reset-memoized ; : solution ( puzzle random? -- solution ) dupd solutions dup +nil+ = [ drop "Unsolvable" alert* ] [ nip car ] if ;
: hint ( puzzle -- puzzle' ) [ [ f swap indices random dup ] [ f solution ] bi nth ] keep swapd >vector [ set-nth ] keep ; : hint ( puzzle -- puzzle' ) [ [ f swap indices random dup ] [ f solution ] bi nth ] keep swapd >vector [ set-nth ] keep ;
: create ( difficulty -- puzzle ) 81 [ f ] replicate : create ( difficulty -- puzzle ) 81 [ f ] replicate
40 random solution [ [ dup length random f spin set-nth ] curry times ] keep ; 40 random solution [ [ dup length random f spin set-nth ] curry times ] keep ;
: do-sudoku ( -- ) [ [ : do-sudoku ( -- ) [ [
[ [
81 [ "" ] replicate <basic> <switch> [ [ <basic> ] map 9 group [ 3 group ] map 3 group 81 [ "" ] replicate <basic> switch-models [ [ <basic> ] map 9 group [ 3 group ] map 3 group
[ [ [ <spacer> [ [ <frp-field> ->% 2 [ string>number ] fmap ] [ [ [ <spacer> [ [ <model-field> ->% 2 [ string>number ] fmap ]
map <spacer> ] map concat ] <hbox> , ] map concat <spacer> ] map concat <product> map <spacer> ] map concat ] <hbox> , ] map concat <spacer> ] map concat <product>
[ "Difficulty:" <label> , "1" <basic> <frp-field> -> [ string>number 1 or 1 + 10 * ] fmap [ "Difficulty:" <label> , "1" <basic> <model-field> -> [ string>number 1 or 1 + 10 * ] fmap
"Generate" <frp-border-button> -> <updates> [ create ] fmap <spacer> "Generate" <model-border-btn> -> updates [ create ] fmap <spacer>
"Hint" <frp-border-button> -> "Solve" <frp-border-button> -> ] <hbox> , "Hint" <model-border-btn> -> "Solve" <model-border-btn> -> ] <hbox> ,
roll [ swap <updates> ] curry bi@ roll [ swap updates ] curry bi@
[ [ hint ] fmap ] [ [ f solution ] fmap ] bi* 3array <merge> [ [ [ number>string ] [ "" ] if* ] map ] fmap [ [ hint ] fmap ] [ [ f solution ] fmap ] bi* 3array merge [ [ [ number>string ] [ "" ] if* ] map ] fmap
] bind ] bind
] with-self , ] <vbox> { 280 220 } >>pref-dim ] with-self , ] <vbox> { 280 220 } >>pref-dim
"Sudoku Sleuth" open-window ] with-ui ; "Sudoku Sleuth" open-window ] with-ui ;

View File

@ -1,83 +0,0 @@
USING: accessors assocs arrays kernel models monads sequences
ui.frp.signals ui.gadgets ui.gadgets.borders ui.gadgets.buttons
ui.gadgets.buttons.private ui.gadgets.editors words images.loader
ui.gadgets.scrollers ui.gadgets.tables ui.images vocabs.parser lexer
models.range ui.gadgets.sliders ;
IN: ui.frp.gadgets
TUPLE: frp-button < button hook value ;
: <frp-button> ( gadget -- button ) [
[ dup hook>> [ call( button -- ) ] [ drop ] if* ]
[ [ [ value>> ] [ ] bi or ] keep set-control-value ]
[ model>> f swap (>>value) ] tri
] frp-button new-button f <basic> >>model ;
: <frp-border-button> ( text -- button ) <frp-button> border-button-theme ;
TUPLE: frp-table < table { quot initial: [ ] } { val-quot initial: [ ] } color-quot column-titles column-alignment actions ;
M: frp-table column-titles column-titles>> ;
M: frp-table column-alignment column-alignment>> ;
M: frp-table row-columns quot>> [ call( a -- b ) ] [ drop f ] if* ;
M: frp-table row-value val-quot>> [ call( a -- b ) ] [ drop f ] if* ;
M: frp-table row-color color-quot>> [ call( a -- b ) ] [ drop f ] if* ;
: new-frp-table ( model class -- table ) f swap new-table dup >>renderer
V{ } clone <basic> >>selected-values V{ } clone <basic> >>selected-indices*
f <basic> >>actions dup [ actions>> set-model ] curry >>action ;
: <frp-table> ( model -- table ) frp-table new-frp-table ;
: <frp-table*> ( -- table ) V{ } clone <model> <frp-table> ;
: <frp-list> ( column-model -- table ) <frp-table> [ 1array ] >>quot ;
: <frp-list*> ( -- table ) V{ } clone <model> <frp-list> ;
: indexed ( table -- table ) f >>val-quot ;
TUPLE: frp-field < field frp-model ;
: init-field ( field -- field' ) [ [ ] [ "" ] if* ] change-value ;
: <frp-field> ( model -- gadget ) frp-field new-field swap init-field >>frp-model ;
M: frp-field graft*
[ [ frp-model>> value>> ] [ editor>> ] bi set-editor-string ]
[ dup editor>> model>> add-connection ]
[ dup frp-model>> add-connection ] tri ;
M: frp-field ungraft*
[ dup editor>> model>> remove-connection ]
[ dup frp-model>> remove-connection ] bi ;
M: frp-field model-changed 2dup frp-model>> =
[ [ value>> ] [ editor>> ] bi* set-editor-string ]
[ nip [ editor>> editor-string ] [ frp-model>> ] bi set-model ] if ;
: <frp-field*> ( -- field ) "" <model> <frp-field> ;
: <empty-field> ( model -- field ) "" <model> <switch> <frp-field> ;
: <frp-editor> ( model -- gadget )
frp-field [ <multiline-editor> ] dip new-border dup gadget-child >>editor
field-theme swap init-field >>frp-model { 1 0 } >>align ;
: <frp-editor*> ( -- editor ) "" <model> <frp-editor> ;
: <empty-editor> ( model -- editor ) "" <model> <switch> <frp-editor> ;
: <frp-action-field> ( -- field ) f <action-field> dup [ set-control-value ] curry >>quot
f <model> >>model ;
: <frp-slider> ( init page min max step -- slider ) <range> horizontal <slider> ;
: image-prep ( -- image ) scan current-vocab name>> "vocab:" "/icons/" surround ".tiff" surround <image-name> dup cached-image drop ;
SYNTAX: IMG-FRP-BTN: image-prep [ <frp-button> ] curry over push-all ;
SYNTAX: IMG-BTN: image-prep [ swap <button> ] curry over push-all ;
GENERIC: output-model ( gadget -- model )
M: gadget output-model model>> ;
M: table output-model dup multiple-selection?>>
[ dup val-quot>> [ selected-values>> ] [ selected-indices*>> ] if ]
[ dup val-quot>> [ selected-value>> ] [ selected-index*>> ] if ] if ;
M: frp-field output-model frp-model>> ;
M: scroller output-model viewport>> children>> first output-model ;
M: slider output-model model>> range-model ;
IN: accessors
M: frp-button text>> children>> first text>> ;
IN: ui.frp.gadgets
SINGLETON: gadget-monad
INSTANCE: gadget-monad monad
INSTANCE: gadget monad
M: gadget monad-of drop gadget-monad ;
M: gadget-monad return drop <gadget> swap >>model ;
M: gadget >>= output-model [ swap call( x -- y ) ] curry ;

View File

@ -1 +0,0 @@
Gadgets using signals as their models

View File

@ -1,49 +0,0 @@
USING: help.markup help.syntax models models.arrow sequences ui.frp.signals monads ;
IN: ui.frp.signals
HELP: <merge>
{ $values { "models" "a list of models" } { "signal" basic-model } }
{ $description "Creates a signal that merges the updates of others" } ;
HELP: <filter>
{ $values { "model" model } { "quot" "quotation with stack effect ( a b -- c )" } { "filter-signal" filter-model } }
{ $description "Creates a signal that uses the updates of another model only when they satisfy a given predicate" } ;
HELP: <fold>
{ $values { "oldval" "starting value" } { "quot" "applied to update and previous values" } { "model" model } { "signal" model } }
{ $description "Similar to " { $link reduce } " but works on models, applying a quotation to the previous and new values at each update" } ;
HELP: <switch>
{ $values { "signal1" model } { "signal2" model } { "signal'" model } }
{ $description "Creates a signal that starts with the behavior of signal2 and switches to the behavior of signal1 on its update" } ;
HELP: <mapped>
{ $values { "model" model } { "quot" "applied to model's value on updates" } { "signal" model } }
{ $description "The signal version of an " { $link <arrow> } } ;
HELP: $>
{ $values { "model" model } { "quot" "applied to model's value on updates" } { "signal" model } }
{ $description "Like " { $link <mapped> } ", but doesn't produce a new value" } ;
HELP: <$
{ $values { "model" model } { "quot" "applied to model's value on updates" } { "signal" model } }
{ $description "Opposite of " { $link <$ } "- gives output, but takes no input" } ;
HELP: frp-when
{ $values { "model" model } { "quot" "called on the model if the quot yields true" } { "cond" "a quotation called on the model's value, yielding a boolean value" } }
{ $description "Calls quot when model updates if its value meets the condition set in cond" } ;
HELP: with-self
{ $values { "quot" "quotation that recieves its own return value" } { "model" model } }
{ $description "Fixed points for signals: the quot reacts to the same signal to gives" } ;
HELP: #1
{ $values { "model" model } { "model'" model } }
{ $description "Moves a signal to the top of its dependencies' connections, thus being notified before the others" } ;
ARTICLE: "signals" "FRP Signals"
"Unlike models, which always have a value, signals have discrete start and end times. "
"They are the core of the frp library: program flow using frp is controlled entirely through manipulating and combining signals. "
"The output signals of some gadgets (see " { $vocab-link "ui.frp.gadgets" } " ) can be manipulated and used as the input signals of others. " ;
ABOUT: "signals"

View File

@ -1 +0,0 @@
Utilities for functional reactive programming in user interfaces

View File

@ -1,5 +1,5 @@
USING: accessors models monads macros generalizations kernel USING: accessors models monads macros generalizations kernel
ui ui.frp.gadgets ui.frp.signals ui.frp.layout ui.gadgets ui ui.gadgets.controls models.combinators ui.gadgets.layout ui.gadgets
ui.gadgets.labels ui.gadgets.editors ui.gadgets.buttons ui.gadgets.labels ui.gadgets.editors ui.gadgets.buttons
ui.gadgets.packs locals sequences fonts io.styles ui.gadgets.packs locals sequences fonts io.styles
wrap.strings ; wrap.strings ;
@ -13,16 +13,16 @@ IN: ui.gadgets.alerts
:: ask-user ( string -- model' ) :: ask-user ( string -- model' )
[ [let | lbl [ string <label> T{ font { name "sans-serif" } { size 14 } } >>font dup , ] [ [let | lbl [ string <label> T{ font { name "sans-serif" } { size 14 } } >>font dup , ]
fldm [ <frp-field*> ->% 1 ] fldm [ <model-field*> ->% 1 ]
btn [ "okay" <frp-border-button> ] | btn [ "okay" <model-border-btn> ] |
btn -> [ fldm swap <updates> ] btn -> [ fldm swap updates ]
[ [ drop lbl close-window ] $> , ] bi [ [ drop lbl close-window ] $> , ] bi
] ] <vbox> { 161 86 } >>pref-dim "" open-window ; ] ] <vbox> { 161 86 } >>pref-dim "" open-window ;
MACRO: ask-buttons ( buttons -- quot ) dup length [ MACRO: ask-buttons ( buttons -- quot ) dup length [
[ swap [ swap
[ 22 wrap-lines <label> T{ font { name "sans-serif" } { size 18 } } >>font , [ 22 wrap-lines <label> T{ font { name "sans-serif" } { size 18 } } >>font ,
[ [ <frp-border-button> [ close-window ] >>hook -> ] map ] <hbox> , ] <vbox> [ [ <model-border-btn> [ close-window ] >>hook -> ] map ] <hbox> , ] <vbox>
"" open-window "" open-window
] dip firstn ] dip firstn
] 2curry ; ] 2curry ;

View File

@ -1,14 +1,15 @@
USING: accessors arrays kernel math.rectangles sequences USING: accessors arrays kernel math.rectangles sequences
ui.frp.gadgets ui.frp.signals ui.gadgets ui.gadgets.glass ui.gadgets.controls models.combinators ui.gadgets ui.gadgets.glass
ui.gadgets.labels ui.gadgets.tables ui.gestures ; ui.gadgets.labels ui.gestures ;
QUALIFIED-WITH: ui.gadgets.tables tbl
IN: ui.gadgets.comboboxes IN: ui.gadgets.comboboxes
TUPLE: combo-table < frp-table spawner ; TUPLE: combo-table < table spawner ;
M: combo-table handle-gesture [ call-next-method drop ] 2keep swap M: combo-table handle-gesture [ call-next-method drop ] 2keep swap
T{ button-up } = [ T{ button-up } = [
[ spawner>> ] [ spawner>> ]
[ selected-row [ swap set-control-value ] [ 2drop ] if ] [ tbl:selected-row [ swap set-control-value ] [ 2drop ] if ]
[ hide-glass ] tri [ hide-glass ] tri
] [ drop ] if t ; ] [ drop ] if t ;
@ -18,4 +19,4 @@ combobox H{
} set-gestures } set-gestures
: <combobox> ( options -- combobox ) [ first [ combobox new-label ] keep <basic> >>model ] keep : <combobox> ( options -- combobox ) [ first [ combobox new-label ] keep <basic> >>model ] keep
<basic> combo-table new-frp-table [ 1array ] >>quot >>table ; <basic> combo-table new-table [ 1array ] >>quot >>table ;

View File

@ -1,40 +1,40 @@
USING: accessors help.markup help.syntax ui.gadgets.buttons USING: accessors help.markup help.syntax ui.gadgets.buttons
ui.gadgets.editors ui.frp.gadgets models ui.gadgets ; ui.gadgets.editors models ui.gadgets ;
IN: ui.frp.gadgets IN: ui.gadgets.controls
HELP: <frp-button> HELP: <model-btn>
{ $values { "gadget" "the button's label" } { "button" button } } { $values { "gadget" "the button's label" } { "button" button } }
{ $description "Creates an button whose signal updates on clicks. " } ; { $description "Creates an button whose signal updates on clicks. " } ;
HELP: <frp-border-button> HELP: <model-border-btn>
{ $values { "text" "the button's label" } { "button" button } } { $values { "text" "the button's label" } { "button" button } }
{ $description "Creates an button whose signal updates on clicks. " } ; { $description "Creates an button whose signal updates on clicks. " } ;
HELP: <frp-table> HELP: <table>
{ $values { "model" "values the table is to display" } { "table" frp-table } } { $values { "model" "values the table is to display" } { "table" table } }
{ $description "Creates an " { $link frp-table } } ; { $description "Creates an " { $link table } } ;
HELP: <frp-table*> HELP: <table*>
{ $values { "table" frp-table } } { $values { "table" table } }
{ $description "Creates an " { $link frp-table } " with no initial values to display" } ; { $description "Creates an " { $link table } " with no initial values to display" } ;
HELP: <frp-list> HELP: <list>
{ $values { "column-model" "values the table is to display" } { "table" frp-table } } { $values { "column-model" "values the table is to display" } { "table" table } }
{ $description "Creates an " { $link frp-table } " with a val-quot that renders each element as its own row" } ; { $description "Creates an " { $link table } " with a val-quot that renders each element as its own row" } ;
HELP: <frp-list*> HELP: <list*>
{ $values { "table" frp-table } } { $values { "table" table } }
{ $description "Creates an frp-list with no initial values to display" } ; { $description "Creates an model-list with no initial values to display" } ;
HELP: indexed HELP: indexed
{ $values { "table" frp-table } } { $values { "table" table } }
{ $description "Sets the output model of an frp-table to the selected-index, rather than the selected-value" } ; { $description "Sets the output model of an table to the selected-index, rather than the selected-value" } ;
HELP: <frp-field> HELP: <model-field>
{ $values { "model" model } { "gadget" model-field } } { $values { "model" model } { "gadget" model-field } }
{ $description "Creates a field with an initial value" } ; { $description "Creates a field with an initial value" } ;
HELP: <frp-field*> HELP: <model-field*>
{ $values { "field" model-field } } { $values { "field" model-field } }
{ $description "Creates a field with an empty initial value" } ; { $description "Creates a field with an empty initial value" } ;
@ -42,11 +42,11 @@ HELP: <empty-field>
{ $values { "model" model } { "field" model-field } } { $values { "model" model } { "field" model-field } }
{ $description "Creates a field with an empty initial value that switches to another signal on its update" } ; { $description "Creates a field with an empty initial value that switches to another signal on its update" } ;
HELP: <frp-editor> HELP: <model-editor>
{ $values { "model" model } { "gadget" model-field } } { $values { "model" model } { "gadget" model-field } }
{ $description "Creates an editor with an initial value" } ; { $description "Creates an editor with an initial value" } ;
HELP: <frp-editor*> HELP: <model-editor*>
{ $values { "editor" "an editor" } } { $values { "editor" "an editor" } }
{ $description "Creates a editor with an empty initial value" } ; { $description "Creates a editor with an empty initial value" } ;
@ -54,16 +54,16 @@ HELP: <empty-editor>
{ $values { "model" model } { "editor" "an editor" } } { $values { "model" model } { "editor" "an editor" } }
{ $description "Creates a field with an empty initial value that switches to another signal on its update" } ; { $description "Creates a field with an empty initial value that switches to another signal on its update" } ;
HELP: <frp-action-field> HELP: <model-action-field>
{ $values { "field" action-field } } { $values { "field" action-field } }
{ $description "Field that updates its model with its contents when the user hits the return key" } ; { $description "Field that updates its model with its contents when the user hits the return key" } ;
HELP: IMG-FRP-BTN: HELP: IMG-MODEL-BTN:
{ $syntax "IMAGE-BUTTON: filename" } { $syntax "IMAGE-MODEL-BTN: filename" }
{ $description "Creates a button using a tiff image named as specified found in the icons subdirectory of the vocabulary path" } ; { $description "Creates a button using a tiff image named as specified found in the icons subdirectory of the vocabulary path" } ;
HELP: IMG-BTN: HELP: IMG-BTN:
{ $syntax "[ do-something ] IMAGE-BUTTON: filename" } { $syntax "[ do-something ] IMAGE-BTN: filename" }
{ $description "Creates a button using a tiff image named as specified found in the icons subdirectory of the vocabulary path, calling the specified quotation on click" } ; { $description "Creates a button using a tiff image named as specified found in the icons subdirectory of the vocabulary path, calling the specified quotation on click" } ;
HELP: output-model HELP: output-model

View File

@ -0,0 +1,87 @@
USING: accessors assocs arrays kernel models monads sequences
models.combinators ui.gadgets ui.gadgets.borders ui.gadgets.buttons
ui.gadgets.buttons.private ui.gadgets.editors words images.loader
ui.gadgets.scrollers ui.images vocabs.parser lexer
models.range ui.gadgets.sliders ;
QUALIFIED-WITH: ui.gadgets.sliders slider
QUALIFIED-WITH: ui.gadgets.tables tbl
EXCLUDE: ui.gadgets.editors => model-field ;
IN: ui.gadgets.controls
TUPLE: model-btn < button hook value ;
: <model-btn> ( gadget -- button ) [
[ dup hook>> [ call( button -- ) ] [ drop ] if* ]
[ [ [ value>> ] [ ] bi or ] keep set-control-value ]
[ model>> f swap (>>value) ] tri
] model-btn new-button f <basic> >>model ;
: <model-border-btn> ( text -- button ) <model-btn> border-button-theme ;
TUPLE: table < tbl:table { quot initial: [ ] } { val-quot initial: [ ] } color-quot column-titles column-alignment actions ;
M: table tbl:column-titles column-titles>> ;
M: table tbl:column-alignment column-alignment>> ;
M: table tbl:row-columns quot>> [ call( a -- b ) ] [ drop f ] if* ;
M: table tbl:row-value val-quot>> [ call( a -- b ) ] [ drop f ] if* ;
M: table tbl:row-color color-quot>> [ call( a -- b ) ] [ drop f ] if* ;
: new-table ( model class -- table ) f swap tbl:new-table dup >>renderer
V{ } clone <basic> >>selected-values V{ } clone <basic> >>selected-indices*
f <basic> >>actions dup [ actions>> set-model ] curry >>action ;
: <table> ( model -- table ) table new-table ;
: <table*> ( -- table ) V{ } clone <model> <table> ;
: <list> ( column-model -- table ) <table> [ 1array ] >>quot ;
: <list*> ( -- table ) V{ } clone <model> <list> ;
: indexed ( table -- table ) f >>val-quot ;
TUPLE: model-field < field model* ;
: init-field ( field -- field' ) [ [ ] [ "" ] if* ] change-value ;
: <model-field> ( model -- gadget ) model-field new-field swap init-field >>model* ;
M: model-field graft*
[ [ model*>> value>> ] [ editor>> ] bi set-editor-string ]
[ dup editor>> model>> add-connection ]
[ dup model*>> add-connection ] tri ;
M: model-field ungraft*
[ dup editor>> model>> remove-connection ]
[ dup model*>> remove-connection ] bi ;
M: model-field model-changed 2dup model*>> =
[ [ value>> ] [ editor>> ] bi* set-editor-string ]
[ nip [ editor>> editor-string ] [ model*>> ] bi set-model ] if ;
: <model-field*> ( -- field ) "" <model> <model-field> ;
: <empty-field> ( model -- field ) "" <model> switch-models <model-field> ;
: (model-editor) ( model class -- gadget )
model-field [ new-editor ] dip new-border dup gadget-child >>editor
field-theme swap init-field >>model* { 1 0 } >>align ;
: <model-editor> ( model -- gadget ) multiline-editor (model-editor) ;
: <model-editor*> ( -- editor ) "" <model> <model-editor> ;
: <empty-editor> ( model -- editor ) "" <model> switch-models <model-editor> ;
: <model-action-field> ( -- field ) f <action-field> dup [ set-control-value ] curry >>quot
f <model> >>model ;
: <slider> ( init page min max step -- slider ) <range> horizontal slider:<slider> ;
: image-prep ( -- image ) scan current-vocab name>> "vocab:" "/icons/" surround ".tiff" surround <image-name> dup cached-image drop ;
SYNTAX: IMG-MODEL-BTN: image-prep [ <model-btn> ] curry over push-all ;
SYNTAX: IMG-BTN: image-prep [ swap <button> ] curry over push-all ;
GENERIC: output-model ( gadget -- model )
M: gadget output-model model>> ;
M: table output-model dup multiple-selection?>>
[ dup val-quot>> [ selected-values>> ] [ selected-indices*>> ] if ]
[ dup val-quot>> [ selected-value>> ] [ selected-index*>> ] if ] if ;
M: model-field output-model model*>> ;
M: scroller output-model viewport>> children>> first output-model ;
M: slider output-model model>> range-model ;
IN: accessors
M: model-btn text>> children>> first text>> ;
IN: ui.gadgets.controls
SINGLETON: gadget-monad
INSTANCE: gadget-monad monad
INSTANCE: gadget monad
M: gadget monad-of drop gadget-monad ;
M: gadget-monad return drop <gadget> swap >>model ;
M: gadget >>= output-model [ swap call( x -- y ) ] curry ;

View File

@ -0,0 +1 @@
Gadgets with expanded model usage

View File

@ -1,5 +1,5 @@
USING: help.markup help.syntax models ui.gadgets.tracks ui.frp.layout ; USING: help.markup help.syntax models ui.gadgets.tracks ;
IN: ui.frp.layout IN: ui.gadgets.layout
HELP: , HELP: ,
{ $values { "item" "a gadget or model" } } { $values { "item" "a gadget or model" } }
@ -38,16 +38,16 @@ HELP: with-interface
{ $values { "quot" "quotation that builds a template and inserts into it" } } { $values { "quot" "quotation that builds a template and inserts into it" } }
{ $description "Create templates, used with " { $link POSTPONE: $ } } ; { $description "Create templates, used with " { $link POSTPONE: $ } } ;
ARTICLE: "ui.frp.layout" "GUI Layout" ARTICLE: "ui.gadgets.layout" "GUI Layout"
"Laying out GUIs works the same way as building lists with " { $vocab-link "make" } "Laying out GUIs works the same way as building lists with " { $vocab-link "make" }
". Gadgets are layed out using " { $vocab-link "ui.gadgets.tracks" } " through " { $link <hbox> } " and " { $link <vbox> } ", which allow both fixed and percentage widths. " ". Gadgets are layed out using " { $vocab-link "ui.gadgets.tracks" } " through " { $link <hbox> } " and " { $link <vbox> } ", which allow both fixed and percentage widths. "
{ $link , } " and " { $link -> } " add a signal or gadget to the gadget you're building. " { $link , } " and " { $link -> } " add a model or gadget to the gadget you're building. "
"Also, books can be made with " { $link <frp-book> } ". " "Also, books can be made with " { $link <book> } ". "
{ $link <spacer> } "s add flexable space between items. " $nl { $link <spacer> } "s add flexable space between items. " $nl
"Using " { $link with-interface } ", one can pre-build templates to add items to later: " "Using " { $link with-interface } ", one can pre-build templates to add items to later: "
"Like in the StringTemplate framework for java, placeholders are defined using $ PLACERHOLDER-NAME $ " "Like in the StringTemplate framework for java, placeholders are defined using $ PLACERHOLDER-NAME $ "
"Using PLACEHOLDER-NAME again sets it as the current insertion point. " "Using PLACEHOLDER-NAME again sets it as the current insertion point. "
"For examples using normal layout, see the " { $vocab-link "sudokus" } " demo. " "For examples using normal layout, see the " { $vocab-link "sudokus" } " demo. "
"For examples of templating, see " { $vocab-link "recipes" } " demo. " ; "For examples of templating, see the " { $vocab-link "recipes" } " demo. " ;
ABOUT: "ui.frp.layout" ABOUT: "ui.gadgets.layout"

View File

@ -1,9 +1,10 @@
USING: accessors assocs arrays fry kernel lexer make math.parser USING: accessors assocs arrays fry kernel lexer make math.parser
models monads namespaces parser sequences models monads namespaces parser sequences
sequences.extras ui.frp.gadgets ui.frp.signals ui.gadgets sequences.extras models.combinators ui.gadgets
ui.gadgets.books ui.gadgets.tracks words ; ui.gadgets.tracks words ui.gadgets.controls ;
QUALIFIED: make QUALIFIED: make
IN: ui.frp.layout QUALIFIED-WITH: ui.gadgets.books book
IN: ui.gadgets.layout
SYMBOL: templates SYMBOL: templates
TUPLE: layout gadget size ; C: <layout> layout TUPLE: layout gadget size ; C: <layout> layout
@ -47,9 +48,9 @@ M: model -> dup , ;
: <hbox> ( gadgets -- track ) horizontal <box> ; inline : <hbox> ( gadgets -- track ) horizontal <box> ; inline
: <vbox> ( gadgets -- track ) vertical <box> ; inline : <vbox> ( gadgets -- track ) vertical <box> ; inline
: make-book ( models gadgets model -- book ) <book> swap [ "No models in books" throw ] unless-empty ; : make-book ( models gadgets model -- book ) book:<book> swap [ "No models in books" throw ] unless-empty ;
: <frp-book> ( quot: ( -- model ) -- book ) f make-layout rot 0 >>value make-book ; inline : <book> ( quot: ( -- model ) -- book ) f make-layout rot 0 >>value make-book ; inline
: <frp-book*> ( quot -- book ) f make-layout f make-book ; inline : <book*> ( quot -- book ) f make-layout f make-book ; inline
ERROR: not-in-template word ; ERROR: not-in-template word ;
SYNTAX: $ CREATE-WORD dup SYNTAX: $ CREATE-WORD dup
@ -58,16 +59,17 @@ SYNTAX: $ CREATE-WORD dup
: insert-gadget ( number parent gadget -- ) -rot [ but-last insert-nth ] change-children drop ; : insert-gadget ( number parent gadget -- ) -rot [ but-last insert-nth ] change-children drop ;
: insert-size ( number parent size -- ) -rot [ but-last insert-nth ] change-sizes drop ; : insert-size ( number parent size -- ) -rot [ but-last insert-nth ] change-sizes drop ;
: insertion-point ( gadget placeholder -- number parent gadget ) dup parent>> [ children>> index ] keep rot ; : insertion-point ( placeholder -- number parent ) dup parent>> [ children>> index ] keep ;
GENERIC# (insert-item) 1 ( item location -- ) GENERIC# add-gadget-at 1 ( item location -- )
M: gadget (insert-item) dup parent>> track? [ [ f <layout> ] dip (insert-item) ] M: gadget add-gadget-at dup parent>> track? [ [ f <layout> ] dip add-gadget-at ]
[ insertion-point [ add-gadget ] keep insert-gadget ] if ; [ insertion-point rot [ add-gadget ] keep insert-gadget ] if ;
M: layout (insert-item) insertion-point [ add-layout ] keep [ gadget>> insert-gadget ] [ size>> insert-size ] 3bi ; M: layout add-gadget-at insertion-point rot [ add-layout ] keep [ gadget>> insert-gadget ] [ size>> insert-size ] 3bi ;
M: model (insert-item) parent>> dup book? [ "No models in books" throw ] M: model add-gadget-at parent>> dup book:book? [ "No models in books" throw ]
[ dup model>> dup collection? [ nip swap add-connection ] [ drop [ 1array <collection> ] dip (>>model) ] if ] if ; [ dup model>> dup collection? [ nip swap add-connection ] [ drop [ 1array <collection> ] dip (>>model) ] if ] if ;
: track-add-at ( item location size -- ) swap [ <layout> ] dip add-gadget-at ;
: insert-item ( item location -- ) [ dup get [ drop ] [ remove-members ] if ] [ on ] [ ] tri : insert-item ( item location -- ) [ dup get [ drop ] [ remove-members ] if ] [ on ] [ ] tri
[ add-member ] 2keep (insert-item) ; [ add-member ] 2keep add-gadget-at ;
: insert-items ( makelist -- ) t swap [ dup placeholder? [ nip ] [ over insert-item ] if ] each drop ; : insert-items ( makelist -- ) t swap [ dup placeholder? [ nip ] [ over insert-item ] if ] each drop ;