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

Box title alignment support #371

Merged
merged 1 commit into from
Apr 24, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package org.hexworks.zircon.api
import org.hexworks.cobalt.databinding.api.extension.createPropertyFrom
import org.hexworks.zircon.api.component.Component
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer.Alignment
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer.RenderingMode
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer.RenderingMode.NON_INTERACTIVE
import org.hexworks.zircon.api.graphics.BoxType
Expand Down Expand Up @@ -42,11 +43,13 @@ object ComponentDecorations {
fun box(
boxType: BoxType = BoxType.SINGLE,
title: String = "",
renderingMode: RenderingMode = NON_INTERACTIVE
renderingMode: RenderingMode = NON_INTERACTIVE,
titleAlignment: Alignment = Alignment.TOP_LEFT
): ComponentDecorationRenderer = BoxDecorationRenderer(
boxType = boxType,
titleProperty = createPropertyFrom(title),
renderingMode = renderingMode
renderingMode = renderingMode,
titleAlignment = titleAlignment
)

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.hexworks.zircon.api.component.renderer

import org.hexworks.zircon.api.component.data.ComponentState
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer.Alignment
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer.RenderingMode.INTERACTIVE
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer.RenderingMode.NON_INTERACTIVE
import org.hexworks.zircon.api.data.Position
Expand Down Expand Up @@ -33,4 +34,37 @@ interface ComponentDecorationRenderer : DecorationRenderer<ComponentDecorationRe
enum class RenderingMode {
INTERACTIVE, NON_INTERACTIVE
}

/**
* Alignment for decorator attributes like box title.
*/
enum class Alignment {
TOP_LEFT,
TOP_CENTER,
TOP_RIGHT,
BOTTOM_LEFT,
BOTTOM_CENTER,
BOTTOM_RIGHT
}
}

