mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-15 00:05:02 +00:00
192 lines
6.4 KiB
Text
192 lines
6.4 KiB
Text
interface Elem
|
|
exposes [Elem, PressEvent, row, col, text, button, none, translate, list]
|
|
imports [Action.{ Action }]
|
|
|
|
Elem state : [
|
|
# PERFORMANCE NOTE:
|
|
# If there are 8 or fewer tags here, then on a 64-bit system, the tag can be stored
|
|
# in the pointer - for massive memory savings. Try extremely hard to always limit the number
|
|
# of tags in this union to 8 or fewer!
|
|
Button (ButtonConfig state) (Elem state),
|
|
Text Str,
|
|
Col (List (Elem state)),
|
|
Row (List (Elem state)),
|
|
Lazy (Result { state, elem : Elem state } [NotCached] -> { state, elem : Elem state }),
|
|
# TODO FIXME: using this definition of Lazy causes a stack overflow in the compiler!
|
|
# Lazy (Result (Cached state) [NotCached] -> Cached state),
|
|
None,
|
|
]
|
|
|
|
## Used internally in the type definition of Lazy
|
|
Cached state : { state, elem : Elem state }
|
|
|
|
ButtonConfig state : { onPress : state, PressEvent -> Action state }
|
|
|
|
PressEvent : { button : [Touch, Mouse [Left, Right, Middle]] }
|
|
|
|
text : Str -> Elem *
|
|
text = \str ->
|
|
Text str
|
|
|
|
button : { onPress : state, PressEvent -> Action state }, Elem state -> Elem state
|
|
button = \config, label ->
|
|
Button config label
|
|
|
|
row : List (Elem state) -> Elem state
|
|
row = \children ->
|
|
Row children
|
|
|
|
col : List (Elem state) -> Elem state
|
|
col = \children ->
|
|
Col children
|
|
|
|
lazy : state, (state -> Elem state) -> Elem state
|
|
lazy = \state, render ->
|
|
# This function gets called by the host during rendering. It will
|
|
# receive the cached state and element (wrapped in Ok) if we've
|
|
# ever rendered this before, and Err otherwise.
|
|
Lazy
|
|
\result ->
|
|
when result is
|
|
Ok cached if cached.state == state ->
|
|
# If we have a cached value, and the new state is the
|
|
# same as the cached one, then we can return exactly
|
|
# what we had cached.
|
|
cached
|
|
|
|
_ ->
|
|
# Either the state changed or else we didn't have a
|
|
# cached value to use. Either way, we need to render
|
|
# with the new state and store that for future use.
|
|
{ state, elem: render state }
|
|
|
|
none : Elem *
|
|
none = None # I've often wanted this in elm/html. Usually end up resorting to (Html.text "") - this seems nicer.
|
|
## Change an element's state type.
|
|
##
|
|
## TODO: indent the following once https://github.com/roc-lang/roc/issues/2585 is fixed.
|
|
## State : { photo : Photo }
|
|
##
|
|
## render : State -> Elem State
|
|
## render = \state ->
|
|
## child : Elem State
|
|
## child =
|
|
## Photo.render state.photo
|
|
## |> Elem.translate .photo &photo
|
|
##
|
|
## col {} [child, otherElems]
|
|
##
|
|
translate = \child, toChild, toParent ->
|
|
when child is
|
|
Text str ->
|
|
Text str
|
|
|
|
Col elems ->
|
|
Col (List.map elems \elem -> translate elem toChild toParent)
|
|
|
|
Row elems ->
|
|
Row (List.map elems \elem -> translate elem toChild toParent)
|
|
|
|
Button config label ->
|
|
onPress = \parentState, event ->
|
|
toChild parentState
|
|
|> config.onPress event
|
|
|> Action.map \c -> toParent parentState c
|
|
|
|
Button { onPress } (translate label toChild toParent)
|
|
|
|
Lazy renderChild ->
|
|
Lazy
|
|
\parentState ->
|
|
{ elem, state } = renderChild (toChild parentState)
|
|
|
|
{
|
|
elem: translate toChild toParent newChild,
|
|
state: toParent parentState state,
|
|
}
|
|
|
|
None ->
|
|
None
|
|
|
|
## Render a list of elements, using [Elem.translate] on each of them.
|
|
##
|
|
## Convenient when you have a [List] in your state and want to make
|
|
## a [List] of child elements out of it.
|
|
##
|
|
## TODO: indent the following once https://github.com/roc-lang/roc/issues/2585 is fixed.
|
|
## State : { photos : List Photo }
|
|
##
|
|
## render : State -> Elem State
|
|
## render = \state ->
|
|
## children : List (Elem State)
|
|
## children =
|
|
## Elem.list Photo.render state .photos &photos
|
|
##
|
|
## col {} children
|
|
## TODO: format as multiline type annotation once https://github.com/roc-lang/roc/issues/2586 is fixed
|
|
list : (child -> Elem child), parent, (parent -> List child), (parent, List child -> parent) -> List (Elem parent)
|
|
list = \renderChild, parent, toChildren, toParent ->
|
|
List.mapWithIndex
|
|
(toChildren parent)
|
|
\index, child ->
|
|
toChild = \par -> List.get (toChildren par) index
|
|
|
|
newChild = translateOrDrop
|
|
child
|
|
toChild
|
|
\par, ch ->
|
|
toChildren par
|
|
|> List.set ch index
|
|
|> toParent
|
|
|
|
renderChild newChild
|
|
|
|
## Internal helper function for Elem.list
|
|
##
|
|
## Tries to translate a child to a parent, but
|
|
## if the child has been removed from the parent,
|
|
## drops it.
|
|
##
|
|
## TODO: format as multiline type annotation once https://github.com/roc-lang/roc/issues/2586 is fixed
|
|
translateOrDrop : Elem child, (parent -> Result child *), (parent, child -> parent) -> Elem parent
|
|
translateOrDrop = \child, toChild, toParent ->
|
|
when child is
|
|
Text str ->
|
|
Text str
|
|
|
|
Col elems ->
|
|
Col (List.map elems \elem -> translateOrDrop elem toChild toParent)
|
|
|
|
Row elems ->
|
|
Row (List.map elems \elem -> translateOrDrop elem toChild toParent)
|
|
|
|
Button config label ->
|
|
onPress = \parentState, event ->
|
|
when toChild parentState is
|
|
Ok newChild ->
|
|
newChild
|
|
|> config.onPress event
|
|
|> Action.map \c -> toParent parentState c
|
|
|
|
Err _ ->
|
|
# The child was removed from the list before this onPress handler resolved.
|
|
# (For example, by a previous event handler that fired simultaneously.)
|
|
Action.none
|
|
|
|
Button { onPress } (translateOrDrop label toChild toParent)
|
|
|
|
Lazy childState renderChild ->
|
|
Lazy
|
|
(toParent childState)
|
|
\parentState ->
|
|
when toChild parentState is
|
|
Ok newChild ->
|
|
renderChild newChild
|
|
|> translateOrDrop toChild toParent
|
|
|
|
Err _ ->
|
|
None
|
|
|
|
# I don't think this should ever happen in practice.
|
|
None ->
|
|
None
|