1
0
mirror of https://github.com/mum4k/termdash.git synced 2025-04-25 13:48:50 +08:00

Refactoring internal functions from align to internal/alignfor.

This commit is contained in:
Jakub Sobon 2019-02-24 15:44:13 -05:00
parent ffbf88caed
commit 0938ae91d8
No known key found for this signature in database
GPG Key ID: F2451A77FB05D3B7
12 changed files with 526 additions and 426 deletions

View File

@ -15,14 +15,6 @@
// Package align defines constants representing types of alignment.
package align
import (
"fmt"
"image"
"strings"
"github.com/mum4k/termdash/internal/runewidth"
)
// Horizontal indicates the type of horizontal alignment.
type Horizontal int
@ -76,98 +68,3 @@ const (
// VerticalBottom is bottom alignment along the vertical axis.
VerticalBottom
)
// hAlign aligns the given area in the rectangle horizontally.
func hAlign(rect image.Rectangle, ar image.Rectangle, h Horizontal) (image.Rectangle, error) {
gap := rect.Dx() - ar.Dx()
switch h {
case HorizontalRight:
// Use gap from above.
case HorizontalCenter:
gap /= 2
case HorizontalLeft:
gap = 0
default:
return image.ZR, fmt.Errorf("unsupported horizontal alignment %v", h)
}
return image.Rect(
rect.Min.X+gap,
ar.Min.Y,
rect.Min.X+gap+ar.Dx(),
ar.Max.Y,
), nil
}
// vAlign aligns the given area in the rectangle vertically.
func vAlign(rect image.Rectangle, ar image.Rectangle, v Vertical) (image.Rectangle, error) {
gap := rect.Dy() - ar.Dy()
switch v {
case VerticalBottom:
// Use gap from above.
case VerticalMiddle:
gap /= 2
case VerticalTop:
gap = 0
default:
return image.ZR, fmt.Errorf("unsupported vertical alignment %v", v)
}
return image.Rect(
ar.Min.X,
rect.Min.Y+gap,
ar.Max.X,
rect.Min.Y+gap+ar.Dy(),
), nil
}
// Rectangle aligns the area within the rectangle returning the
// aligned area. The area must fall within the rectangle.
func Rectangle(rect image.Rectangle, ar image.Rectangle, h Horizontal, v Vertical) (image.Rectangle, error) {
if !ar.In(rect) {
return image.ZR, fmt.Errorf("cannot align area %v inside rectangle %v, the area falls outside of the rectangle", ar, rect)
}
aligned, err := hAlign(rect, ar, h)
if err != nil {
return image.ZR, err
}
aligned, err = vAlign(rect, aligned, v)
if err != nil {
return image.ZR, err
}
return aligned, nil
}
// Text aligns the text within the given rectangle, returns the start point for the text.
// For the purposes of the alignment this assumes that text will be trimmed if
// it overruns the rectangle.
// This only supports a single line of text, the text must not contain newlines.
func Text(rect image.Rectangle, text string, h Horizontal, v Vertical) (image.Point, error) {
if strings.ContainsRune(text, '\n') {
return image.ZP, fmt.Errorf("the provided text contains a newline character: %q", text)
}
cells := runewidth.StringWidth(text)
var textLen int
if cells < rect.Dx() {
textLen = cells
} else {
textLen = rect.Dx()
}
textRect := image.Rect(
rect.Min.X,
rect.Min.Y,
// For the purposes of aligning the text, assume that it will be
// trimmed to the available space.
rect.Min.X+textLen,
rect.Min.Y+1,
)
aligned, err := Rectangle(rect, textRect, h, v)
if err != nil {
return image.ZP, err
}
return image.Point{aligned.Min.X, aligned.Min.Y}, nil
}

View File

