From a6f958ccfbebc047e24d8a653053bca6e6b98670 Mon Sep 17 00:00:00 2001 From: David Ashby Date: Sun, 4 Jul 2021 13:49:53 -0400 Subject: [PATCH] support ratio layouts --- ui/ui.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++---- ui/ui_test.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 4 deletions(-) diff --git a/ui/ui.go b/ui/ui.go index 40849fe..e1fc202 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -12,10 +12,11 @@ type Drawable interface { } type Offsets struct { - Top int - Bottom int - Left int - Right int + Top int + Bottom int + Left int + Right int + Percent int } type Contents []struct { @@ -27,6 +28,8 @@ const ( LayoutUnmanaged = iota LayoutHorizontalEven LayoutVerticalEven + LayoutHorizontalPercent + LayoutVerticalPercent ) // A Container has no visible UI of its own, but arranges sub-components on the screen. @@ -87,6 +90,58 @@ func (c *Container) SetSize(x, y, h, w int) { } c.contents[r].Container.SetSize(x, y, h, w) } + } else if c.layoutMethod == LayoutHorizontalPercent { + // first, work out overall distribution + total := 0 + for r := range c.contents { + // `0` or negatives are set as minimum + if c.contents[r].Offsets.Percent < 1 { + total += 1 + } else { + total += c.contents[r].Offsets.Percent + } + } + carry := 0 + // push around containers + for r := range c.contents { + ratio := (float64(c.contents[r].Offsets.Percent) / float64(total)) + w := int(float64(c.w) * ratio) + h := c.h + x := c.x + carry + y := c.y + carry += w + // and add any remaining space to the last container + if r == len(c.contents)-1 { + w += (c.w - carry) + } + c.contents[r].Container.SetSize(x, y, h, w) + } + } else if c.layoutMethod == LayoutVerticalPercent { + // first, work out overall distribution + total := 0 + for r := range c.contents { + // `0` or negatives are set as minimum + if c.contents[r].Offsets.Percent < 1 { + total += 1 + } else { + total += c.contents[r].Offsets.Percent + } + } + carry := 0 + // push around containers + for r := range c.contents { + ratio := (float64(c.contents[r].Offsets.Percent) / float64(total)) + w := c.w + h := int(float64(c.h) * ratio) + x := c.x + y := c.y + carry + carry += h + // and add any remaining space to the last container + if r == len(c.contents)-1 { + h += (c.h - carry) + } + c.contents[r].Container.SetSize(x, y, h, w) + } } else { for r := range c.contents { x := c.x + c.contents[r].Offsets.Left diff --git a/ui/ui_test.go b/ui/ui_test.go index 90982bf..caabc32 100644 --- a/ui/ui_test.go +++ b/ui/ui_test.go @@ -55,6 +55,7 @@ func TestContainerTwoBoxesHStack(t *testing.T) { t.Fail() } } + func TestContainerThreeBoxesUnevenHStack(t *testing.T) { expect := `┌─ one ──┐┌─ two ──┐┌─ three │ ││ ││ │ @@ -82,6 +83,34 @@ func TestContainerThreeBoxesUnevenHStack(t *testing.T) { } } +func TestContainerTwoBoxesHPercentStack(t *testing.T) { + expect := `┌─ one ──────┐┌─ two ┐ +│ ││ │ +│ ││ │ +│ ││ │ +└────────────┘└──────┘ +` + m := &MockScreen{} + one := NewBox("one", nil, Contents{}) + two := NewBox("two", nil, Contents{}) + container := NewContainer( + Contents{ + {Container: one, Offsets: Offsets{Percent: 2}}, + {Container: two, Offsets: Offsets{Percent: 1}}}, + LayoutHorizontalPercent, + ) + m.Init() + m.Resize(0, 0, 5, 22) + container.SetSize(0, 0, 5, 22) + container.Draw(m) + result := m.DumpContents() + if result != expect { + fmt.Printf("expected:\n%+v", expect) + fmt.Printf("actual:\n%+v", result) + t.Fail() + } +} + func TestContainerTwoBoxesVStack(t *testing.T) { expect := `┌─ one ──┐ │ │ @@ -112,3 +141,36 @@ func TestContainerTwoBoxesVStack(t *testing.T) { t.Fail() } } + +func TestContainerTwoBoxesPercentageVStack(t *testing.T) { + expect := `┌─ one ──┐ +│ │ +│ │ +│ │ +│ │ +└────────┘ +┌─ two ──┐ +│ │ +│ │ +└────────┘ +` + m := &MockScreen{} + one := NewBox("one", nil, Contents{}) + two := NewBox("two", nil, Contents{}) + container := NewContainer( + Contents{ + {Container: one, Offsets: Offsets{Percent: 2}}, + {Container: two, Offsets: Offsets{Percent: 1}}}, + LayoutVerticalPercent, + ) + m.Init() + m.Resize(0, 0, 10, 10) + container.SetSize(0, 0, 10, 10) + container.Draw(m) + result := m.DumpContents() + if result != expect { + fmt.Printf("expected:\n%+v", expect) + fmt.Printf("actual:\n%+v", result) + t.Fail() + } +}