mirror of
https://github.com/mum4k/termdash.git
synced 2025-04-25 13:48:50 +08:00
Button's support for cell options on each text cell.
This commit is contained in:
parent
73644716a5
commit
0643120697
@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
animation.
|
||||
- the `button` widget can now be drawn without horizontal padding around its
|
||||
text.
|
||||
- the `button`widget now allows specifying cell options for each cell of the
|
||||
displayed text.
|
||||
|
||||
## [0.13.0] - 17-Nov-2020
|
||||
|
||||
|
@ -18,7 +18,9 @@ package button
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -26,6 +28,7 @@ import (
|
||||
"github.com/mum4k/termdash/cell"
|
||||
"github.com/mum4k/termdash/mouse"
|
||||
"github.com/mum4k/termdash/private/alignfor"
|
||||
"github.com/mum4k/termdash/private/attrrange"
|
||||
"github.com/mum4k/termdash/private/button"
|
||||
"github.com/mum4k/termdash/private/canvas"
|
||||
"github.com/mum4k/termdash/private/draw"
|
||||
@ -45,6 +48,20 @@ import (
|
||||
// termdash.ErrorHandler.
|
||||
type CallbackFn func() error
|
||||
|
||||
// TextChunk is a part of or the full text displayed in the button.
|
||||
type TextChunk struct {
|
||||
text string
|
||||
wOpts *writeOptions
|
||||
}
|
||||
|
||||
// NewChunk creates a new text chunk. Each chunk of text can have its own cell options.
|
||||
func NewChunk(text string, wOpts ...WriteOption) *TextChunk {
|
||||
return &TextChunk{
|
||||
text: text,
|
||||
wOpts: newWriteOptions(wOpts...),
|
||||
}
|
||||
}
|
||||
|
||||
// Button can be pressed using a mouse click or a configured keyboard key.
|
||||
//
|
||||
// Upon each press, the button invokes a callback provided by the user.
|
||||
@ -52,7 +69,12 @@ type CallbackFn func() error
|
||||
// Implements widgetapi.Widget. This object is thread-safe.
|
||||
type Button struct {
|
||||
// text in the text label displayed in the button.
|
||||
text string
|
||||
text strings.Builder
|
||||
|
||||
// givenWOpts are write options given for the content of text.
|
||||
givenWOpts []*writeOptions
|
||||
// wOptsTracker tracks the positions in a text to which the givenWOpts apply.
|
||||
wOptsTracker *attrrange.Tracker
|
||||
|
||||
// mouseFSM tracks left mouse clicks.
|
||||
mouseFSM *button.FSM
|
||||
@ -78,22 +100,57 @@ type Button struct {
|
||||
// New returns a new Button that will display the provided text.
|
||||
// Each press of the button will invoke the callback function.
|
||||
func New(text string, cFn CallbackFn, opts ...Option) (*Button, error) {
|
||||
return NewFromChunks([]*TextChunk{NewChunk(text)}, cFn, opts...)
|
||||
}
|
||||
|
||||
// NewFromChunks is like New, but allows specifying write options for
|
||||
// individual chunks of text displayed in the button.
|
||||
func NewFromChunks(chunks []*TextChunk, cFn CallbackFn, opts ...Option) (*Button, error) {
|
||||
if cFn == nil {
|
||||
return nil, errors.New("the CallbackFn argument cannot be nil")
|
||||
}
|
||||
|
||||
opt := newOptions(text)
|
||||
if len(chunks) == 0 {
|
||||
return nil, errors.New("at least one text chunk must be specified")
|
||||
}
|
||||
|
||||
var (
|
||||
text strings.Builder
|
||||
givenWOpts []*writeOptions
|
||||
)
|
||||
wOptsTracker := attrrange.NewTracker()
|
||||
for i, tc := range chunks {
|
||||
if tc.text == "" {
|
||||
return nil, fmt.Errorf("text chunk[%d] is empty, all chunks must contains some text", i)
|
||||
}
|
||||
|
||||
pos := text.Len()
|
||||
givenWOpts = append(givenWOpts, tc.wOpts)
|
||||
wOptsIdx := len(givenWOpts) - 1
|
||||
if err := wOptsTracker.Add(pos, pos+len(tc.text), wOptsIdx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
text.WriteString(tc.text)
|
||||
}
|
||||
|
||||
opt := newOptions(text.String())
|
||||
for _, o := range opts {
|
||||
o.set(opt)
|
||||
}
|
||||
if err := opt.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, wOpts := range givenWOpts {
|
||||
wOpts.setDefaultFgColor(opt.textColor)
|
||||
}
|
||||
return &Button{
|
||||
text: text,
|
||||
mouseFSM: button.NewFSM(mouse.ButtonLeft, image.ZR),
|
||||
callback: cFn,
|
||||
opts: opt,
|
||||
text: text,
|
||||
givenWOpts: givenWOpts,
|
||||
wOptsTracker: wOptsTracker,
|
||||
mouseFSM: button.NewFSM(mouse.ButtonLeft, image.ZR),
|
||||
callback: cFn,
|
||||
opts: opt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -145,15 +202,40 @@ func (b *Button) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
|
||||
|
||||
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)
|
||||
start, err := alignfor.Text(textAr, b.text.String(), align.HorizontalCenter, align.VerticalMiddle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return draw.Text(cvs, b.text, start,
|
||||
draw.TextOverrunMode(draw.OverrunModeThreeDot),
|
||||
draw.TextMaxX(buttonAr.Max.X),
|
||||
draw.TextCellOpts(cell.FgColor(b.opts.textColor)),
|
||||
)
|
||||
|
||||
maxCells := buttonAr.Max.X - start.X
|
||||
trimmed, err := draw.TrimText(b.text.String(), maxCells, draw.OverrunModeThreeDot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
optRange, err := b.wOptsTracker.ForPosition(0) // Text options for the current byte.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cur := start
|
||||
for i, r := range trimmed {
|
||||
if i >= optRange.High { // Get the next write options.
|
||||
or, err := b.wOptsTracker.ForPosition(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
optRange = or
|
||||
}
|
||||
|
||||
wOpts := b.givenWOpts[optRange.AttrIdx]
|
||||
cells, err := cvs.SetCell(cur, r, wOpts.cellOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cur = image.Point{cur.X + cells, cur.Y}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// activated asserts whether the keyboard event activated the button.
|
||||
|
63
widgets/button/write_options.go
Normal file
63
widgets/button/write_options.go
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2020 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 button
|
||||
|
||||
// write_options.go contains options used when writing content to the widget.
|
||||
|
||||
import "github.com/mum4k/termdash/cell"
|
||||
|
||||
// WriteOption is used to provide options to NewFromChunks().
|
||||
type WriteOption interface {
|
||||
// set sets the provided option.
|
||||
set(*writeOptions)
|
||||
}
|
||||
|
||||
// writeOptions stores the provided options.
|
||||
type writeOptions struct {
|
||||
cellOpts []cell.Option
|
||||
}
|
||||
|
||||
// setDefaultFgColor configures a default color for text if one isn't specified
|
||||
// in the write options.
|
||||
func (wo *writeOptions) setDefaultFgColor(c cell.Color) {
|
||||
wo.cellOpts = append(
|
||||
[]cell.Option{cell.FgColor(c)},
|
||||
wo.cellOpts...,
|
||||
)
|
||||
}
|
||||
|
||||
// newWriteOptions returns new writeOptions instance.
|
||||
func newWriteOptions(wOpts ...WriteOption) *writeOptions {
|
||||
wo := &writeOptions{}
|
||||
for _, o := range wOpts {
|
||||
o.set(wo)
|
||||
}
|
||||
return wo
|
||||
}
|
||||
|
||||
// writeOption implements WriteOption.
|
||||
type writeOption func(*writeOptions)
|
||||
|
||||
// set implements WriteOption.set.
|
||||
func (wo writeOption) set(wOpts *writeOptions) {
|
||||
wo(wOpts)
|
||||
}
|
||||
|
||||
// WriteCellOpts sets options on the cells that contain the text.
|
||||
func WriteCellOpts(opts ...cell.Option) WriteOption {
|
||||
return writeOption(func(wOpts *writeOptions) {
|
||||
wOpts.cellOpts = opts
|
||||
})
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user