Skip to content

Commit

Permalink
Make Layout a widget (fixes #41)
Browse files Browse the repository at this point in the history
Massively improved DX aside, this is the first step towards more react-like components in tui.

Next steps I'll try:
- Make `BlockWidget` take children, so we don't need to duplicate the logic in all widgets
- Make `Widgets` return a datatype akin to `ReactElement` which will call `render` for us in most cases. Probably with a fallback
  • Loading branch information
oyvindberg committed Apr 30, 2023
1 parent faf534b commit 5fec02d
Show file tree
Hide file tree
Showing 24 changed files with 632 additions and 735 deletions.
75 changes: 33 additions & 42 deletions demo/src/scala/tuiexamples/BarChartExample.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ object BarChartExample {
}

while (true) {
terminal.draw(f => ui(f, app))
terminal.draw(f => ui(app).render(f.buffer.area, f.buffer))

if (jni.poll(timeout)) {
jni.read() match {
Expand All @@ -61,46 +61,37 @@ object BarChartExample {
}
}

def ui(f: Frame, app: App): Unit = {
val verticalChunks = Layout(
direction = Direction.Vertical,
margin = Margin(2, 2),
constraints = Array(Constraint.Percentage(50), Constraint.Percentage(50))
).split(f.size)

val barchart1 = BarChartWidget(
block = Some(BlockWidget(title = Some(Spans.nostyle("Data1")), borders = Borders.ALL)),
data = app.data,
barWidth = 9,
barStyle = Style(fg = Some(Color.Yellow)),
valueStyle = Style(fg = Some(Color.Black), bg = Some(Color.Yellow))
)
f.renderWidget(barchart1, verticalChunks(0))

val horizontalChunks = Layout(
direction = Direction.Horizontal,
constraints = Array(Constraint.Percentage(50), Constraint.Percentage(50))
).split(verticalChunks(1))

val barchart2 = BarChartWidget(
block = Some(BlockWidget(title = Some(Spans.nostyle("Data2")), borders = Borders.ALL)),
barWidth = 5,
barGap = 3,
barStyle = Style(fg = Some(Color.Green)),
valueStyle = Style(bg = Some(Color.Green), addModifier = Modifier.BOLD),
data = app.data
def ui(app: App): Widget =
Layout(direction = Direction.Vertical, margin = Margin(2, 2))(
Constraint.Percentage(50) ->
BarChartWidget(
block = Some(BlockWidget(title = Some(Spans.nostyle("Data1")), borders = Borders.ALL)),
data = app.data,
barWidth = 9,
barStyle = Style(fg = Some(Color.Yellow)),
valueStyle = Style(fg = Some(Color.Black), bg = Some(Color.Yellow))
),
Constraint.Percentage(50) ->
Layout(direction = Direction.Horizontal)(
Constraint.Percentage(50) ->
BarChartWidget(
block = Some(BlockWidget(title = Some(Spans.nostyle("Data2")), borders = Borders.ALL)),
barWidth = 5,
barGap = 3,
barStyle = Style(fg = Some(Color.Green)),
valueStyle = Style(bg = Some(Color.Green), addModifier = Modifier.BOLD),
data = app.data
),
Constraint.Percentage(50) ->
BarChartWidget(
block = Some(BlockWidget(title = Some(Spans.nostyle("Data3")), borders = Borders.ALL)),
data = app.data,
barStyle = Style(fg = Some(Color.Red)),
barWidth = 7,
barGap = 0,
valueStyle = Style(bg = Some(Color.Red)),
labelStyle = Style(fg = Some(Color.Cyan), addModifier = Modifier.ITALIC)
)
)
)
f.renderWidget(barchart2, horizontalChunks(0))

val barchart3 = BarChartWidget(
block = Some(BlockWidget(title = Some(Spans.nostyle("Data3")), borders = Borders.ALL)),
data = app.data,
barStyle = Style(fg = Some(Color.Red)),
barWidth = 7,
barGap = 0,
valueStyle = Style(bg = Some(Color.Red)),
labelStyle = Style(fg = Some(Color.Cyan), addModifier = Modifier.ITALIC)
)
f.renderWidget(barchart3, horizontalChunks(1))
}
}
75 changes: 26 additions & 49 deletions demo/src/scala/tuiexamples/BlockExample.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,57 +35,34 @@ object BlockExample {
)
f.renderWidget(block0, size)

val chunks = Layout(
direction = Direction.Vertical,
margin = Margin(4),
constraints = Array(Constraint.Percentage(50), Constraint.Percentage(50))
)
.split(f.size)

// Top two inner blocks
val top_chunks = Layout(
direction = Direction.Horizontal,
constraints = Array(Constraint.Percentage(50), Constraint.Percentage(50))
)
.split(chunks(0))

// Top left inner block with green background
val block_top0 = BlockWidget(
title = Some(
Spans.from(
Span.styled("With", Style.DEFAULT.fg(Color.Yellow)),
Span.nostyle(" background")
val layout = Layout(direction = Direction.Vertical, margin = Margin(4))(
Constraint.Percentage(50) -> Layout(direction = Direction.Horizontal)(
// Top left inner block with green background
Constraint.Percentage(50) -> BlockWidget(
title = Some(
Spans.from(
Span.styled("With", Style.DEFAULT.fg(Color.Yellow)),
Span.nostyle(" background")
)
),
style = Style.DEFAULT.bg(Color.Green)
),
// Top right inner block with styled title aligned to the right
Constraint.Percentage(50) -> BlockWidget(
title = Some(Spans.from(Span.styled("Styled title", Style(fg = Some(Color.White), bg = Some(Color.Red), addModifier = Modifier.BOLD)))),
titleAlignment = Alignment.Right
)
),
style = Style.DEFAULT.bg(Color.Green)
)
f.renderWidget(block_top0, top_chunks(0))

// Top right inner block with styled title aligned to the right
val block_top1 = BlockWidget(
title = Some(Spans.from(Span.styled("Styled title", Style(fg = Some(Color.White), bg = Some(Color.Red), addModifier = Modifier.BOLD)))),
titleAlignment = Alignment.Right
)
f.renderWidget(block_top1, top_chunks(1))

// Bottom two inner blocks
val bottom_chunks = Layout(
direction = Direction.Horizontal,
constraints = Array(Constraint.Percentage(50), Constraint.Percentage(50))
)
.split(chunks(1))

// Bottom left block with all default borders
val block_bottom_0 = BlockWidget(title = Some(Spans.nostyle("With borders")), borders = Borders.ALL)
f.renderWidget(block_bottom_0, bottom_chunks(0))

// Bottom right block with styled left and right border
val block_bottom_1 = BlockWidget(
title = Some(Spans.nostyle("With styled borders and doubled borders")),
borderStyle = Style.DEFAULT.fg(Color.Cyan),
borders = Borders.LEFT | Borders.RIGHT,
borderType = BlockWidget.BorderType.Double
Constraint.Percentage(50) -> Layout(direction = Direction.Horizontal)(
Constraint.Percentage(50) -> BlockWidget(title = Some(Spans.nostyle("With borders")), borders = Borders.ALL),
Constraint.Percentage(50) -> BlockWidget(
title = Some(Spans.nostyle("With styled borders and doubled borders")),
borderStyle = Style.DEFAULT.fg(Color.Cyan),
borders = Borders.LEFT | Borders.RIGHT,
borderType = BlockWidget.BorderType.Double
)
)
)
f.renderWidget(block_bottom_1, bottom_chunks(1))
layout.render(f.size, f.buffer)
}
}
44 changes: 18 additions & 26 deletions demo/src/scala/tuiexamples/CanvasExample.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,30 +90,22 @@ object CanvasExample {
}
}

def ui(f: Frame, app: App): Unit = {
val chunks = Layout(
direction = Direction.Horizontal,
constraints = Array(Constraint.Percentage(50), Constraint.Percentage(50))
)
.split(f.size)

val canvas0 = CanvasWidget(
block = Some(BlockWidget(borders = Borders.ALL, title = Some(Spans.nostyle("World")))),
yBounds = Point(-90.0, 90.0),
xBounds = Point(-180.0, 180.0)
) { ctx =>
ctx.draw(WorldMap(color = Color.White, resolution = MapResolution.High))
ctx.print(app.x, -app.y, Spans.from(Span.styled("You are here", Style(fg = Some(Color.Yellow)))))
}
f.renderWidget(canvas0, chunks(0))

val canvas1 = CanvasWidget(
block = Some(BlockWidget(borders = Borders.ALL, title = Some(Spans.nostyle("Pong")))),
yBounds = Point(10.0, 110.0),
xBounds = Point(10.0, 110.0)
) { ctx =>
ctx.draw(app.ball)
}
f.renderWidget(canvas1, chunks(1))
}
def ui(f: Frame, app: App): Unit =
Layout(direction = Direction.Horizontal)(
Constraint.Percentage(50) -> CanvasWidget(
block = Some(BlockWidget(borders = Borders.ALL, title = Some(Spans.nostyle("World")))),
yBounds = Point(-90.0, 90.0),
xBounds = Point(-180.0, 180.0)
) { ctx =>
ctx.draw(WorldMap(color = Color.White, resolution = MapResolution.High))
ctx.print(app.x, -app.y, Spans.from(Span.styled("You are here", Style(fg = Some(Color.Yellow)))))
},
Constraint.Percentage(50) -> CanvasWidget(
block = Some(BlockWidget(borders = Borders.ALL, title = Some(Spans.nostyle("Pong")))),
yBounds = Point(10.0, 110.0),
xBounds = Point(10.0, 110.0)
) { ctx =>
ctx.draw(app.ball)
}
).render(f.size, f.buffer)
}
80 changes: 32 additions & 48 deletions demo/src/scala/tuiexamples/ChartExample.scala
Original file line number Diff line number Diff line change
Expand Up @@ -88,28 +88,39 @@ object ChartExample {
}

def ui(f: Frame, app: App): Unit = {
val size = f.size
val chunks = Layout(
direction = Direction.Vertical,
constraints = Array(Constraint.Ratio(1, 3), Constraint.Ratio(1, 3), Constraint.Ratio(1, 3))
).split(size)
val datasets0 = Array(
ChartWidget.Dataset(name = "data2", marker = symbols.Marker.Dot, style = Style(fg = Some(Color.Cyan)), data = app.data1),
ChartWidget.Dataset(name = "data3", marker = symbols.Marker.Braille, style = Style(fg = Some(Color.Yellow)), data = app.data2)
)
val datasets1 = Array(
ChartWidget.Dataset(
name = "data",
marker = symbols.Marker.Braille,
style = Style(fg = Some(Color.Yellow)),
graphType = ChartWidget.GraphType.Line,
data = DATA
)
)
val datasets2 = Array(
ChartWidget.Dataset(
name = "data",
marker = symbols.Marker.Braille,
style = Style(fg = Some(Color.Yellow)),
graphType = ChartWidget.GraphType.Line,
data = DATA2
)
)

val Bold = Style(addModifier = Modifier.BOLD)

val x_labels = Array(
Span.styled(app.window.x.toString, Bold),
Span.nostyle(((app.window.x + app.window.y) / 2.0).toString),
Span.styled(app.window.y.toString, Bold)
)

{
val datasets = Array(
ChartWidget.Dataset(name = "data2", marker = symbols.Marker.Dot, style = Style(fg = Some(Color.Cyan)), data = app.data1),
ChartWidget.Dataset(name = "data3", marker = symbols.Marker.Braille, style = Style(fg = Some(Color.Yellow)), data = app.data2)
)

val chart = ChartWidget(
datasets = datasets,
Layout(direction = Direction.Vertical)(
Constraint.Ratio(1, 3) -> ChartWidget(
datasets = datasets0,
block = Some(
BlockWidget(title = Some(Spans.from(Span.styled("Chart 1", Style(fg = Some(Color.Cyan), addModifier = Modifier.BOLD)))), borders = Borders.ALL)
),
Expand All @@ -126,22 +137,9 @@ object ChartExample {
),
bounds = Point(-20.0, 20.0)
)
)
f.renderWidget(chart, chunks(0))
}

{
val datasets = Array(
ChartWidget.Dataset(
name = "data",
marker = symbols.Marker.Braille,
style = Style(fg = Some(Color.Yellow)),
graphType = ChartWidget.GraphType.Line,
data = DATA
)
)
val chart = ChartWidget(
datasets = datasets,
),
Constraint.Ratio(1, 3) -> ChartWidget(
datasets = datasets1,
block = Some(
BlockWidget(
title = Some(Spans.from(Span.styled("Chart 2", Style(fg = Some(Color.Cyan), addModifier = Modifier.BOLD)))),
Expand All @@ -160,22 +158,9 @@ object ChartExample {
bounds = Point(0.0, 5.0),
labels = Some(Array(Span.styled("0", Bold), Span.nostyle("2.5"), Span.styled("5.0", Bold)))
)
)
f.renderWidget(chart, chunks(1))
}

{
val datasets = Array(
ChartWidget.Dataset(
name = "data",
marker = symbols.Marker.Braille,
style = Style(fg = Some(Color.Yellow)),
graphType = ChartWidget.GraphType.Line,
data = DATA2
)
)
val chart = ChartWidget(
datasets = datasets,
),
Constraint.Ratio(1, 3) -> ChartWidget(
datasets = datasets2,
block = Some(
BlockWidget(title = Some(Spans.from(Span.styled("Chart 3", Style(fg = Some(Color.Cyan), addModifier = Modifier.BOLD)))), borders = Borders.ALL)
),
Expand All @@ -192,7 +177,6 @@ object ChartExample {
labels = Some(Array(Span.styled("0", Bold), Span.nostyle("2.5"), Span.styled("5", Bold)))
)
)
f.renderWidget(chart, chunks(2))
}
).render(f.size, f.buffer)
}
}
Loading

0 comments on commit 5fec02d

Please sign in to comment.