Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it easier to implement a ComponentWithValue #73

Merged
merged 3 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion core/src/main/scala/eu/joaocosta/interim/Ref.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,20 @@ final case class Ref[T](private var value: T):
value = newValue
this

/** Modifies the value pf this Ref.
/** Modifies the value of this Ref.
* Shorthand for `ref := f(ref.value)`
*/
def modify(f: T => T): this.type =
value = f(value)
this

/** Modifies the value of this Ref if the condition is true.
* Shorthand for `if (cond) ref := f(ref.value) else ref`
*/
def modifyIf(cond: Boolean)(f: T => T): this.type =
if (cond) value = f(value)
this

object Ref:

/** Creates a Ref that can be used inside a block and returns that value.
Expand Down
56 changes: 24 additions & 32 deletions core/src/main/scala/eu/joaocosta/interim/api/Components.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ trait Components:
type Component[+T] = (inputState: InputState.Historical, uiContext: UiContext) ?=> T

trait ComponentWithValue[T]:
def applyRef(value: Ref[T]): Component[T]
def render(value: Ref[T]): Component[Unit]

def applyRef(value: Ref[T]): Component[T] =
render(value)
value.get

def applyValue(value: T): Component[T] =
apply(Ref(value))
Expand Down Expand Up @@ -42,12 +46,11 @@ trait Components:
*/
final def checkbox(id: ItemId, area: Rect, skin: CheckboxSkin = CheckboxSkin.default()): ComponentWithValue[Boolean] =
new ComponentWithValue[Boolean]:
def applyRef(value: Ref[Boolean]): Component[Boolean] =
def render(value: Ref[Boolean]): Component[Unit] =
val checkboxArea = skin.checkboxArea(area)
val itemStatus = UiContext.registerItem(id, checkboxArea)
skin.renderCheckbox(area, value.get, itemStatus)
if (itemStatus.clicked) value.modify(!_)
value.get
value.modifyIf(itemStatus.clicked)(!_)

/** Radio button component. Returns value currently selected.
*
Expand All @@ -62,13 +65,12 @@ trait Components:
skin: ButtonSkin = ButtonSkin.default()
): ComponentWithValue[T] =
new ComponentWithValue[T]:
def applyRef(value: Ref[T]): Component[T] =
def render(value: Ref[T]): Component[Unit] =
val buttonArea = skin.buttonArea(area)
val itemStatus = UiContext.registerItem(id, buttonArea)
if (itemStatus.clicked) value := buttonValue
if (value.get == buttonValue) skin.renderButton(area, label, itemStatus.copy(hot = true, active = true))
else skin.renderButton(area, label, itemStatus)
value.get

/** Select box component. Returns the index value currently selected inside a PanelState.
*
Expand All @@ -81,19 +83,18 @@ trait Components:
skin: SelectSkin = SelectSkin.default()
): ComponentWithValue[PanelState[Int]] =
new ComponentWithValue[PanelState[Int]]:
def applyRef(value: Ref[PanelState[Int]]): Component[PanelState[Int]] =
def render(value: Ref[PanelState[Int]]): Component[Unit] =
val selectBoxArea = skin.selectBoxArea(area)
val itemStatus = UiContext.registerItem(id, area)
if (itemStatus.selected) value.modify(_.open)
value.modifyIf(itemStatus.selected)(_.open)
skin.renderSelectBox(area, value.get.value, labels, itemStatus)
if (value.get.isOpen)
if (!itemStatus.selected) value.modify(_.close)
value.modifyIf(!itemStatus.selected)(_.close)
labels.zipWithIndex.foreach: (label, idx) =>
val selectOptionArea = skin.selectOptionArea(area, idx)
val optionStatus = UiContext.registerItem(id |> idx, selectOptionArea)
skin.renderSelectOption(area, idx, labels, optionStatus)
if (optionStatus.active) value := PanelState.closed(idx)
value.get

/** Slider component. Returns the current position of the slider, between min and max.
*
Expand All @@ -108,7 +109,7 @@ trait Components:
skin: SliderSkin = SliderSkin.default()
): ComponentWithValue[Int] =
new ComponentWithValue[Int]:
def applyRef(value: Ref[Int]): Component[Int] =
def render(value: Ref[Int]): Component[Unit] =
val sliderArea = skin.sliderArea(area)
val sliderSize = skin.sliderSize(area, min, max)
val range = max - min
Expand All @@ -124,7 +125,6 @@ trait Components:
val mousePos = summon[InputState].mouseInput.y - sliderArea.y - sliderSize / 2
val maxPos = sliderArea.h - sliderSize
value := math.max(min, math.min((mousePos * range) / maxPos, max))
value.get

/** Text input component. Returns the current string inputed.
*/
Expand All @@ -134,13 +134,11 @@ trait Components:
skin: TextInputSkin = TextInputSkin.default()
): ComponentWithValue[String] =
new ComponentWithValue[String]:
def applyRef(value: Ref[String]): Component[String] =
def render(value: Ref[String]): Component[Unit] =
val textInputArea = skin.textInputArea(area)
val itemStatus = UiContext.registerItem(id, textInputArea)
skin.renderTextInput(area, value.get, itemStatus)
if (itemStatus.selected)
value.modify(summon[InputState].appendKeyboardInput)
value.get
value.modifyIf(itemStatus.selected)(summon[InputState].appendKeyboardInput)

