Skip to content

Commit

Permalink
WIP: Actually stateless components
Browse files Browse the repository at this point in the history
  • Loading branch information
davesmith00000 committed May 29, 2024
1 parent 7e3ddd2 commit b932920
Show file tree
Hide file tree
Showing 16 changed files with 183 additions and 165 deletions.
8 changes: 4 additions & 4 deletions demo/src/main/scala/demo/ColourWindow.scala
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ object ColorPalette:
): Outcome[Layer] =
model.componentGroup.present(context).map(_.toLayer)

def cascade(model: ColorPalette, newBounds: Bounds): ColorPalette =
def cascade(context: UiContext[Unit], model: ColorPalette, newBounds: Bounds): ColorPalette =
model.copy(
componentGroup = model.componentGroup.cascade(newBounds)
componentGroup = model.componentGroup.cascade(context, newBounds)
)

def refresh(model: ColorPalette): ColorPalette =
def refresh(context: UiContext[Unit], model: ColorPalette): ColorPalette =
model.copy(
componentGroup = model.componentGroup.reflow
componentGroup = model.componentGroup.reflow(context)
)
2 changes: 1 addition & 1 deletion demo/src/main/scala/demo/ComponentsWindow2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ object ComponentsWindow2:
Input(20, Input.Theme(charSheet))
)
.add(
TextArea("abc.\nde,f\n0123456!", TextArea.Theme(charSheet))
TextArea((count: Int) => "abc.\nde,f\n0123456!\nCount: " + count, TextArea.Theme(charSheet))
)
)
.withTitle("More component examples")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ trait Component[A, ReferenceData]:

/** The position and size of the component
*/
def bounds(model: A): Bounds
def bounds(context: UiContext[ReferenceData], model: A): Bounds

/** Update this componenets model.
*/
Expand All @@ -30,9 +30,9 @@ trait Component[A, ReferenceData]:
/** Used internally to instruct the component that the layout has changed in some way, and that it
* should reflow it's contents - whatever that means in the context of this component type.
*/
def reflow(model: A): A
def reflow(context: UiContext[ReferenceData], model: A): A

/** Informs the Component that something about its parent has changed in case it needs to take
* action. Currently the only cascaded change is the bounds.
*/
def cascade(model: A, parentBounds: Bounds): A
def cascade(context: UiContext[ReferenceData], model: A, parentBounds: Bounds): A
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ trait StatelessComponent[A, ReferenceData] extends Component[A, ReferenceData]:

/** The position and size of the component
*/
def bounds(model: A): Bounds
def bounds(context: UiContext[ReferenceData], model: A): Bounds

/** Update this componenets model. In stateless componenets, the model is more like a view model,
* and should be assumed to be a cache for presentation data that could be reset at any time.
/** Handle an event that has been dispatched to this component.
*/
def updateModel(
def handleEvent(
context: UiContext[ReferenceData],
model: A
): GlobalEvent => Outcome[A]
): GlobalEvent => Batch[GlobalEvent]

/** Produce a renderable output for this component, based on the component's model.
*/
Expand All @@ -28,5 +27,8 @@ trait StatelessComponent[A, ReferenceData] extends Component[A, ReferenceData]:
model: A
): Outcome[ComponentFragment]

def reflow(model: A): A = model
def cascade(model: A, parentBounds: Bounds): A = model
def updateModel(context: UiContext[ReferenceData], model: A): GlobalEvent => Outcome[A] =
case e => Outcome(model).addGlobalEvents(handleEvent(context, model)(e))

