Skip to content

Commit

Permalink
Add clicked helper methods
Browse files Browse the repository at this point in the history
  • Loading branch information
JD557 committed Nov 12, 2023
1 parent 93cb144 commit 2f40d42
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 35 deletions.
3 changes: 3 additions & 0 deletions core/src/main/scala/eu/joaocosta/interim/InputState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ object InputState:
keyboardInput: String
) extends InputState:

/** If true, then the mouse was released on this frame, performing a click */
lazy val mouseClicked: Boolean = mouseInput.isPressed == false && previousMouseInput.isPressed == true

/** How much the mouse moved in the X axis */
lazy val deltaX: Int =
if (previousMouseInput.x == Int.MinValue || mouseInput.x == Int.MinValue) 0
Expand Down
11 changes: 8 additions & 3 deletions core/src/main/scala/eu/joaocosta/interim/UiContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ final class UiContext private (
if (!passive && (activeItem == None || activeItem == Some(id)) && inputState.mouseInput.isPressed)
activeItem = Some(id)
selectedItem = Some(id)
UiContext.ItemStatus(hotItem.map(_._2) == Some(id), activeItem == Some(id), selectedItem == Some(id))
val hot = hotItem.map(_._2) == Some(id)
val active = activeItem == Some(id)
val selected = selectedItem == Some(id)
val clicked = hot && active && inputState.mouseInput.isPressed == false
UiContext.ItemStatus(hot, active, selected, clicked)

private[interim] def getOrderedOps(): List[RenderOp] =
ops.values.toList.flatten
Expand Down Expand Up @@ -78,10 +82,11 @@ object UiContext:
* @param hot if the mouse is on top of the item
* @param active if the mouse clicked the item (and is still pressed down).
* This value stays true for one extra frame, so that it's
* possible to trigger an action on mouse up.
* possible to trigger an action on mouse up (see `clicked`).
* @param selected if this was the last element clicked
* @param clicked if the mouse clicked this element and was just released.
*/
final case class ItemStatus(hot: Boolean, active: Boolean, selected: Boolean)
final case class ItemStatus(hot: Boolean, active: Boolean, selected: Boolean, clicked: Boolean)

/** Registers an item on the UI state, taking a certain area.
*
Expand Down
7 changes: 3 additions & 4 deletions core/src/main/scala/eu/joaocosta/interim/api/Components.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ trait Components:
val buttonArea = skin.buttonArea(area)
val itemStatus = UiContext.registerItem(id, buttonArea)
skin.renderButton(area, label, itemStatus)
itemStatus.hot && itemStatus.active && summon[InputState].mouseInput.isPressed == false
itemStatus.clicked

/** Checkbox component. Returns true if it's enabled, false otherwise.
*/
Expand All @@ -46,7 +46,7 @@ trait Components:
val checkboxArea = skin.checkboxArea(area)
val itemStatus = UiContext.registerItem(id, checkboxArea)
skin.renderCheckbox(area, value.get, itemStatus)
if (itemStatus.hot && itemStatus.active && summon[InputState].mouseInput.isPressed == false) value.modify(!_)
if (itemStatus.clicked) value.modify(!_)
value.get

/** Radio button component. Returns value currently selected.
Expand All @@ -65,8 +65,7 @@ trait Components:
def applyRef(value: Ref[T]): Component[T] =
val buttonArea = skin.buttonArea(area)
val itemStatus = UiContext.registerItem(id, buttonArea)
if (itemStatus.hot && itemStatus.active && summon[InputState].mouseInput.isPressed == false)
value := buttonValue
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
Expand Down
10 changes: 5 additions & 5 deletions core/src/main/scala/eu/joaocosta/interim/skins/ButtonSkin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ object ButtonSkin extends DefaultSkin:
shadowColor
)
itemStatus match
case UiContext.ItemStatus(false, false, _) =>
case UiContext.ItemStatus(false, false, _, _) =>
rectangle(buttonArea, inactiveColor)
case UiContext.ItemStatus(true, false, _) =>
case UiContext.ItemStatus(true, false, _, _) =>
rectangle(buttonArea, hotColor)
case UiContext.ItemStatus(false, true, _) =>
case UiContext.ItemStatus(false, true, _, _) =>
rectangle(buttonArea, activeColor)
case UiContext.ItemStatus(true, true, _) =>
case UiContext.ItemStatus(true, true, _, _) =>
rectangle(clickedArea, activeColor)
itemStatus match
case UiContext.ItemStatus(true, true, _) =>
case UiContext.ItemStatus(true, true, _, _) =>
text(clickedArea, textColor, label, font, HorizontalAlignment.Center, VerticalAlignment.Center)
case _ =>
text(buttonArea, textColor, label, font, HorizontalAlignment.Center, VerticalAlignment.Center)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ object CheckboxSkin extends DefaultSkin:
def renderCheckbox(area: Rect, value: Boolean, itemStatus: UiContext.ItemStatus)(using uiContext: UiContext): Unit =
val checkboxArea = this.checkboxArea(area)
itemStatus match
case UiContext.ItemStatus(false, false, _) =>
case UiContext.ItemStatus(false, false, _, _) =>
rectangle(checkboxArea, inactiveColor)
case UiContext.ItemStatus(true, false, _) =>
case UiContext.ItemStatus(true, false, _, _) =>
rectangle(checkboxArea, hotColor)
case UiContext.ItemStatus(_, true, _) =>
case UiContext.ItemStatus(_, true, _, _) =>
rectangle(checkboxArea, activeColor)
if (value)
rectangle(
Expand Down
18 changes: 9 additions & 9 deletions core/src/main/scala/eu/joaocosta/interim/skins/HandleSkin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ object HandleSkin extends DefaultSkin:
def renderMoveHandle(area: Rect, itemStatus: UiContext.ItemStatus)(using uiContext: UiContext): Unit =
val handleArea = this.moveHandleArea(area)
val color = itemStatus match
case UiContext.ItemStatus(false, false, _) => inactiveColor
case UiContext.ItemStatus(true, false, _) => hotColor
case UiContext.ItemStatus(_, true, _) => activeColor
case UiContext.ItemStatus(false, false, _, _) => inactiveColor
case UiContext.ItemStatus(true, false, _, _) => hotColor
case UiContext.ItemStatus(_, true, _, _) => activeColor
val lineHeight = handleArea.h / 3
rectangle(handleArea.copy(h = lineHeight), color)
rectangle(handleArea.copy(y = handleArea.y + 2 * lineHeight, h = lineHeight), color)
Expand All @@ -41,9 +41,9 @@ object HandleSkin extends DefaultSkin:
def renderCloseHandle(area: Rect, itemStatus: UiContext.ItemStatus)(using uiContext: UiContext): Unit =
val handleArea = this.closeHandleArea(area)
val color = itemStatus match
case UiContext.ItemStatus(false, false, _) => inactiveColor
case UiContext.ItemStatus(true, false, _) => hotColor
case UiContext.ItemStatus(_, true, _) => activeColor
case UiContext.ItemStatus(false, false, _, _) => inactiveColor
case UiContext.ItemStatus(true, false, _, _) => hotColor
case UiContext.ItemStatus(_, true, _, _) => activeColor
rectangle(handleArea, color)

def resizeHandleArea(area: Rect): Rect =
Expand All @@ -53,9 +53,9 @@ object HandleSkin extends DefaultSkin:
def renderResizeHandle(area: Rect, itemStatus: UiContext.ItemStatus)(using uiContext: UiContext): Unit =
val handleArea = this.resizeHandleArea(area)
val color = itemStatus match
case UiContext.ItemStatus(false, false, _) => inactiveColor
case UiContext.ItemStatus(true, false, _) => hotColor
case UiContext.ItemStatus(_, true, _) => activeColor
case UiContext.ItemStatus(false, false, _, _) => inactiveColor
case UiContext.ItemStatus(true, false, _, _) => hotColor
case UiContext.ItemStatus(_, true, _, _) => activeColor
val lineSize = handleArea.h / 3
rectangle(handleArea.move(dx = handleArea.w - lineSize, dy = 0).copy(w = lineSize), color)
rectangle(handleArea.move(dx = 0, dy = handleArea.h - lineSize).copy(h = lineSize), color)
Expand Down
12 changes: 6 additions & 6 deletions core/src/main/scala/eu/joaocosta/interim/skins/SelectSkin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ object SelectSkin extends DefaultSkin:
val selectBoxArea = this.selectBoxArea(area)
val selectedLabel = labels.applyOrElse(value, _ => "")
itemStatus match
case UiContext.ItemStatus(_, _, true) | UiContext.ItemStatus(_, true, _) =>
case UiContext.ItemStatus(_, _, true, _) | UiContext.ItemStatus(_, true, _, _) =>
rectangle(selectBoxArea, activeColor)
case UiContext.ItemStatus(true, _, _) =>
case UiContext.ItemStatus(true, _, _, _) =>
rectangle(selectBoxArea, hotColor)
case UiContext.ItemStatus(_, _, _) =>
case UiContext.ItemStatus(_, _, _, _) =>
rectangle(selectBoxArea, inactiveColor)
text(
selectBoxArea.shrink(border),
Expand All @@ -61,11 +61,11 @@ object SelectSkin extends DefaultSkin:
val optionLabel = labels.applyOrElse(value, _ => "")
onTop:
itemStatus match
case UiContext.ItemStatus(_, _, true) | UiContext.ItemStatus(_, true, _) =>
case UiContext.ItemStatus(_, _, true, _) | UiContext.ItemStatus(_, true, _, _) =>
rectangle(selectOptionArea, activeColor)
case UiContext.ItemStatus(true, _, _) =>
case UiContext.ItemStatus(true, _, _, _) =>
rectangle(selectOptionArea, hotColor)
case UiContext.ItemStatus(_, _, _) =>
case _ =>
rectangle(selectOptionArea, inactiveColor)
text(
selectOptionArea.shrink(border),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ object SliderSkin extends DefaultSkin:
Rect(area.x + padding, area.y + padding + pos, sliderFill, sliderSize)
rectangle(area, scrollbarColor) // Scrollbar
itemStatus match
case UiContext.ItemStatus(false, false, _) =>
case UiContext.ItemStatus(false, false, _, _) =>
rectangle(sliderRect, inactiveColor)
case UiContext.ItemStatus(true, false, _) =>
case UiContext.ItemStatus(true, false, _, _) =>
rectangle(sliderRect, hotColor)
case _ =>
rectangle(sliderRect, activeColor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ object TextInputSkin extends DefaultSkin:
def renderTextInput(area: Rect, value: String, itemStatus: UiContext.ItemStatus)(using uiContext: UiContext): Unit =
val textInputArea = this.textInputArea(area)
itemStatus match
case UiContext.ItemStatus(_, _, true) | UiContext.ItemStatus(_, true, _) =>
case UiContext.ItemStatus(_, _, true, _) | UiContext.ItemStatus(_, true, _, _) =>
rectangle(area, activeColor)
case UiContext.ItemStatus(true, _, _) =>
case UiContext.ItemStatus(true, _, _, _) =>
rectangle(area, hotColor)
case UiContext.ItemStatus(_, _, _) =>
case _ =>
rectangle(area, inactiveColor)
rectangle(textInputArea, textAreaColor)
text(
Expand Down
15 changes: 15 additions & 0 deletions core/src/test/scala/eu/joaocosta/interim/UiContextSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class UiContextSpec extends munit.FunSuite:
assertEquals(itemStatus.hot, false)
assertEquals(itemStatus.active, false)
assertEquals(itemStatus.selected, false)
assertEquals(itemStatus.clicked, false)
assertEquals(uiContext.hotItem, None)
assertEquals(uiContext.activeItem, None)
assertEquals(uiContext.selectedItem, None)
Expand All @@ -20,6 +21,7 @@ class UiContextSpec extends munit.FunSuite:
assertEquals(itemStatus.hot, true)
assertEquals(itemStatus.active, false)
assertEquals(itemStatus.selected, false)
assertEquals(itemStatus.clicked, false)
assertEquals(uiContext.hotItem, Some(0 -> 1))
assertEquals(uiContext.activeItem, None)
assertEquals(uiContext.selectedItem, None)
Expand All @@ -31,10 +33,22 @@ class UiContextSpec extends munit.FunSuite:
assertEquals(itemStatus.hot, true)
assertEquals(itemStatus.active, true)
assertEquals(itemStatus.selected, true)
assertEquals(itemStatus.clicked, false)
assertEquals(uiContext.hotItem, Some(0 -> 1))
assertEquals(uiContext.activeItem, Some(1))
assertEquals(uiContext.selectedItem, Some(1))

test("registerItem should mark a clicked item as clicked once the mouse is released"):
val uiContext: UiContext = new UiContext()
val inputState1: InputState = InputState(5, 5, true, "")
UiContext.registerItem(1, Rect(1, 1, 10, 10))(using uiContext, inputState1)
val inputState2: InputState = InputState(5, 5, false, "")
val itemStatus = UiContext.registerItem(1, Rect(1, 1, 10, 10))(using uiContext, inputState2)
assertEquals(itemStatus.hot, true)
assertEquals(itemStatus.active, true)
assertEquals(itemStatus.selected, true)
assertEquals(itemStatus.clicked, true)

test("registerItem should not override an active item with another one"):
val uiContext = new UiContext()
val inputState1 = InputState(5, 5, true, "")
Expand All @@ -45,6 +59,7 @@ class UiContextSpec extends munit.FunSuite:
assertEquals(itemStatus.hot, true)
assertEquals(itemStatus.active, false)
assertEquals(itemStatus.selected, false)
assertEquals(itemStatus.clicked, false)
assertEquals(uiContext.hotItem, Some(0 -> 2))
assertEquals(uiContext.activeItem, Some(1))
assertEquals(uiContext.selectedItem, Some(1))
Expand Down

0 comments on commit 2f40d42

Please sign in to comment.