/** Draggable handle. Returns the moved area.
*
Expand All @@ -149,15 +147,13 @@ trait Components:
*/
final def moveHandle(id: ItemId, area: Rect, skin: HandleSkin = HandleSkin.default()): ComponentWithValue[Rect] =
new ComponentWithValue[Rect]:
def applyRef(value: Ref[Rect]): Component[Rect] =
def render(value: Ref[Rect]): Component[Unit] =
val handleArea = skin.moveHandleArea(area)
val itemStatus = UiContext.registerItem(id, handleArea)
val deltaX = summon[InputState.Historical].deltaX
val deltaY = summon[InputState.Historical].deltaY
skin.renderMoveHandle(area, itemStatus)
if (itemStatus.active)
val deltaX = summon[InputState.Historical].deltaX
val deltaY = summon[InputState.Historical].deltaY
value.modify(_.move(deltaX, deltaY))
value.get
value.modifyIf(itemStatus.active)(_.move(deltaX, deltaY))

/** Draggable handle. Returns the resized area.
*
Expand All @@ -166,15 +162,13 @@ trait Components:
*/
final def resizeHandle(id: ItemId, area: Rect, skin: HandleSkin = HandleSkin.default()): ComponentWithValue[Rect] =
new ComponentWithValue[Rect]:
def applyRef(value: Ref[Rect]): Component[Rect] =
def render(value: Ref[Rect]): Component[Unit] =
val handleArea = skin.resizeHandleArea(area)
val itemStatus = UiContext.registerItem(id, handleArea)
val deltaX = summon[InputState.Historical].deltaX
val deltaY = summon[InputState.Historical].deltaY
skin.renderResizeHandle(area, itemStatus)
if (itemStatus.active)
val deltaX = summon[InputState.Historical].deltaX
val deltaY = summon[InputState.Historical].deltaY
value.modify(_.resize(deltaX, deltaY))
value.get
value.modifyIf(itemStatus.active)(_.resize(deltaX, deltaY))

/** Close handle. Closes the panel when clicked.
*
Expand All @@ -187,10 +181,8 @@ trait Components:
skin: HandleSkin = HandleSkin.default()
): ComponentWithValue[PanelState[T]] =
new ComponentWithValue[PanelState[T]]:
def applyRef(value: Ref[PanelState[T]]): Component[PanelState[T]] =
def render(value: Ref[PanelState[T]]): Component[Unit] =
val handleArea = skin.closeHandleArea(area)
val itemStatus = UiContext.registerItem(id, handleArea)
skin.renderCloseHandle(area, itemStatus)
if (itemStatus.clicked)
value.modify(_.close)
value.get
value.modifyIf(itemStatus.clicked)(_.close)
8 changes: 8 additions & 0 deletions core/src/test/scala/eu/joaocosta/interim/RefSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ class RefSpec extends munit.FunSuite:
assertEquals(x.modify(_ + 1).get, 2)
assertEquals(x.get, 2)

test("Ref values can be modified with modifyIf"):
val x = Ref(1)

assertEquals(x.modifyIf(false)(_ + 1).get, 1)
assertEquals(x.get, 1)
assertEquals(x.modifyIf(true)(_ + 1).get, 2)
assertEquals(x.get, 2)

test("withRef allows to use a temporary Ref value"):
val result = Ref.withRef(0): ref =>
ref.modify(_ + 2)
Expand Down
Loading