Button can be drawn without horizontal padding around its text.

This commit is contained in:
Jakub Sobon 2020-11-25 01:44:50 -05:00
parent ce3adfb084
commit 73644716a5
No known key found for this signature in database
GPG Key ID: F2451A77FB05D3B7
5 changed files with 81 additions and 21 deletions

View File

@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- the `button` widget allows users to specify multiple trigger keys.
- the `button` widget can now be drawn without the shadow and the pressing the
animation.
- the `button` widget can now be drawn without horizontal padding around its
text.
## [0.13.0] - 17-Nov-2020

View File

@ -143,7 +143,8 @@ func (b *Button) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
return err
}
textAr := image.Rect(buttonAr.Min.X+1, buttonAr.Min.Y, buttonAr.Dx()-1, buttonAr.Max.Y)
pad := b.opts.textHorizontalPadding
textAr := image.Rect(buttonAr.Min.X+pad, buttonAr.Min.Y, buttonAr.Dx()-pad, buttonAr.Max.Y)
start, err := alignfor.Text(textAr, b.text, align.HorizontalCenter, align.VerticalMiddle)
if err != nil {
return err
@ -223,7 +224,7 @@ func (b *Button) shadowWidth() int {
func (b *Button) Options() widgetapi.Options {
// No need to lock, as the height and width get fixed when New is called.
width := b.opts.width + b.shadowWidth()
width := b.opts.width + b.shadowWidth() + 2*b.opts.textHorizontalPadding
height := b.opts.height + b.shadowWidth()
return widgetapi.Options{
MinimumSize: image.Point{width, height},

View File

@ -115,6 +115,15 @@ func TestButton(t *testing.T) {
canvas: image.Rect(0, 0, 1, 1),
wantNewErr: true,
},
{
desc: "New fails with negative textHorizontalPadding",
callback: &callbackTracker{},
opts: []Option{
TextHorizontalPadding(-1),
},
canvas: image.Rect(0, 0, 1, 1),
wantNewErr: true,
},
{
desc: "draw fails on canvas too small",
callback: &callbackTracker{},
@ -934,11 +943,25 @@ func TestOptions(t *testing.T) {
},
},
{
desc: "custom width specified",
desc: "custom width specified with default padding",
text: "hello",
opts: []Option{
Width(10),
},
want: widgetapi.Options{
MinimumSize: image.Point{13, 4},
MaximumSize: image.Point{13, 4},
WantKeyboard: widgetapi.KeyScopeNone,
WantMouse: widgetapi.MouseScopeGlobal,
},
},
{
desc: "custom width specified with custom padding",
text: "hello",
opts: []Option{
Width(10),
TextHorizontalPadding(0),
},
want: widgetapi.Options{
MinimumSize: image.Point{11, 4},
MaximumSize: image.Point{11, 4},
@ -946,7 +969,21 @@ func TestOptions(t *testing.T) {
WantMouse: widgetapi.MouseScopeGlobal,
},
},
{
desc: "without shadow or padding",
text: "hello",
opts: []Option{
Width(10),
TextHorizontalPadding(0),
DisableShadow(),
},
want: widgetapi.Options{
MinimumSize: image.Point{10, 3},
MaximumSize: image.Point{10, 3},
WantKeyboard: widgetapi.KeyScopeNone,
WantMouse: widgetapi.MouseScopeGlobal,
},
},
{
desc: "doesn't want keyboard by default",
text: "hello",

View File

@ -60,6 +60,7 @@ func main() {
button.GlobalKeys('s', 'S'),
button.DisableShadow(),
button.Height(1),
button.TextHorizontalPadding(0),
)
if err != nil {
panic(err)
@ -75,6 +76,7 @@ func main() {
button.GlobalKeys('c', 'C'),
button.DisableShadow(),
button.Height(1),
button.TextHorizontalPadding(0),
)
if err != nil {
panic(err)

View File

@ -42,19 +42,23 @@ func (o option) set(opts *options) {
// options holds the provided options.
type options struct {
fillColor cell.Color
textColor cell.Color
shadowColor cell.Color
disableShadow bool
height int
width int
keys map[keyboard.Key]bool
keyScope widgetapi.KeyScope
keyUpDelay time.Duration
fillColor cell.Color
textColor cell.Color
textHorizontalPadding int
shadowColor cell.Color
disableShadow bool
height int
width int
keys map[keyboard.Key]bool
keyScope widgetapi.KeyScope
keyUpDelay time.Duration
}
// validate validates the provided options.
func (o *options) validate() error {
if min := 0; o.textHorizontalPadding < min {
return fmt.Errorf("invalid textHorizontalPadding %d, must be %d <= textHorizontalPadding", o.textHorizontalPadding, min)
}
if min := 1; o.height < min {
return fmt.Errorf("invalid height %d, must be %d <= height", o.height, min)
}
@ -76,13 +80,14 @@ type keyScope struct {
// newOptions returns options with the default values set.
func newOptions(text string) *options {
return &options{
fillColor: cell.ColorNumber(117),
textColor: cell.ColorBlack,
shadowColor: cell.ColorNumber(240),
height: DefaultHeight,
width: widthFor(text),
keyUpDelay: DefaultKeyUpDelay,
keys: map[keyboard.Key]bool{},
fillColor: cell.ColorNumber(117),
textColor: cell.ColorBlack,
textHorizontalPadding: DefaultTextHorizontalPadding,
shadowColor: cell.ColorNumber(240),
height: DefaultHeight,
width: widthFor(text),
keyUpDelay: DefaultKeyUpDelay,
keys: map[keyboard.Key]bool{},
}
}
@ -122,6 +127,8 @@ func Height(cells int) Option {
// Width sets the width of the button in cells.
// Must be a positive non-zero integer.
// Defaults to the auto-width based on the length of the text label.
// Not all the width may be available to the text if TextHorizontalPadding is
// set to a non-zero integer.
func Width(cells int) Option {
return option(func(opts *options) {
opts.width = cells
@ -211,7 +218,18 @@ func DisableShadow() Option {
})
}
// DefaultTextHorizontalPadding is the default value for the HorizontalPadding option.
const DefaultTextHorizontalPadding = 1
// TextHorizontalPadding sets padding on the left and right side of the
// button's text as the amount of cells.
func TextHorizontalPadding(p int) Option {
return option(func(opts *options) {
opts.textHorizontalPadding = p
})
}
// widthFor returns the required width for the specified text.
func widthFor(text string) int {
return runewidth.StringWidth(text) + 2 // One empty cell at each side.
return runewidth.StringWidth(text)
}