def reflow(context: UiContext[ReferenceData], model: A): A = model
def cascade(context: UiContext[ReferenceData], model: A, parentBounds: Bounds): A = model
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import roguelikestarterkit.ui.datatypes.UiContext
/** Buttons `Component`s allow you to create buttons for your UI.
*/
final case class Button[ReferenceData](
bounds: Bounds,
state: ButtonState,
up: (Coords, Bounds, ReferenceData) => Outcome[ComponentFragment],
over: Option[(Coords, Bounds, ReferenceData) => Outcome[ComponentFragment]],
Expand Down Expand Up @@ -59,11 +58,12 @@ final case class Button[ReferenceData](
): Outcome[ComponentFragment] =
summon[StatelessComponent[Button[ReferenceData], ReferenceData]].present(context, this)

def reflow: Button[ReferenceData] =
summon[StatelessComponent[Button[ReferenceData], ReferenceData]].reflow(this)
def reflow(context: UiContext[ReferenceData]): Button[ReferenceData] =
summon[StatelessComponent[Button[ReferenceData], ReferenceData]].reflow(context, this)

def cascade(parentBounds: Bounds): Button[ReferenceData] =
summon[StatelessComponent[Button[ReferenceData], ReferenceData]].cascade(this, parentBounds)
def cascade(context: UiContext[ReferenceData], parentBounds: Bounds): Button[ReferenceData] =
summon[StatelessComponent[Button[ReferenceData], ReferenceData]]
.cascade(context, this, parentBounds)

object Button:

Expand Down Expand Up @@ -153,7 +153,6 @@ object Button:
present: (Coords, Bounds, ReferenceData) => Outcome[ComponentFragment]
): Button[ReferenceData] =
Button(
bounds,
ButtonState.Up,
present,
None,
Expand All @@ -168,7 +167,6 @@ object Button:
present: (Coords, Bounds, ReferenceData) => Outcome[ComponentFragment]
): Button[ReferenceData] =
Button(
Bounds(0, 0, 1, 1),
ButtonState.Up,
present,
None,
Expand All @@ -186,7 +184,6 @@ object Button:
calculateBounds: ReferenceData => Bounds
): Button[ReferenceData] =
Button(
if theme.hasBorder then Bounds(0, 0, 3, 3) else Bounds(0, 0, 1, 1),
ButtonState.Up,
presentButton(
label,
Expand Down Expand Up @@ -254,34 +251,22 @@ object Button:
)

given [ReferenceData]: StatelessComponent[Button[ReferenceData], ReferenceData] with
def bounds(model: Button[ReferenceData]): Bounds =
model.bounds
def bounds(context: UiContext[ReferenceData], model: Button[ReferenceData]): Bounds =
model.calculateBounds(context.reference)

def updateModel(
def handleEvent(
context: UiContext[ReferenceData],
model: Button[ReferenceData]
): GlobalEvent => Outcome[Button[ReferenceData]] =
case FrameTick =>
val newBounds =
model.calculateBounds(context.reference)

Outcome(
model.copy(
state =
if newBounds.moveBy(context.bounds.coords).contains(context.mouseCoords) then
if context.mouse.isLeftDown then ButtonState.Down
else ButtonState.Over
else ButtonState.Up,
bounds = newBounds
)
)

): GlobalEvent => Batch[GlobalEvent] =
case _: MouseEvent.Click
if model.bounds.moveBy(context.bounds.coords).contains(context.mouseCoords) =>
Outcome(model).addGlobalEvents(model.click(context.reference))
if model
.calculateBounds(context.reference)
.moveBy(context.bounds.coords)
.contains(context.mouseCoords) =>
model.click(context.reference)

case _ =>
Outcome(model)
Batch.empty

def present(
context: UiContext[ReferenceData],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,11 @@ final case class Input(
): Outcome[ComponentFragment] =
summon[Component[Input, ?]].present(context, this)

def reflow: Input =
summon[Component[Input, ?]].reflow(this)
def reflow(context: UiContext[?]): Input =
summon[Component[Input, ?]].reflow(context, this)

def cascade(parentBounds: Bounds): Input =
summon[Component[Input, ?]].cascade(this, parentBounds)
def cascade(context: UiContext[?], parentBounds: Bounds): Input =
summon[Component[Input, ?]].cascade(context, this, parentBounds)

object Input:

Expand Down Expand Up @@ -286,7 +286,7 @@ object Input:
)

given [ReferenceData]: Component[Input, ReferenceData] with
def bounds(model: Input): Bounds =
def bounds(context: UiContext[ReferenceData], model: Input): Bounds =
model.bounds.resizeBy(2, 2)

def updateModel(
Expand Down Expand Up @@ -345,10 +345,10 @@ object Input:
context.running
)

def reflow(model: Input): Input =
def reflow(context: UiContext[ReferenceData], model: Input): Input =
model

def cascade(model: Input, parentBounds: Bounds): Input =
def cascade(context: UiContext[ReferenceData], model: Input, parentBounds: Bounds): Input =
model

final case class Theme(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,13 @@ import scala.annotation.targetName
*/
final case class Label[ReferenceData](
text: ReferenceData => String,
bounds: Bounds,
render: (Coords, String, Dimensions) => Outcome[ComponentFragment]
):
def withText(value: String): Label[ReferenceData] =
this.copy(text = _ => value)
def withText(f: ReferenceData => String): Label[ReferenceData] =
this.copy(text = f)

def withBounds(value: Bounds): Label[ReferenceData] =
this.copy(bounds = value)

// Delegates, for convenience.

def update[StartupData, ContextData](
Expand All @@ -43,11 +39,12 @@ final case class Label[ReferenceData](
): Outcome[ComponentFragment] =
summon[StatelessComponent[Label[ReferenceData], ReferenceData]].present(context, this)

def reflow: Label[ReferenceData] =
summon[StatelessComponent[Label[ReferenceData], ReferenceData]].reflow(this)
def reflow(context: UiContext[ReferenceData]): Label[ReferenceData] =
summon[StatelessComponent[Label[ReferenceData], ReferenceData]].reflow(context, this)

def cascade(parentBounds: Bounds): Label[ReferenceData] =
summon[StatelessComponent[Label[ReferenceData], ReferenceData]].cascade(this, parentBounds)
def cascade(context: UiContext[ReferenceData], parentBounds: Bounds): Label[ReferenceData] =
summon[StatelessComponent[Label[ReferenceData], ReferenceData]]
.cascade(context, this, parentBounds)

object Label:

Expand All @@ -59,13 +56,13 @@ object Label:
def apply[ReferenceData](text: String)(
present: (Coords, String, Dimensions) => Outcome[ComponentFragment]
): Label[ReferenceData] =
Label(_ => text, findBounds(text), present)
Label(_ => text, present)

@targetName("Label_apply_curried")
def apply[ReferenceData](text: ReferenceData => String)(
present: (Coords, String, Dimensions) => Outcome[ComponentFragment]
): Label[ReferenceData] =
Label(text, Bounds(0, 0, 1, 1), present)
Label(text, present)

private val graphic = Graphic(0, 0, TerminalMaterial(AssetName(""), RGBA.White, RGBA.Black))

Expand Down Expand Up @@ -97,7 +94,7 @@ object Label:
def apply[ReferenceData](text: String, theme: Theme): Label[ReferenceData] =
Label(
_ => text,
findBounds(text),
// findBounds(text),
presentLabel(theme.charSheet, theme.colors.foreground, theme.colors.background)
)

Expand All @@ -107,29 +104,35 @@ object Label:
def apply[ReferenceData](text: ReferenceData => String, theme: Theme): Label[ReferenceData] =
Label(
text,
Bounds(0, 0, 1, 1),
// Bounds(0, 0, 1, 1),
presentLabel(theme.charSheet, theme.colors.foreground, theme.colors.background)
)

given [ReferenceData]: StatelessComponent[Label[ReferenceData], ReferenceData] with
def bounds(model: Label[ReferenceData]): Bounds =
model.bounds

def updateModel(
def bounds(context: UiContext[ReferenceData], model: Label[ReferenceData]): Bounds =
findBounds(model.text(context.reference))

// def updateModel(
// context: UiContext[ReferenceData],
// model: Label[ReferenceData]
// ): GlobalEvent => Outcome[Label[ReferenceData]] =
// case FrameTick =>
// Outcome(model.withBounds(findBounds(model.text(context.reference))))

// case _ =>
// Outcome(model)
def handleEvent(
context: UiContext[ReferenceData],
model: Label[ReferenceData]
): GlobalEvent => Outcome[Label[ReferenceData]] =
case FrameTick =>
Outcome(model.withBounds(findBounds(model.text(context.reference))))

case _ =>
Outcome(model)
): GlobalEvent => Batch[GlobalEvent] =
_ => Batch.empty

def present(
context: UiContext[ReferenceData],
model: Label[ReferenceData]
): Outcome[ComponentFragment] =
model.render(context.bounds.coords, model.text(context.reference), model.bounds.dimensions)
val bounds = findBounds(model.text(context.reference))
model.render(context.bounds.coords, model.text(context.reference), bounds.dimensions)

final case class Theme(
charSheet: CharSheet,
Expand Down
Loading

0 comments on commit b932920

Please sign in to comment.