Skip to content

Commit

Permalink
Adding canvas function that sets area of cells.
Browse files Browse the repository at this point in the history
  • Loading branch information
mum4k committed Feb 23, 2019
1 parent 0490734 commit 417eee0
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 0 deletions.
27 changes: 27 additions & 0 deletions canvas/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/mum4k/termdash/area"
"github.com/mum4k/termdash/cell"
"github.com/mum4k/termdash/cell/runewidth"
"github.com/mum4k/termdash/terminalapi"
)

Expand Down Expand Up @@ -117,6 +118,32 @@ func (c *Canvas) SetCellOpts(p image.Point, opts ...cell.Option) error {
return nil
}

// SetAreaCells is like SetCell, but sets the specified rune and options on all
// the cells within the provided area.
// This method is idempotent.
func (c *Canvas) SetAreaCells(cellArea image.Rectangle, r rune, opts ...cell.Option) error {
haveArea := c.Area()
if !cellArea.In(haveArea) {
return fmt.Errorf("unable to set cell runes in area %v, it must fit inside the available cell area is %v", cellArea, haveArea)
}

rw := runewidth.RuneWidth(r)
for row := cellArea.Min.Y; row < cellArea.Max.Y; row++ {
for col := cellArea.Min.X; col < cellArea.Max.X; {
p := image.Point{col, row}
if col+rw > cellArea.Max.X {
break
}
cells, err := c.SetCell(p, r, opts...)
if err != nil {
return err
}
col += cells
}
}
return nil
}

// SetAreaCellOpts is like SetCellOpts, but sets the specified options on all
// the cells within the provided area.
func (c *Canvas) SetAreaCellOpts(cellArea image.Rectangle, opts ...cell.Option) error {
Expand Down
167 changes: 167 additions & 0 deletions canvas/canvas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,173 @@ func TestCanvas(t *testing.T) {
}
}

if err := cvs.Apply(ft); err != nil {
return nil, err
}
return ft, nil
},
},
{
desc: "SetAreaCells sets cells in the full canvas",
canvas: image.Rect(0, 0, 1, 1),
ops: func(cvs *Canvas) error {
return cvs.SetAreaCells(image.Rect(0, 0, 1, 1), 'r')
},
want: func(size image.Point) (*faketerm.Terminal, error) {
ft := faketerm.MustNew(size)
cvs, err := New(ft.Area())
if err != nil {
return nil, err
}

if _, err := cvs.SetCell(image.Point{0, 0}, 'r'); err != nil {
return nil, err
}

if err := cvs.Apply(ft); err != nil {
return nil, err
}
return ft, nil
},
},
{
desc: "SetAreaCells is idempotent",
canvas: image.Rect(0, 0, 1, 1),
ops: func(cvs *Canvas) error {
if err := cvs.SetAreaCells(image.Rect(0, 0, 1, 1), 'r'); err != nil {
return err
}
return cvs.SetAreaCells(image.Rect(0, 0, 1, 1), 'r')
},
want: func(size image.Point) (*faketerm.Terminal, error) {
ft := faketerm.MustNew(size)
cvs, err := New(ft.Area())
if err != nil {
return nil, err
}

if _, err := cvs.SetCell(image.Point{0, 0}, 'r'); err != nil {
return nil, err
}

if err := cvs.Apply(ft); err != nil {
return nil, err
}
return ft, nil
},
},
{
desc: "SetAreaCells fails on area too large",
canvas: image.Rect(0, 0, 1, 1),
ops: func(cvs *Canvas) error {
return cvs.SetAreaCells(image.Rect(0, 0, 2, 2), 'r', cell.FgColor(cell.ColorRed), cell.BgColor(cell.ColorBlue))
},
wantErr: true,
},
{
desc: "SetAreaCells sets cell options",
canvas: image.Rect(0, 0, 1, 1),
ops: func(cvs *Canvas) error {
return cvs.SetAreaCells(image.Rect(0, 0, 1, 1), 'r', cell.FgColor(cell.ColorRed), cell.BgColor(cell.ColorBlue))
},
want: func(size image.Point) (*faketerm.Terminal, error) {
ft := faketerm.MustNew(size)
cvs, err := New(ft.Area())
if err != nil {
return nil, err
}

if _, err := cvs.SetCell(image.Point{0, 0}, 'r', cell.FgColor(cell.ColorRed), cell.BgColor(cell.ColorBlue)); err != nil {
return nil, err
}

if err := cvs.Apply(ft); err != nil {
return nil, err
}
return ft, nil
},
},
{
desc: "SetAreaCells sets cell in a sub-area",
canvas: image.Rect(0, 0, 3, 3),
ops: func(cvs *Canvas) error {
return cvs.SetAreaCells(image.Rect(0, 0, 2, 2), 'p')
},
want: func(size image.Point) (*faketerm.Terminal, error) {
ft := faketerm.MustNew(size)
cvs, err := New(ft.Area())
if err != nil {
return nil, err
}

for _, p := range []image.Point{
{0, 0},
{0, 1},
{1, 0},
{1, 1},
} {
if _, err := cvs.SetCell(p, 'p'); err != nil {
return nil, err
}
}

if err := cvs.Apply(ft); err != nil {
return nil, err
}
return ft, nil
},
},
{
desc: "SetAreaCells sets full-width runes that fit",
canvas: image.Rect(0, 0, 3, 3),
ops: func(cvs *Canvas) error {
return cvs.SetAreaCells(image.Rect(0, 0, 2, 2), '世')
},
want: func(size image.Point) (*faketerm.Terminal, error) {
ft := faketerm.MustNew(size)
cvs, err := New(ft.Area())
if err != nil {
return nil, err
}

for _, p := range []image.Point{
{0, 0},
{0, 1},
} {
if _, err := cvs.SetCell(p, '世'); err != nil {
return nil, err
}
}

if err := cvs.Apply(ft); err != nil {
return nil, err
}
return ft, nil
},
},
{
desc: "SetAreaCells sets full-width runes that will leave a gap at the end of each row",
canvas: image.Rect(0, 0, 3, 3),
ops: func(cvs *Canvas) error {
return cvs.SetAreaCells(image.Rect(0, 0, 3, 3), '世')
},
want: func(size image.Point) (*faketerm.Terminal, error) {
ft := faketerm.MustNew(size)
cvs, err := New(ft.Area())
if err != nil {
return nil, err
}

for _, p := range []image.Point{
{0, 0},
{0, 1},
{0, 2},
} {
if _, err := cvs.SetCell(p, '世'); err != nil {
return nil, err
}
}

if err := cvs.Apply(ft); err != nil {
return nil, err
}
Expand Down

0 comments on commit 417eee0

Please sign in to comment.