Plot layout Views on grid #10

Merged
alex merged 6 commits from plot-layout-on-grid into main 2025-12-25 19:50:42 +01:00
4 changed files with 177 additions and 116 deletions
Showing only changes of commit 95eaeb60f4 - Show all commits

View File

@@ -33,13 +33,41 @@ pub type Node {
Cell(content: String, style: Style) Cell(content: String, style: Style)
} }
pub type ViewIdx =
Int
pub type View =
dict.Dict(Section, Node)
// Layout consists of a list Views, and only one View is rendered at a time
pub type Layout { pub type Layout {
Layout(columns: Int, rows: Int, nodes: dict.Dict(Section, Node)) Layout(
columns: Int,
rows: Int,
current_view: ViewIdx,
views: dict.Dict(ViewIdx, View),
)
} }
pub fn new(columns: Int, rows: Int, nodes: List(#(Section, Node))) -> Layout { pub fn new(
columns: Int,
rows: Int,
views: List(List(#(Section, Node))),
) -> Layout {
let views =
list.index_map(views, fn(view_nodes, i) { #(i, view_nodes) })
|> list.fold(dict.new(), fn(view_acc, iv) {
let #(i, view_nodes) = iv
dict.insert(view_acc, i, view_loop(i, view_nodes))
})
Layout(columns:, rows:, current_view: 0, views:)
}
fn view_loop(i: ViewIdx, view_nodes: List(#(Section, Node))) -> View {
let children = let children =
nodes view_nodes
|> list.flat_map(fn(node) { |> list.flat_map(fn(node) {
case pair.second(node) { case pair.second(node) {
Row(children: c, ..) -> c Row(children: c, ..) -> c
@@ -51,22 +79,19 @@ pub fn new(columns: Int, rows: Int, nodes: List(#(Section, Node))) -> Layout {
// All sections that are not children of other nodes will be added as // All sections that are not children of other nodes will be added as
// children to the root // children to the root
let orphans = let orphans =
nodes view_nodes
|> list.map(pair.first) |> list.map(pair.first)
|> list.filter(fn(node) { !set.contains(children, node) }) |> list.filter(fn(node) { !set.contains(children, node) })
let nodes = dict.from_list(view_nodes)
dict.from_list(nodes) |> dict.insert(
|> dict.insert( Section(string.append("view_", string.inspect(i))),
Section(root_section), Row(
Row( content: "",
content: "", style: Style(dimensions: Percent(width: 100, height: 100)),
style: Style(dimensions: Percent(width: 100, height: 100)), children: orphans,
children: orphans, ),
), )
)
Layout(columns:, rows:, nodes:)
} }
pub fn update_section( pub fn update_section(
@@ -74,16 +99,25 @@ pub fn update_section(
section: Section, section: Section,
content: String, content: String,
) -> Layout { ) -> Layout {
case dict.get(layout.nodes, section) { case dict.get(layout.views, layout.current_view) {
Error(_) -> layout Error(_) -> layout
Ok(node) -> { Ok(view) ->
let updated = case node { case dict.get(view, section) {
Cell(..) -> Cell(..node, content: content) Error(_) -> layout
Row(..) -> Row(..node, content: content) Ok(node) -> {
} let updated_node = case node {
Cell(..) -> Cell(..node, content: content)
Row(..) -> Row(..node, content: content)
}
Layout(..layout, nodes: dict.insert(layout.nodes, section, updated)) let updated_view = dict.insert(view, section, updated_node)
}
Layout(
..layout,
views: dict.insert(layout.views, layout.current_view, updated_view),
)
}
}
} }
} }
@@ -107,11 +141,21 @@ pub fn render(layout: Layout) -> Nil {
position_index: 0, position_index: 0,
) )
let buffer: Buffer = dict.new() case dict.get(layout.views, layout.current_view) {
Error(_) -> Nil
Ok(view) -> {
let buffer: Buffer = dict.new()
render_loop(layout, context, Section(root_section), buffer) render_loop(
|> plot.flush_buffer(layout.columns, layout.rows) view,
|> internal.update context,
Section(string.append("view_", string.inspect(layout.current_view))),
buffer,
)
|> plot.flush_buffer(layout.columns, layout.rows)
|> internal.update
}
}
} }
pub type RenderContext { pub type RenderContext {
@@ -125,12 +169,12 @@ pub type RenderContext {
} }
pub fn render_loop( pub fn render_loop(
layout: Layout, view: View,
context: RenderContext, context: RenderContext,
from: Section, from: Section,
buffer: Buffer, buffer: Buffer,
) -> Buffer { ) -> Buffer {
case dict.get(layout.nodes, from) { case dict.get(view, from) {
Error(_) -> buffer Error(_) -> buffer
Ok(node) -> { Ok(node) -> {
// Margin between container and the node being rendered // Margin between container and the node being rendered
@@ -193,7 +237,7 @@ pub fn render_loop(
position_index: i, position_index: i,
) )
render_loop(layout, context, child, acc_buffer) render_loop(view, context, child, acc_buffer)
}) })
} }
} }

View File

@@ -16,41 +16,43 @@ pub fn main() {
/// First row has two cells /// First row has two cells
/// Second row has no cells /// Second row has no cells
fn two_rows_with_cells(columns: Int, rows: Int) -> layout.Layout { fn two_rows_with_cells(columns: Int, rows: Int) -> layout.Layout {
let nodes = [ let views = [
#( [
Section("Row1"), #(
layout.Row( Section("Row1"),
content: "row 1", layout.Row(
style: Style(dimensions: Percent(width: 100, height: 50)), content: "row 1",
children: [ style: Style(dimensions: Percent(width: 100, height: 50)),
Section("A"), children: [
Section("B"), Section("A"),
], Section("B"),
],
),
), ),
), #(
#( Section("A"),
Section("A"), layout.Cell(
layout.Cell( content: "cell 1",
content: "cell 1", style: Style(dimensions: Percent(width: 50, height: 50)),
style: Style(dimensions: Percent(width: 50, height: 50)), ),
), ),
), #(
#( Section("B"),
Section("B"), layout.Cell(
layout.Cell( content: "cell 2",
content: "cell 2", style: Style(dimensions: Percent(width: 50, height: 50)),
style: Style(dimensions: Percent(width: 50, height: 50)), ),
), ),
), #(
#( Section("Row2"),
Section("Row2"), layout.Row(
layout.Row( content: "row 2",
content: "row 2", style: Style(dimensions: Percent(width: 50, height: 50)),
style: Style(dimensions: Percent(width: 50, height: 50)), children: [],
children: [], ),
), ),
), ],
] ]
layout.new(columns, rows, nodes) layout.new(columns, rows, views)
} }

View File

@@ -19,30 +19,41 @@ pub fn new() -> Result(Subject(Control), String) {
let layout = let layout =
[ [
#( [
layout.Header, #(
layout.Row( layout.Header,
content: "Foo (1) | Bar (2) | Baz (3)", layout.Row(
style: layout.Style(dimensions: layout.Percent(width: 100, height: 33)), content: "Foo (1) | Bar (2) | Baz (3)",
children: [], style: layout.Style(dimensions: layout.Percent(
width: 100,
height: 33,
)),
children: [],
),
), ),
), #(
#( layout.Search,
layout.Search, layout.Row(
layout.Row( content: "",
content: "", style: layout.Style(dimensions: layout.Percent(
style: layout.Style(dimensions: layout.Percent(width: 100, height: 33)), width: 100,
children: [], height: 33,
)),
children: [],
),
), ),
), #(
#( layout.PlaybackTime,
layout.PlaybackTime, layout.Row(
layout.Row( content: "00:00",
content: "00:00", style: layout.Style(dimensions: layout.Percent(
style: layout.Style(dimensions: layout.Percent(width: 100, height: 33)), width: 100,
children: [], height: 33,
)),
children: [],
),
), ),
), ],
] ]
|> layout.new(0, 0, _) |> layout.new(0, 0, _)

