From d31b767d5d3925daf2b2ec8ae913edfffa592f02 Mon Sep 17 00:00:00 2001 From: Jakub Sobon Date: Sun, 7 Apr 2019 16:58:18 -0400 Subject: [PATCH] Allow options on intermediate containers in the grid. Fixes #181. --- CHANGELOG.md | 3 ++ container/grid/grid.go | 35 ++++++++++++++-- container/grid/grid_test.go | 78 ++++++++++++++++++++++++++++++++++++ termdashdemo/termdashdemo.go | 20 ++++----- 4 files changed, 123 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20f14fa..ddad9c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 executed. - The SegmentDisplay widget now has a method that returns the observed character capacity the last time Draw was called. +- The grid.Builder API now allows users to specify options for intermediate + containers, i.e. containers that don't have widgets, but represent rows and + columns. #### Breaking API changes diff --git a/container/grid/grid.go b/container/grid/grid.go index 9a59c0c..633de15 100644 --- a/container/grid/grid.go +++ b/container/grid/grid.go @@ -109,18 +109,19 @@ func build(elems []Element, parentHeightPerc, parentWidthPerc int) []container.O switch e := elem.(type) { case *row: + if len(elems) > 0 { perc := innerPerc(e.heightPerc, parentHeightPerc) childHeightPerc := parentHeightPerc - e.heightPerc return []container.Option{ container.SplitHorizontal( - container.Top(build(e.subElem, 100, parentWidthPerc)...), + container.Top(append(e.cOpts, build(e.subElem, 100, parentWidthPerc)...)...), container.Bottom(build(elems, childHeightPerc, parentWidthPerc)...), container.SplitPercent(perc), ), } } - return build(e.subElem, 100, parentWidthPerc) + return append(e.cOpts, build(e.subElem, 100, parentWidthPerc)...) case *col: if len(elems) > 0 { @@ -128,13 +129,13 @@ func build(elems []Element, parentHeightPerc, parentWidthPerc int) []container.O childWidthPerc := parentWidthPerc - e.widthPerc return []container.Option{ container.SplitVertical( - container.Left(build(e.subElem, parentHeightPerc, 100)...), + container.Left(append(e.cOpts, build(e.subElem, parentHeightPerc, 100)...)...), container.Right(build(elems, parentHeightPerc, childWidthPerc)...), container.SplitPercent(perc), ), } } - return build(e.subElem, parentHeightPerc, 100) + return append(e.cOpts, build(e.subElem, parentHeightPerc, 100)...) case *widget: opts := e.cOpts @@ -186,6 +187,9 @@ type row struct { // subElem are the sub Rows or Columns or a single widget. subElem []Element + + // cOpts are the options for the row's container. + cOpts []container.Option } // isElement implements Element.isElement. @@ -204,6 +208,9 @@ type col struct { // subElem are the sub Rows or Columns or a single widget. subElem []Element + + // cOpts are the options for the column's container. + cOpts []container.Option } // isElement implements Element.isElement. @@ -244,6 +251,16 @@ func RowHeightPerc(heightPerc int, subElements ...Element) Element { } } +// RowHeightPercWithOpts is like RowHeightPerc, but also allows to apply +// additional options to the container that represents the row. +func RowHeightPercWithOpts(heightPerc int, cOpts []container.Option, subElements ...Element) Element { + return &row{ + heightPerc: heightPerc, + subElem: subElements, + cOpts: cOpts, + } +} + // ColWidthPerc creates a column of the specified width. // The width is supplied as width percentage of the parent element. // The sum of all widths at the same level cannot be larger than 100%. If it @@ -257,6 +274,16 @@ func ColWidthPerc(widthPerc int, subElements ...Element) Element { } } +// ColWidthPercWithOpts is like ColWidthPerc, but also allows to apply +// additional options to the container that represents the column. +func ColWidthPercWithOpts(widthPerc int, cOpts []container.Option, subElements ...Element) Element { + return &col{ + widthPerc: widthPerc, + subElem: subElements, + cOpts: cOpts, + } +} + // Widget adds a widget into the Row or Column. // The options will be applied to the container that directly holds this // widget. diff --git a/container/grid/grid_test.go b/container/grid/grid_test.go index 9ab155a..01f6b29 100644 --- a/container/grid/grid_test.go +++ b/container/grid/grid_test.go @@ -389,6 +389,45 @@ func TestBuilder(t *testing.T) { return ft }, }, + { + desc: "two equal rows with options", + termSize: image.Point{10, 10}, + builder: func() *Builder { + b := New() + b.Add(RowHeightPercWithOpts( + 50, + []container.Option{ + container.Border(linestyle.Double), + }, + Widget(mirror()), + )) + b.Add(RowHeightPercWithOpts( + 50, + []container.Option{ + container.Border(linestyle.Double), + }, + Widget(mirror()), + )) + return b + }(), + want: func(size image.Point) *faketerm.Terminal { + ft := faketerm.MustNew(size) + + top, bot := mustHSplit(ft.Area(), 50) + topCvs := testcanvas.MustNew(top) + botCvs := testcanvas.MustNew(bot) + testdraw.MustBorder(topCvs, topCvs.Area(), draw.BorderLineStyle(linestyle.Double)) + testdraw.MustBorder(botCvs, botCvs.Area(), draw.BorderLineStyle(linestyle.Double)) + testcanvas.MustApply(topCvs, ft) + testcanvas.MustApply(botCvs, ft) + + topWidget := testcanvas.MustNew(area.ExcludeBorder(top)) + botWidget := testcanvas.MustNew(area.ExcludeBorder(bot)) + fakewidget.MustDraw(ft, topWidget, &widgetapi.Meta{}, widgetapi.Options{}) + fakewidget.MustDraw(ft, botWidget, &widgetapi.Meta{}, widgetapi.Options{}) + return ft + }, + }, { desc: "two unequal rows", termSize: image.Point{10, 10}, @@ -406,6 +445,45 @@ func TestBuilder(t *testing.T) { return ft }, }, + { + desc: "two equal columns with options", + termSize: image.Point{20, 10}, + builder: func() *Builder { + b := New() + b.Add(ColWidthPercWithOpts( + 50, + []container.Option{ + container.Border(linestyle.Double), + }, + Widget(mirror()), + )) + b.Add(ColWidthPercWithOpts( + 50, + []container.Option{ + container.Border(linestyle.Double), + }, + Widget(mirror()), + )) + return b + }(), + want: func(size image.Point) *faketerm.Terminal { + ft := faketerm.MustNew(size) + + left, right := mustVSplit(ft.Area(), 50) + leftCvs := testcanvas.MustNew(left) + rightCvs := testcanvas.MustNew(right) + testdraw.MustBorder(leftCvs, leftCvs.Area(), draw.BorderLineStyle(linestyle.Double)) + testdraw.MustBorder(rightCvs, rightCvs.Area(), draw.BorderLineStyle(linestyle.Double)) + testcanvas.MustApply(leftCvs, ft) + testcanvas.MustApply(rightCvs, ft) + + leftWidget := testcanvas.MustNew(area.ExcludeBorder(left)) + rightWidget := testcanvas.MustNew(area.ExcludeBorder(right)) + fakewidget.MustDraw(ft, leftWidget, &widgetapi.Meta{}, widgetapi.Options{}) + fakewidget.MustDraw(ft, rightWidget, &widgetapi.Meta{}, widgetapi.Options{}) + return ft + }, + }, { desc: "two equal columns", termSize: image.Point{20, 10}, diff --git a/termdashdemo/termdashdemo.go b/termdashdemo/termdashdemo.go index 6601b59..768057f 100644 --- a/termdashdemo/termdashdemo.go +++ b/termdashdemo/termdashdemo.go @@ -166,16 +166,18 @@ func gridLayout(w *widgets, lt layoutType) ([]container.Option, error) { container.BorderTitle("A rolling text"), ), ), - grid.RowHeightPerc(50, - grid.Widget(w.spGreen, - container.Border(linestyle.Light), - container.BorderTitle("Green SparkLine"), + grid.ColWidthPerc(50, + grid.RowHeightPerc(50, + grid.Widget(w.spGreen, + container.Border(linestyle.Light), + container.BorderTitle("Green SparkLine"), + ), ), - ), - grid.RowHeightPerc(50, - grid.Widget(w.spRed, - container.Border(linestyle.Light), - container.BorderTitle("Red SparkLine"), + grid.RowHeightPerc(50, + grid.Widget(w.spRed, + container.Border(linestyle.Light), + container.BorderTitle("Red SparkLine"), + ), ), ), ),