internal fun Alignment.isLeft() = when(this) {
Alignment.TOP_LEFT, Alignment.BOTTOM_LEFT -> true
else -> false
}
internal fun Alignment.isRight() = when(this) {
Alignment.TOP_RIGHT, Alignment.BOTTOM_RIGHT -> true
else -> false
}
internal fun Alignment.isCenter() = when(this) {
Alignment.TOP_CENTER, Alignment.BOTTOM_CENTER -> true
else -> false
}
internal fun Alignment.isTop() = when (this) {
Alignment.TOP_LEFT, Alignment.TOP_RIGHT, Alignment.TOP_CENTER -> true
else -> false
}
internal fun Alignment.isBottom() = when (this) {
Alignment.BOTTOM_LEFT, Alignment.BOTTOM_RIGHT, Alignment.BOTTOM_CENTER -> true
else -> false
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import org.hexworks.cobalt.databinding.api.property.Property
import org.hexworks.zircon.api.behavior.TitleOverride
import org.hexworks.zircon.api.builder.data.TileBuilder
import org.hexworks.zircon.api.builder.graphics.BoxBuilder
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderContext
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer
import org.hexworks.zircon.api.component.renderer.*
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer.Alignment
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer.RenderingMode
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer.RenderingMode.NON_INTERACTIVE
import org.hexworks.zircon.api.component.renderer.isRight
import org.hexworks.zircon.api.component.renderer.isTop
import org.hexworks.zircon.api.data.Position
import org.hexworks.zircon.api.data.Size
import org.hexworks.zircon.api.graphics.BoxType
Expand All @@ -17,7 +19,8 @@ import org.hexworks.zircon.api.graphics.TileGraphics
data class BoxDecorationRenderer(
val boxType: BoxType = BoxType.SINGLE,
private val titleProperty: Property<String> = createPropertyFrom(""),
private val renderingMode: RenderingMode = NON_INTERACTIVE
private val renderingMode: RenderingMode = NON_INTERACTIVE,
private val titleAlignment: Alignment = Alignment.TOP_LEFT
) : ComponentDecorationRenderer {

override val offset = Position.offset1x1()
Expand Down Expand Up @@ -47,13 +50,24 @@ data class BoxDecorationRenderer(
} else {
finalTitle
}
val titleOffsetX = when {
titleAlignment.isLeft() -> 0
titleAlignment.isRight() -> size.width - cleanText.length - 4
titleAlignment.isCenter() -> (size.width - cleanText.length - 4) / 2
else -> throw IllegalStateException("unreachable")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should have written this should never happen 😂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want me to? 😛

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, it is OK.

}
val titleOffsetY = when {
titleAlignment.isTop() -> 0
titleAlignment.isBottom() -> size.height - 1
else -> throw IllegalStateException("unreachable")
}
tileGraphics.draw(
TileBuilder.newBuilder()
.withStyleSet(style)
.withCharacter(boxType.connectorLeft)
.build(), Position.create(1, 0)
.build(), Position.create(1 + titleOffsetX, titleOffsetY)
)
val pos = Position.create(2, 0)
val pos = Position.create(2 + titleOffsetX, titleOffsetY)
(cleanText.indices).forEach { idx ->
tileGraphics.draw(
tile = TileBuilder.newBuilder()
Expand All @@ -68,7 +82,7 @@ data class BoxDecorationRenderer(
.withStyleSet(style)
.withCharacter(boxType.connectorRight)
.build(),
drawPosition = Position.create(2 + cleanText.length, 0)
drawPosition = Position.create(2 + titleOffsetX + cleanText.length, titleOffsetY)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.hexworks.zircon.internal.component.renderer.decoration

import org.assertj.core.api.Assertions.assertThat
import org.hexworks.zircon.api.CP437TilesetResources
import org.hexworks.zircon.api.ComponentDecorations.box
import org.hexworks.zircon.api.Components
import org.hexworks.zircon.api.DrawSurfaces
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer
import org.hexworks.zircon.api.component.renderer.ComponentDecorationRenderer.Alignment.*
import org.hexworks.zircon.api.data.Size
import org.hexworks.zircon.convertCharacterTilesToString
import org.hexworks.zircon.internal.graphics.Renderable
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized

@RunWith(Parameterized::class)
class BoxDecorationRendererTest(
@Suppress("unused") private val testTitle: String, // used by Parameterized for display in IntelliJ/terminal
private val decorator: ComponentDecorationRenderer,
private val expected: String
) {
@Test
fun renderTest() {
val target = Components.panel()
.withTileset(CP437TilesetResources.rexPaint20x20())
.withDecorations(decorator)
.build()

val graphics = DrawSurfaces.tileGraphicsBuilder().withSize(Size.create(12, 4)).build()
(target as Renderable).render(graphics)
assertThat(graphics.convertCharacterTilesToString()).isEqualTo(expected.trimIndent())
}

companion object {
@Parameterized.Parameters(name = "{index}: {0}")
@JvmStatic
fun data() = listOf(
arrayOf("Default parameters", box(), """
┌──────────┐
│ │
│ │
└──────────┘
"""),
arrayOf("With title", box(title = "Foo"), """
┌┤Foo├─────┐
│ │
│ │
└──────────┘
"""),
arrayOf("With title, right-aligned", box(title = "Foo", titleAlignment = TOP_RIGHT), """
┌─────┤Foo├┐
│ │
│ │
└──────────┘
"""),
// Titlebar is even-sized width, but title is odd-sized, so we're off by half, rounding to the left.
arrayOf("With title, center-aligned, approx center", box(title = "Foo", titleAlignment = TOP_CENTER), """
┌──┤Foo├───┐
│ │
│ │
└──────────┘
"""),
arrayOf("With title, center-aligned, exact center", box(title = "Food", titleAlignment = TOP_CENTER), """
┌──┤Food├──┐
│ │
│ │
└──────────┘
"""),
arrayOf("With title, bottom left-aligned", box(title = "Foo", titleAlignment = BOTTOM_LEFT), """
┌──────────┐
│ │
│ │
└┤Foo├─────┘
"""),
arrayOf("With title, bottom right-aligned", box(title = "Foo", titleAlignment = BOTTOM_RIGHT), """
┌──────────┐
│ │
│ │
└─────┤Foo├┘
"""),
arrayOf("With title, bottom center-aligned", box(title = "Foo", titleAlignment = BOTTOM_CENTER), """
┌──────────┐
│ │
│ │
└──┤Foo├───┘
""")
)
}
}