@ -1,349 +1,76 @@
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package align
import (
"image"
"testing"
import "testing"
"github.com/kylelemons/godebug/pretty"
)
func TestRectangle(t *testing.T) {
func TestHorizontal(t *testing.T) {
tests := []struct {
desc string
rect image.Rectangle
area image.Rectangle
hAlign Horizontal
vAlign Vertical
want image.Rectangle
wantErr bool
desc string
align Horizontal
want string
}{
{
desc: "area falls outside of the rectangle",
rect: image.Rect(0, 0, 1, 1),
area: image.Rect(1, 1, 2, 2),
hAlign: HorizontalLeft,
vAlign: VerticalTop,
wantErr: true,
desc: "unknown",
align: Horizontal(-1),
want: "HorizontalUnknown",
},
{
desc: "unsupported horizontal alignment",
rect: image.Rect(0, 0, 2, 2),
area: image.Rect(0, 0, 1, 1),
hAlign: Horizontal(-1),
vAlign: VerticalTop,
wantErr: true,
desc: "left",
align: HorizontalLeft,
want: "HorizontalLeft",
},
{
desc: "unsupported vertical alignment",
rect: image.Rect(0, 0, 2, 2),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalLeft,
vAlign: Vertical(-1),
wantErr: true,
desc: "center",
align: HorizontalCenter,
want: "HorizontalCenter",
},
{
desc: "nothing to align if the rectangles are equal",
rect: image.Rect(0, 0, 2, 2),
area: image.Rect(0, 0, 2, 2),
hAlign: HorizontalLeft,
vAlign: VerticalTop,
want: image.Rect(0, 0, 2, 2),
},
{
desc: "aligns top and left, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalLeft,
vAlign: VerticalTop,
want: image.Rect(0, 0, 1, 1),
},
{
desc: "aligns top and center, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalCenter,
vAlign: VerticalTop,
want: image.Rect(1, 0, 2, 1),
},
{
desc: "aligns top and right, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalRight,
vAlign: VerticalTop,
want: image.Rect(2, 0, 3, 1),
},
{
desc: "aligns middle and left, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalLeft,
vAlign: VerticalMiddle,
want: image.Rect(0, 1, 1, 2),
},
{
desc: "aligns middle and center, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalCenter,
vAlign: VerticalMiddle,
want: image.Rect(1, 1, 2, 2),
},
{
desc: "aligns middle and right, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalRight,
vAlign: VerticalMiddle,
want: image.Rect(2, 1, 3, 2),
},
{
desc: "aligns bottom and left, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalLeft,
vAlign: VerticalBottom,
want: image.Rect(0, 2, 1, 3),
},
{
desc: "aligns bottom and center, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalCenter,
vAlign: VerticalBottom,
want: image.Rect(1, 2, 2, 3),
},
{
desc: "aligns bottom and right, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalRight,
vAlign: VerticalBottom,
want: image.Rect(2, 2, 3, 3),
},
{
desc: "aligns top and left, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: HorizontalLeft,
vAlign: VerticalTop,
want: image.Rect(0, 0, 1, 1),
},
{
desc: "aligns top and center, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: HorizontalCenter,
vAlign: VerticalTop,
want: image.Rect(1, 0, 2, 1),
},
{
desc: "aligns top and right, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: HorizontalRight,
vAlign: VerticalTop,
want: image.Rect(2, 0, 3, 1),
},
{
desc: "aligns middle and left, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: HorizontalLeft,
vAlign: VerticalMiddle,
want: image.Rect(0, 1, 1, 2),
},
{
desc: "aligns middle and center, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: HorizontalCenter,
vAlign: VerticalMiddle,
want: image.Rect(1, 1, 2, 2),
},
{
desc: "aligns middle and right, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: HorizontalRight,
vAlign: VerticalMiddle,
want: image.Rect(2, 1, 3, 2),
},
{
desc: "aligns bottom and left, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: HorizontalLeft,
vAlign: VerticalBottom,
want: image.Rect(0, 2, 1, 3),
},
{
desc: "aligns bottom and center, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: HorizontalCenter,
vAlign: VerticalBottom,
want: image.Rect(1, 2, 2, 3),
},
{
desc: "aligns bottom and right, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: HorizontalRight,
vAlign: VerticalBottom,
want: image.Rect(2, 2, 3, 3),
desc: "right",
align: HorizontalRight,
want: "HorizontalRight",
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
got, err := Rectangle(tc.rect, tc.area, tc.hAlign, tc.vAlign)
if (err != nil) != tc.wantErr {
t.Errorf("Rectangle => unexpected error: %v, wantErr: %v", err, tc.wantErr)
}
if err != nil {
return
}
if diff := pretty.Compare(tc.want, got); diff != "" {
t.Errorf("Rectangle => unexpected diff (-want, +got):\n%s", diff)
if got := tc.align.String(); got != tc.want {
t.Errorf("String => %q, want %q", got, tc.want)
}
})
}
}
func TestText(t *testing.T) {
func TestVertical(t *testing.T) {
tests := []struct {
desc string
rect image.Rectangle
text string
hAlign Horizontal
vAlign Vertical
want image.Point
wantErr bool
desc string
align Vertical
want string
}{
{
desc: "fails when text contains newline",
rect: image.Rect(0, 0, 3, 3),
text: "a\nb",
wantErr: true,
desc: "unknown",
align: Vertical(-1),
want: "VerticalUnknown",
},
{
desc: "aligns text top and left",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: HorizontalLeft,
vAlign: VerticalTop,
want: image.Point{1, 1},
desc: "top",
align: VerticalTop,
want: "VerticalTop",
},
{
desc: "aligns text top and center",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: HorizontalCenter,
vAlign: VerticalTop,
want: image.Point{2, 1},
desc: "middle",
align: VerticalMiddle,
want: "VerticalMiddle",
},
{
desc: "aligns text top and right",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: HorizontalRight,
vAlign: VerticalTop,
want: image.Point{3, 1},
},
{
desc: "aligns text middle and left",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: HorizontalLeft,
vAlign: VerticalMiddle,
want: image.Point{1, 2},
},
{
desc: "aligns half-width text rune middle and center",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: HorizontalCenter,
vAlign: VerticalMiddle,
want: image.Point{2, 2},
},
{
desc: "aligns full-width text rune middle and center",
rect: image.Rect(1, 1, 4, 4),
text: "界",
hAlign: HorizontalCenter,
vAlign: VerticalMiddle,
want: image.Point{1, 2},
},
{
desc: "aligns text middle and right",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: HorizontalRight,
vAlign: VerticalMiddle,
want: image.Point{3, 2},
},
{
desc: "aligns text bottom and left",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: HorizontalLeft,
vAlign: VerticalBottom,
want: image.Point{1, 3},
},
{
desc: "aligns text bottom and center",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: HorizontalCenter,
vAlign: VerticalBottom,
want: image.Point{2, 3},
},
{
desc: "aligns text bottom and right",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: HorizontalRight,
vAlign: VerticalBottom,
want: image.Point{3, 3},
},
{
desc: "aligns text that is too long, assumes trimming",
rect: image.Rect(1, 1, 4, 4),
text: "abcd",
hAlign: HorizontalCenter,
vAlign: VerticalTop,
want: image.Point{1, 1},
desc: "bottom",
align: VerticalBottom,
want: "VerticalBottom",
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
got, err := Text(tc.rect, tc.text, tc.hAlign, tc.vAlign)
if (err != nil) != tc.wantErr {
t.Errorf("Text => unexpected error: %v, wantErr: %v", err, tc.wantErr)
}
if err != nil {
return
}
if diff := pretty.Compare(tc.want, got); diff != "" {
t.Errorf("Text => unexpected diff (-want, +got):\n%s", diff)
if got := tc.align.String(); got != tc.want {
t.Errorf("String => %q, want %q", got, tc.want)
}
})
}

View File

@ -26,7 +26,7 @@ import (
"image"
"sync"
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/internal/alignfor"
"github.com/mum4k/termdash/internal/area"
"github.com/mum4k/termdash/internal/event"
"github.com/mum4k/termdash/internal/widgetapi"
@ -142,7 +142,7 @@ func (c *Container) widgetArea() (image.Rectangle, error) {
if wOpts.Ratio.X > 0 && wOpts.Ratio.Y > 0 {
adjusted = area.WithRatio(adjusted, wOpts.Ratio)
}
adjusted, err := align.Rectangle(c.usable(), adjusted, c.opts.hAlign, c.opts.vAlign)
adjusted, err := alignfor.Rectangle(c.usable(), adjusted, c.opts.hAlign, c.opts.vAlign)
if err != nil {
return image.ZR, err
}

120
internal/alignfor/align.go Normal file
View File

@ -0,0 +1,120 @@
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package alignfor provides functions that align elements.
package alignfor
import (
"fmt"
"image"
"strings"
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/internal/runewidth"
)
// hAlign aligns the given area in the rectangle horizontally.
func hAlign(rect image.Rectangle, ar image.Rectangle, h align.Horizontal) (image.Rectangle, error) {
gap := rect.Dx() - ar.Dx()
switch h {
case align.HorizontalRight:
// Use gap from above.
case align.HorizontalCenter:
gap /= 2
case align.HorizontalLeft:
gap = 0
default:
return image.ZR, fmt.Errorf("unsupported horizontal alignment %v", h)
}
return image.Rect(
rect.Min.X+gap,
ar.Min.Y,
rect.Min.X+gap+ar.Dx(),
ar.Max.Y,
), nil
}
// vAlign aligns the given area in the rectangle vertically.
func vAlign(rect image.Rectangle, ar image.Rectangle, v align.Vertical) (image.Rectangle, error) {
gap := rect.Dy() - ar.Dy()
switch v {
case align.VerticalBottom:
// Use gap from above.
case align.VerticalMiddle:
gap /= 2
case align.VerticalTop:
gap = 0
default:
return image.ZR, fmt.Errorf("unsupported vertical alignment %v", v)
}
return image.Rect(
ar.Min.X,
rect.Min.Y+gap,
ar.Max.X,
rect.Min.Y+gap+ar.Dy(),
), nil
}
// Rectangle aligns the area within the rectangle returning the
// aligned area. The area must fall within the rectangle.
func Rectangle(rect image.Rectangle, ar image.Rectangle, h align.Horizontal, v align.Vertical) (image.Rectangle, error) {
if !ar.In(rect) {
return image.ZR, fmt.Errorf("cannot align area %v inside rectangle %v, the area falls outside of the rectangle", ar, rect)
}
aligned, err := hAlign(rect, ar, h)
if err != nil {
return image.ZR, err
}
aligned, err = vAlign(rect, aligned, v)
if err != nil {
return image.ZR, err
}
return aligned, nil
}
// Text aligns the text within the given rectangle, returns the start point for the text.
// For the purposes of the alignment this assumes that text will be trimmed if
// it overruns the rectangle.
// This only supports a single line of text, the text must not contain newlines.
func Text(rect image.Rectangle, text string, h align.Horizontal, v align.Vertical) (image.Point, error) {
if strings.ContainsRune(text, '\n') {
return image.ZP, fmt.Errorf("the provided text contains a newline character: %q", text)
}
cells := runewidth.StringWidth(text)
var textLen int
if cells < rect.Dx() {
textLen = cells
} else {
textLen = rect.Dx()
}
textRect := image.Rect(
rect.Min.X,
rect.Min.Y,
// For the purposes of aligning the text, assume that it will be
// trimmed to the available space.
rect.Min.X+textLen,
rect.Min.Y+1,
)
aligned, err := Rectangle(rect, textRect, h, v)
if err != nil {
return image.ZP, err
}
return image.Point{aligned.Min.X, aligned.Min.Y}, nil
}

View File

@ -0,0 +1,351 @@
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package alignfor
import (
"image"
"testing"
"github.com/kylelemons/godebug/pretty"
"github.com/mum4k/termdash/align"
)
func TestRectangle(t *testing.T) {
tests := []struct {
desc string
rect image.Rectangle
area image.Rectangle
hAlign align.Horizontal
vAlign align.Vertical
want image.Rectangle
wantErr bool
}{
{
desc: "area falls outside of the rectangle",
rect: image.Rect(0, 0, 1, 1),
area: image.Rect(1, 1, 2, 2),
hAlign: align.HorizontalLeft,
vAlign: align.VerticalTop,
wantErr: true,
},
{
desc: "unsupported horizontal alignment",
rect: image.Rect(0, 0, 2, 2),
area: image.Rect(0, 0, 1, 1),
hAlign: align.Horizontal(-1),
vAlign: align.VerticalTop,
wantErr: true,
},
{
desc: "unsupported vertical alignment",
rect: image.Rect(0, 0, 2, 2),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalLeft,
vAlign: align.Vertical(-1),
wantErr: true,
},
{
desc: "nothing to align if the rectangles are equal",
rect: image.Rect(0, 0, 2, 2),
area: image.Rect(0, 0, 2, 2),
hAlign: align.HorizontalLeft,
vAlign: align.VerticalTop,
want: image.Rect(0, 0, 2, 2),
},
{
desc: "aligns top and left, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalLeft,
vAlign: align.VerticalTop,
want: image.Rect(0, 0, 1, 1),
},
{
desc: "aligns top and center, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalCenter,
vAlign: align.VerticalTop,
want: image.Rect(1, 0, 2, 1),
},
{
desc: "aligns top and right, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalRight,
vAlign: align.VerticalTop,
want: image.Rect(2, 0, 3, 1),
},
{
desc: "aligns middle and left, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalLeft,
vAlign: align.VerticalMiddle,
want: image.Rect(0, 1, 1, 2),
},
{
desc: "aligns middle and center, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalCenter,
vAlign: align.VerticalMiddle,
want: image.Rect(1, 1, 2, 2),
},
{
desc: "aligns middle and right, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalRight,
vAlign: align.VerticalMiddle,
want: image.Rect(2, 1, 3, 2),
},
{
desc: "aligns bottom and left, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalLeft,
vAlign: align.VerticalBottom,
want: image.Rect(0, 2, 1, 3),
},
{
desc: "aligns bottom and center, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalCenter,
vAlign: align.VerticalBottom,
want: image.Rect(1, 2, 2, 3),
},
{
desc: "aligns bottom and right, area is zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalRight,
vAlign: align.VerticalBottom,
want: image.Rect(2, 2, 3, 3),
},
{
desc: "aligns top and left, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(0, 0, 1, 1),
hAlign: align.HorizontalLeft,
vAlign: align.VerticalTop,
want: image.Rect(0, 0, 1, 1),
},
{
desc: "aligns top and center, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: align.HorizontalCenter,
vAlign: align.VerticalTop,
want: image.Rect(1, 0, 2, 1),
},
{
desc: "aligns top and right, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: align.HorizontalRight,
vAlign: align.VerticalTop,
want: image.Rect(2, 0, 3, 1),
},
{
desc: "aligns middle and left, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: align.HorizontalLeft,
vAlign: align.VerticalMiddle,
want: image.Rect(0, 1, 1, 2),
},
{
desc: "aligns middle and center, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: align.HorizontalCenter,
vAlign: align.VerticalMiddle,
want: image.Rect(1, 1, 2, 2),
},
{
desc: "aligns middle and right, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: align.HorizontalRight,
vAlign: align.VerticalMiddle,
want: image.Rect(2, 1, 3, 2),
},
{
desc: "aligns bottom and left, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: align.HorizontalLeft,
vAlign: align.VerticalBottom,
want: image.Rect(0, 2, 1, 3),
},
{
desc: "aligns bottom and center, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: align.HorizontalCenter,
vAlign: align.VerticalBottom,
want: image.Rect(1, 2, 2, 3),
},
{
desc: "aligns bottom and right, area isn't zero based",
rect: image.Rect(0, 0, 3, 3),
area: image.Rect(1, 1, 2, 2),
hAlign: align.HorizontalRight,
vAlign: align.VerticalBottom,
want: image.Rect(2, 2, 3, 3),
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
got, err := Rectangle(tc.rect, tc.area, tc.hAlign, tc.vAlign)
if (err != nil) != tc.wantErr {
t.Errorf("Rectangle => unexpected error: %v, wantErr: %v", err, tc.wantErr)
}
if err != nil {
return
}
if diff := pretty.Compare(tc.want, got); diff != "" {
t.Errorf("Rectangle => unexpected diff (-want, +got):\n%s", diff)
}
})
}
}
func TestText(t *testing.T) {
tests := []struct {
desc string
rect image.Rectangle
text string
hAlign align.Horizontal
vAlign align.Vertical
want image.Point
wantErr bool
}{
{
desc: "fails when text contains newline",
rect: image.Rect(0, 0, 3, 3),
text: "a\nb",
wantErr: true,
},
{
desc: "aligns text top and left",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: align.HorizontalLeft,
vAlign: align.VerticalTop,
want: image.Point{1, 1},
},
{
desc: "aligns text top and center",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: align.HorizontalCenter,
vAlign: align.VerticalTop,
want: image.Point{2, 1},
},
{
desc: "aligns text top and right",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: align.HorizontalRight,
vAlign: align.VerticalTop,
want: image.Point{3, 1},
},
{
desc: "aligns text middle and left",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: align.HorizontalLeft,
vAlign: align.VerticalMiddle,
want: image.Point{1, 2},
},
{
desc: "aligns half-width text rune middle and center",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: align.HorizontalCenter,
vAlign: align.VerticalMiddle,
want: image.Point{2, 2},
},
{
desc: "aligns full-width text rune middle and center",
rect: image.Rect(1, 1, 4, 4),
text: "界",
hAlign: align.HorizontalCenter,
vAlign: align.VerticalMiddle,
want: image.Point{1, 2},
},
{
desc: "aligns text middle and right",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: align.HorizontalRight,
vAlign: align.VerticalMiddle,
want: image.Point{3, 2},
},
{
desc: "aligns text bottom and left",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: align.HorizontalLeft,
vAlign: align.VerticalBottom,
want: image.Point{1, 3},
},
{
desc: "aligns text bottom and center",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: align.HorizontalCenter,
vAlign: align.VerticalBottom,
want: image.Point{2, 3},
},
{
desc: "aligns text bottom and right",
rect: image.Rect(1, 1, 4, 4),
text: "a",
hAlign: align.HorizontalRight,
vAlign: align.VerticalBottom,
want: image.Point{3, 3},
},
{
desc: "aligns text that is too long, assumes trimming",
rect: image.Rect(1, 1, 4, 4),
text: "abcd",
hAlign: align.HorizontalCenter,
vAlign: align.VerticalTop,
want: image.Point{1, 1},
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
got, err := Text(tc.rect, tc.text, tc.hAlign, tc.vAlign)
if (err != nil) != tc.wantErr {
t.Errorf("Text => unexpected error: %v, wantErr: %v", err, tc.wantErr)
}
if err != nil {
return
}
if diff := pretty.Compare(tc.want, got); diff != "" {
t.Errorf("Text => unexpected diff (-want, +got):\n%s", diff)
}
})
}
}

View File

@ -22,6 +22,7 @@ import (
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/cell"
"github.com/mum4k/termdash/internal/alignfor"
"github.com/mum4k/termdash/internal/canvas"
"github.com/mum4k/termdash/linestyle"
)
@ -120,7 +121,7 @@ func drawTitle(c *canvas.Canvas, border image.Rectangle, opt *borderOptions) err
border.Max.X-1, // One space for the top right corner char.
border.Min.Y+1,
)
start, err := align.Text(available, opt.title, opt.titleHAlign, align.VerticalTop)
start, err := alignfor.Text(available, opt.title, opt.titleHAlign, align.VerticalTop)
if err != nil {
return err
}

View File

@ -24,6 +24,7 @@ import (
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/cell"
"github.com/mum4k/termdash/internal/alignfor"
"github.com/mum4k/termdash/internal/area"
"github.com/mum4k/termdash/internal/canvas"
"github.com/mum4k/termdash/internal/draw"
@ -139,7 +140,7 @@ func (bc *BarChart) drawText(cvs *canvas.Canvas, i int, text string, color cell.
barCol = image.Rect(r.Min.X, cvs.Area().Min.Y, r.Max.X, cvs.Area().Max.Y)
}
start, err := align.Text(barCol, text, align.HorizontalCenter, align.VerticalBottom)
start, err := alignfor.Text(barCol, text, align.HorizontalCenter, align.VerticalBottom)
if err != nil {
return err
}

View File

@ -24,6 +24,7 @@ import (
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/cell"
"github.com/mum4k/termdash/internal/alignfor"
"github.com/mum4k/termdash/internal/button"
"github.com/mum4k/termdash/internal/canvas"
"github.com/mum4k/termdash/internal/draw"
@ -142,7 +143,7 @@ func (b *Button) Draw(cvs *canvas.Canvas) error {
}
textAr := image.Rect(buttonAr.Min.X+1, buttonAr.Min.Y, buttonAr.Dx()-1, buttonAr.Max.Y)
start, err := align.Text(textAr, b.text, align.HorizontalCenter, align.VerticalMiddle)
start, err := alignfor.Text(textAr, b.text, align.HorizontalCenter, align.VerticalMiddle)
if err != nil {
return err
}

View File

@ -23,6 +23,7 @@ import (
"sync"
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/internal/alignfor"
"github.com/mum4k/termdash/internal/canvas"
"github.com/mum4k/termdash/internal/canvas/braille"
"github.com/mum4k/termdash/internal/draw"
@ -173,9 +174,9 @@ func (d *Donut) drawText(cvs *canvas.Canvas, mid image.Point, holeR int) error {
}
ar := image.Rect(first.X, first.Y, first.X+cells+2, first.Y+1)
start, err := align.Text(ar, t, align.HorizontalCenter, align.VerticalMiddle)
start, err := alignfor.Text(ar, t, align.HorizontalCenter, align.VerticalMiddle)
if err != nil {
return fmt.Errorf("align.Text => %v", err)
return fmt.Errorf("alignfor.Text => %v", err)
}
if err := draw.Text(cvs, t, start, draw.TextMaxX(start.X+needCells), draw.TextCellOpts(d.opts.textCellOpts...)); err != nil {
return fmt.Errorf("draw.Text => %v", err)

View File

@ -22,8 +22,8 @@ import (
"image"
"sync"
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/cell"
"github.com/mum4k/termdash/internal/alignfor"
"github.com/mum4k/termdash/internal/area"
"github.com/mum4k/termdash/internal/canvas"
"github.com/mum4k/termdash/internal/draw"
@ -200,7 +200,7 @@ func (g *Gauge) drawText(cvs *canvas.Canvas, progress image.Rectangle) error {
return err
}
cur, err := align.Text(ar, trimmed, g.opts.hTextAlign, g.opts.vTextAlign)
cur, err := alignfor.Text(ar, trimmed, g.opts.hTextAlign, g.opts.vTextAlign)
if err != nil {
return err
}

View File

@ -21,6 +21,7 @@ import (
"image"
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/internal/alignfor"
)
// LabelOrientation represents the orientation of text labels.
@ -115,7 +116,7 @@ func rowLabel(scale *YScale, y int, labelWidth int) (*Label, error) {
}
ar := rowLabelArea(y, labelWidth)
pos, err := align.Text(ar, v.Text(), align.HorizontalRight, align.VerticalMiddle)
pos, err := alignfor.Text(ar, v.Text(), align.HorizontalRight, align.VerticalMiddle)
if err != nil {
return nil, fmt.Errorf("unable to align the label value: %v", err)
}

View File

@ -23,7 +23,7 @@ import (
"image"
"sync"
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/internal/alignfor"
"github.com/mum4k/termdash/internal/attrrange"
"github.com/mum4k/termdash/internal/canvas"
"github.com/mum4k/termdash/internal/segdisp/sixteen"
@ -188,9 +188,9 @@ func (sd *SegmentDisplay) Draw(cvs *canvas.Canvas) error {
}
text := sd.buff.String()
aligned, err := align.Rectangle(cvs.Area(), segAr.needArea(), sd.opts.hAlign, sd.opts.vAlign)
aligned, err := alignfor.Rectangle(cvs.Area(), segAr.needArea(), sd.opts.hAlign, sd.opts.vAlign)
if err != nil {
return fmt.Errorf("align.Rectangle => %v", err)
return fmt.Errorf("alignfor.Rectangle => %v", err)
}
optRange, err := sd.wOptsTracker.ForPosition(0) // Text options for the current byte.