Skip to content

Commit

Permalink
Simplifying and commonising container code
Browse files Browse the repository at this point in the history
  • Loading branch information
davesmith00000 committed Jun 4, 2024
1 parent 0ee9408 commit 1b46566
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 288 deletions.
44 changes: 23 additions & 21 deletions demo/src/main/scala/demo/ComponentsWindow2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ object ComponentsWindow2:
.add(
ComponentGroup()
.withLayout(ComponentLayout.Horizontal(Padding(0, 1, 0, 0)))
.add(
Label("label 1", Label.Theme(charSheet)),
Label("label 2", Label.Theme(charSheet)),
Label("label 3", Label.Theme(charSheet))
)
// .add(
// Label("label 1", Label.Theme(charSheet)),
// Label("label 2", Label.Theme(charSheet)),
// Label("label 3", Label.Theme(charSheet))
// )
// .withBoundsType(BoundsType.Fixed(Bounds(0, 0, 20, 2)))
)
.withBoundsType(BoundsType.Fixed(Bounds(0, 0, 20, 2)))
// .add(
// ComponentGroup()
// .withLayout(ComponentLayout.Horizontal(Padding(0, 1, 0, 0)))
Expand All @@ -47,26 +49,26 @@ object ComponentsWindow2:
// }
// .withLayout(ComponentLayout.Vertical())
// )
.add(
ComponentList(Bounds(0, 0, 20, 3)) { (count: Int) =>
Batch(Label[Int]("How many windows: ", Label.Theme(charSheet))) ++
Batch.fill(count)(Label("x", Label.Theme(charSheet)))
}
.add((count: Int) =>
Batch.fill(count)(
Button[Int]("Button", Button.Theme(charSheet)).onClick(Log("count: " + count))
)
:+ Button[Int]("test", Button.Theme(charSheet)).onClick(Log("test"))
)
.add((i: Int) => TextArea[Int]("abc.\nde,f\n0123456! " + i, TextArea.Theme(charSheet)))
.withLayout(ComponentLayout.Vertical(Padding.zero))
)
// .add(
// ComponentList(Bounds(0, 0, 20, 3)) { (count: Int) =>
// Batch(Label[Int]("How many windows: ", Label.Theme(charSheet))) ++
// Batch.fill(count)(Label("x", Label.Theme(charSheet)))
// }
// .add((count: Int) =>
// Batch.fill(count)(
// Button[Int]("Button", Button.Theme(charSheet)).onClick(Log("count: " + count))
// )
// :+ Button[Int]("test", Button.Theme(charSheet)).onClick(Log("test"))
// )
// .add((i: Int) => TextArea[Int]("abc.\nde,f\n0123456! " + i, TextArea.Theme(charSheet)))
// .withLayout(ComponentLayout.Vertical(Padding.zero))
// Input(20, Input.Theme(charSheet))
// )
// .add(
// Input(20, Input.Theme(charSheet))
// TextArea("abc.\nde,f\n0123456!", TextArea.Theme(charSheet))
// )
.add(
TextArea("abc.\nde,f\n0123456!", TextArea.Theme(charSheet))
)
)
.withTitle("More component examples")
.moveTo(2, 2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import indigo.shared.collections.Batch
import indigo.shared.datatypes.Point
import indigo.shared.datatypes.Rectangle

import ui.components.common.ComponentLayout
import ui.components.common.Overflow
import ui.components.common.Padding

object syntax:

extension (r: Rectangle)
Expand Down Expand Up @@ -119,14 +123,15 @@ val ComponentGroup: ui.components.group.ComponentGroup.type = ui.components.grou
type ComponentList[ReferenceData] = ui.components.list.ComponentList[ReferenceData]
val ComponentList: ui.components.list.ComponentList.type = ui.components.list.ComponentList

type ComponentLayout = ui.components.group.ComponentLayout
val ComponentLayout: ui.components.group.ComponentLayout.type = ui.components.group.ComponentLayout
type ComponentLayout = ui.components.common.ComponentLayout
val ComponentLayout: ui.components.common.ComponentLayout.type =
ui.components.common.ComponentLayout

type Overflow = ui.components.group.Overflow
val Overflow: ui.components.group.Overflow.type = ui.components.group.Overflow
type Overflow = ui.components.common.Overflow
val Overflow: ui.components.common.Overflow.type = ui.components.common.Overflow

type Padding = ui.components.group.Padding
val Padding: ui.components.group.Padding.type = ui.components.group.Padding
type Padding = ui.components.common.Padding
val Padding: ui.components.common.Padding.type = ui.components.common.Padding

type BoundsType = ui.components.group.BoundsType
val BoundsType: ui.components.group.BoundsType.type = ui.components.group.BoundsType
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roguelikestarterkit.ui.components.group
package roguelikestarterkit.ui.components.common

import indigo.*
import roguelikestarterkit.ui.component.Component
Expand All @@ -8,11 +8,11 @@ import roguelikestarterkit.ui.datatypes.Coords
/** `ComponentEntry`s record a components model, position, and relevant component typeclass instance
* for use inside a `ComponentGroup`.
*/
final case class ComponentGroupEntry[A, ReferenceData](
final case class ComponentEntry[A, ReferenceData](
offset: Coords,
model: A,
component: Component[A, ReferenceData]
):

def cascade(parentBounds: Bounds): ComponentGroupEntry[A, ReferenceData] =
def cascade(parentBounds: Bounds): ComponentEntry[A, ReferenceData] =
this.copy(model = component.cascade(model, parentBounds))
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roguelikestarterkit.ui.components.group
package roguelikestarterkit.ui.components.common

/** `ComponentLayout` instructs a `ComponentGroup` how it should layout the components it contains.
* They are always placed one after another, optionally with some padding unless the layout type is
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,66 @@
package roguelikestarterkit.ui.components.common

import roguelikestarterkit.ui.components.group.Padding
import indigo.*
import roguelikestarterkit.ui.component.ComponentFragment
import roguelikestarterkit.ui.datatypes.Bounds
import roguelikestarterkit.ui.datatypes.Coords
import roguelikestarterkit.ui.datatypes.UiContext

object ContainerLikeFunctions:

extension (b: Bounds)
def withPadding(p: Padding): Bounds =
b.moveBy(p.left, p.top).resize(b.width + p.right, b.height + p.bottom)

def calculateNextOffset[ReferenceData](bounds: Bounds, layout: ComponentLayout)(
reference: ReferenceData,
components: Batch[ComponentEntry[?, ReferenceData]]
): Coords =
layout match
case ComponentLayout.Horizontal(padding, Overflow.Hidden) =>
components
.takeRight(1)
.headOption
.map(c =>
c.offset + Coords(c.component.bounds(reference, c.model).withPadding(padding).right, 0)
)
.getOrElse(Coords(padding.left, padding.top))

case ComponentLayout.Horizontal(padding, Overflow.Wrap) =>
val maxY = components
.map(c => c.offset.y + c.component.bounds(reference, c.model).withPadding(padding).height)
.sortWith(_ > _)
.headOption
.getOrElse(0)

components
.takeRight(1)
.headOption
.map { c =>
val padded = c.component.bounds(reference, c.model).withPadding(padding)
val maybeOffset = c.offset + Coords(padded.right, 0)

if padded.moveBy(maybeOffset).right < bounds.width then maybeOffset
else Coords(padding.left, maxY)
}
.getOrElse(Coords(padding.left, padding.top))

case ComponentLayout.Vertical(padding) =>
components
.takeRight(1)
.headOption
.map(c =>
c.offset + Coords(0, c.component.bounds(reference, c.model).withPadding(padding).bottom)
)
.getOrElse(Coords(padding.left, padding.top))

def present[ReferenceData](
context: UiContext[ReferenceData],
components: Batch[ComponentEntry[?, ReferenceData]]
): Outcome[ComponentFragment] =
components
.map { c =>
c.component.present(context.copy(bounds = context.bounds.moveBy(c.offset)), c.model)
}
.sequence
.map(_.foldLeft(ComponentFragment.empty)(_ |+| _))
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roguelikestarterkit.ui.components.group
package roguelikestarterkit.ui.components.common

/** Overflow describes what to do in the event that a component's layout position is beyond the
* bounds of the `ComponentGroup`.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package roguelikestarterkit.ui.components.group
package roguelikestarterkit.ui.components.common

/** Describes the padding between components.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package roguelikestarterkit.ui.components.group

import indigo.*
import roguelikestarterkit.tiles.Tile.r
import roguelikestarterkit.tiles.Tile
import roguelikestarterkit.ui.component.*
import roguelikestarterkit.ui.components.common.ComponentEntry
import roguelikestarterkit.ui.components.common.ComponentLayout
import roguelikestarterkit.ui.components.common.ContainerLikeFunctions
import roguelikestarterkit.ui.components.common.Overflow
import roguelikestarterkit.ui.components.common.Padding
import roguelikestarterkit.ui.datatypes.*

import scala.annotation.tailrec
Expand All @@ -13,7 +18,7 @@ import scala.annotation.tailrec
final case class ComponentGroup[ReferenceData] private (
boundsType: BoundsType,
layout: ComponentLayout,
components: Batch[ComponentGroupEntry[?, ReferenceData]],
components: Batch[ComponentEntry[?, ReferenceData]],
bounds: Bounds,
contentBounds: Bounds,
dirty: Boolean
Expand All @@ -23,7 +28,7 @@ final case class ComponentGroup[ReferenceData] private (
c: Component[A, ReferenceData]
): ComponentGroup[ReferenceData] =
this.copy(
components = components :+ ComponentGroupEntry(Coords.zero, entry, c),
components = components :+ ComponentEntry(Coords.zero, entry, c),
dirty = true
)

Expand Down Expand Up @@ -105,7 +110,8 @@ object ComponentGroup:
def apply[ReferenceData](): ComponentGroup[ReferenceData] =
ComponentGroup(
BoundsType.default,
ComponentLayout.Horizontal(Padding.zero, Overflow.Wrap),
roguelikestarterkit.ui.components.common.ComponentLayout
.Horizontal(Padding.zero, Overflow.Wrap),
Batch.empty,
Bounds.zero,
Bounds.zero,
Expand All @@ -115,7 +121,8 @@ object ComponentGroup:
def apply[ReferenceData](boundsType: BoundsType): ComponentGroup[ReferenceData] =
ComponentGroup(
boundsType,
ComponentLayout.Horizontal(Padding.zero, Overflow.Wrap),
roguelikestarterkit.ui.components.common.ComponentLayout
.Horizontal(Padding.zero, Overflow.Wrap),
Batch.empty,
Bounds.zero,
Bounds.zero,
Expand All @@ -125,7 +132,8 @@ object ComponentGroup:
def apply[ReferenceData](bounds: Bounds): ComponentGroup[ReferenceData] =
ComponentGroup(
BoundsType.Fixed(bounds),
ComponentLayout.Horizontal(Padding.zero, Overflow.Wrap),
roguelikestarterkit.ui.components.common.ComponentLayout
.Horizontal(Padding.zero, Overflow.Wrap),
Batch.empty,
bounds,
Bounds.zero,
Expand Down Expand Up @@ -181,7 +189,7 @@ object ComponentGroup:

private def calculateContentBounds[ReferenceData](
reference: ReferenceData,
components: Batch[ComponentGroupEntry[?, ReferenceData]]
components: Batch[ComponentEntry[?, ReferenceData]]
): Bounds =
components.foldLeft(Bounds.zero) { (acc, c) =>
val bounds = c.component.bounds(reference, c.model).moveTo(c.offset)
Expand All @@ -192,30 +200,25 @@ object ComponentGroup:
context: UiContext[ReferenceData],
model: ComponentGroup[ReferenceData]
): Outcome[ComponentFragment] =
model.components
.map { c =>
c.component.present(context.copy(bounds = context.bounds.moveBy(c.offset)), c.model)
}
.sequence
.map(_.foldLeft(ComponentFragment.empty)(_ |+| _))
ContainerLikeFunctions.present(context, model.components)

def reflow(
reference: ReferenceData,
model: ComponentGroup[ReferenceData]
): ComponentGroup[ReferenceData] =
val reflowed: Batch[ComponentGroupEntry[?, ReferenceData]] = model.components.map { c =>
val reflowed: Batch[ComponentEntry[?, ReferenceData]] = model.components.map { c =>
c.copy(
model = c.component.reflow(reference, c.model)
)
}

val nextOffset =
GroupFunctions.calculateNextOffset[ReferenceData](reference, model.bounds, model.layout)
ContainerLikeFunctions.calculateNextOffset[ReferenceData](model.bounds, model.layout)

val newComponents = reflowed.foldLeft(Batch.empty[ComponentGroupEntry[?, ReferenceData]]) {
val newComponents = reflowed.foldLeft(Batch.empty[ComponentEntry[?, ReferenceData]]) {
(acc, entry) =>
val reflowed = entry.copy(
offset = nextOffset(acc),
offset = nextOffset(reference, acc),
model = entry.component.reflow(reference, entry.model)
)

Expand All @@ -231,7 +234,7 @@ object ComponentGroup:
parentBounds: Bounds
): ComponentGroup[ReferenceData] =
val newBounds: Bounds =
GroupFunctions.calculateCascadeBounds(
calculateCascadeBounds(
model.bounds,
model.contentBounds,
parentBounds,
Expand All @@ -244,3 +247,78 @@ object ComponentGroup:
components = model.components.map(_.cascade(newBounds)),
dirty = true
)

def calculateCascadeBounds(
currentBounds: Bounds,
contentBounds: Bounds,
parentBounds: Bounds,
boundsType: BoundsType
): Bounds =
boundsType match
case BoundsType.Fixed(b) =>
b

case BoundsType.Inherit =>
parentBounds

case BoundsType.Relative(x, y, width, height) =>
Bounds(
(parentBounds.width.toDouble * x).toInt,
(parentBounds.height.toDouble * y).toInt,
(parentBounds.width.toDouble * width).toInt,
(parentBounds.height.toDouble * height).toInt
)

case BoundsType.RelativePosition(x, y) =>
currentBounds.withPosition(
(parentBounds.width.toDouble * x).toInt,
(parentBounds.height.toDouble * y).toInt
)

case BoundsType.RelativeSize(width, height) =>
currentBounds.withDimensions(
(parentBounds.width.toDouble * width).toInt,
(parentBounds.height.toDouble * height).toInt
)

case BoundsType.Offset(amountPosition, amountSize) =>
Bounds(parentBounds.coords + amountPosition, parentBounds.dimensions + amountSize)

case BoundsType.OffsetPosition(amount) =>
currentBounds.withPosition(parentBounds.coords + amount)

case BoundsType.OffsetSize(amount) =>
currentBounds.withDimensions(parentBounds.dimensions + amount)

case BoundsType.Dynamic(FitMode.Available, FitMode.Available) =>
parentBounds

case BoundsType.Dynamic(FitMode.Available, FitMode.Content) =>
parentBounds.withDimensions(
parentBounds.dimensions.width,
contentBounds.dimensions.height
)

case BoundsType.Dynamic(FitMode.Available, FitMode.Fixed(units)) =>
parentBounds.withDimensions(parentBounds.dimensions.width, units)

case BoundsType.Dynamic(FitMode.Content, FitMode.Available) =>
parentBounds.withDimensions(
contentBounds.dimensions.height,
parentBounds.dimensions.width
)

case BoundsType.Dynamic(FitMode.Content, FitMode.Content) =>
contentBounds

case BoundsType.Dynamic(FitMode.Content, FitMode.Fixed(units)) =>
contentBounds.withDimensions(contentBounds.dimensions.width, units)

case BoundsType.Dynamic(FitMode.Fixed(units), FitMode.Available) =>
parentBounds.withDimensions(units, parentBounds.dimensions.height)

case BoundsType.Dynamic(FitMode.Fixed(units), FitMode.Content) =>
contentBounds.withDimensions(units, contentBounds.dimensions.height)

case BoundsType.Dynamic(FitMode.Fixed(unitsW), FitMode.Fixed(unitsH)) =>
currentBounds.withDimensions(unitsW, unitsH)
Loading

0 comments on commit 1b46566

Please sign in to comment.