View File

@@ -12,45 +12,47 @@ pub fn main() -> Nil {
} }
pub fn percent_layout_test() { pub fn percent_layout_test() {
let nodes = [ let views = [
#( [
Section("Row1"), #(
layout.Row( Section("Row1"),
content: "row 1", layout.Row(
style: Style(dimensions: Percent(width: 100, height: 50)), content: "row 1",
children: [ style: Style(dimensions: Percent(width: 100, height: 50)),
Section("A"), children: [
Section("B"), Section("A"),
], Section("B"),
],
),
), ),
), #(
#( Section("A"),
Section("A"), layout.Cell(
layout.Cell( content: "cell 1",
content: "cell 1", style: Style(dimensions: Percent(width: 50, height: 100)),
style: Style(dimensions: Percent(width: 50, height: 100)), ),
), ),
), #(
#( Section("B"),
Section("B"), layout.Cell(
layout.Cell( content: "cell 2",
content: "cell 2", style: Style(dimensions: Percent(width: 50, height: 100)),
style: Style(dimensions: Percent(width: 50, height: 100)), ),
), ),
), #(
#( Section("Row2"),
Section("Row2"), layout.Row(
layout.Row( content: "row 1",
content: "row 1", style: Style(dimensions: Percent(width: 100, height: 50)),
style: Style(dimensions: Percent(width: 100, height: 50)), children: [],
children: [], ),
), ),
), ],
] ]
let columns = 80 let columns = 80
let rows = 20 let rows = 20
let layout = layout.new(columns, rows, nodes) let layout = layout.new(columns, rows, views)
let expected = let expected =
" "
@@ -87,11 +89,13 @@ pub fn percent_layout_test() {
position_index: 0, position_index: 0,
) )
let assert Ok(view) = dict.get(layout.views, layout.current_view)
let flushed = let flushed =
layout.render_loop( layout.render_loop(
layout, view,
context, context,
Section(layout.root_section), Section(string.append("view_", string.inspect(layout.current_view))),
dict.new(), dict.new(),
) )
|> plot.flush_buffer(layout.columns, layout.rows) |> plot.flush_buffer(layout.columns, layout.rows)