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:
parent
ffbf88caed
commit
0938ae91d8
103
align/align.go
103
align/align.go
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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
120
internal/alignfor/align.go
Normal 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
|
||||
}
|
351
internal/alignfor/align_test.go
Normal file
351
internal/alignfor/align_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user