Skip to content

Commit

Permalink
A simple number "spinner" widget
Browse files Browse the repository at this point in the history
This widget renders something like "15 [^v]". If you click the "^"
button, the value increases by one; or "v" will decrease the value by
one. I haven't tried to make this sophisticated at all, so there aren't
configurable keys yet, or increments/decrements by values other than
one. If needed, I'll add them later. I am going to use this to all
configuring the custom columns' occurrences field.
  • Loading branch information
gcla committed Feb 22, 2021
1 parent f2ad329 commit 5137bd3
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 0 deletions.
141 changes: 141 additions & 0 deletions widgets/number/number.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2019-2021 Graham Clark. All rights reserved. Use of this source
// code is governed by the MIT license that can be found in the LICENSE
// file.

// Package hexdumper provides a numeric widget with a couple of buttons that increase or decrease its value.
package number

import (
"fmt"

"github.com/gcla/gowid"
"github.com/gcla/gowid/gwutil"
"github.com/gcla/gowid/widgets/button"
"github.com/gcla/gowid/widgets/columns"
"github.com/gcla/gowid/widgets/fill"
"github.com/gcla/gowid/widgets/holder"
"github.com/gcla/gowid/widgets/hpadding"
"github.com/gcla/gowid/widgets/text"
)

//======================================================================

type Options struct {
Value int
Max gwutil.IntOption
Min gwutil.IntOption
Styler func(gowid.IWidget) gowid.IWidget
}

type Widget struct {
gowid.IWidget
up *button.Widget
down *button.Widget
valHolder *holder.Widget
Value int
Opt Options
}

var _ gowid.IWidget = (*Widget)(nil)

var blank *hpadding.Widget
var upArrow *text.Widget
var downArrow *text.Widget
var leftbr *text.Widget
var rightbr *text.Widget

func init() {
blank = hpadding.New(
fill.New(' '),
gowid.HAlignLeft{},
gowid.RenderWithUnits{U: 1},
)

leftbr = text.New("[")
rightbr = text.New("]")
upArrow = text.New("^")
downArrow = text.New("v")
}

func New(opts ...Options) *Widget {

var opt Options
if len(opts) > 0 {
opt = opts[0]
}

res := &Widget{
valHolder: holder.New(text.New(fmt.Sprintf("%d", opt.Value))),
Value: opt.Value,
}

up := button.NewBare(upArrow)

up.OnClick(gowid.MakeWidgetCallback("cb", func(app gowid.IApp, widget gowid.IWidget) {
if !res.Opt.Max.IsNone() && res.Value >= res.Opt.Max.Val() {
res.Value = res.Opt.Max.Val()
} else {
res.Value += 1
}
res.valHolder.SetSubWidget(text.New(fmt.Sprintf("%d", res.Value)), app)
}))

down := button.NewBare(downArrow)

down.OnClick(gowid.MakeWidgetCallback("cb", func(app gowid.IApp, widget gowid.IWidget) {
if !res.Opt.Min.IsNone() && res.Value <= res.Opt.Min.Val() {
res.Value = res.Opt.Min.Val()
} else {
res.Value -= 1
}
res.valHolder.SetSubWidget(text.New(fmt.Sprintf("%d", res.Value)), app)
}))

styler := opt.Styler
if styler == nil {
styler = func(w gowid.IWidget) gowid.IWidget {
return w
}
}

cols := columns.New([]gowid.IContainerWidget{
&gowid.ContainerWidget{
IWidget: res.valHolder,
D: gowid.RenderFixed{},
//D: gowid.RenderWithWeight{W: 1},
},
&gowid.ContainerWidget{
IWidget: blank,
D: gowid.RenderWithUnits{U: 1},
},
&gowid.ContainerWidget{
IWidget: leftbr,
D: gowid.RenderFixed{},
},
&gowid.ContainerWidget{
IWidget: styler(up),
D: gowid.RenderFixed{},
},
&gowid.ContainerWidget{
IWidget: styler(down),
D: gowid.RenderFixed{},
},
&gowid.ContainerWidget{
IWidget: rightbr,
D: gowid.RenderFixed{},
},
})

res.IWidget = cols
res.Opt = opt
res.up = up
res.down = down

return res
}

//======================================================================
// Local Variables:
// mode: Go
// fill-column: 110
// End:
61 changes: 61 additions & 0 deletions widgets/number/number_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2019-2021 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package number

import (
"testing"

"github.com/gcla/gowid"
"github.com/gcla/gowid/gwtest"
"github.com/gdamore/tcell"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)

//======================================================================

func evclick(x, y int) *tcell.EventMouse {
return tcell.NewEventMouse(x, y, tcell.Button1, 0)
}

func evunclick(x, y int) *tcell.EventMouse {
return tcell.NewEventMouse(x, y, tcell.ButtonNone, 0)
}

func TestNumber1(t *testing.T) {
v := 2

w := New(Options{
Value: v,
})
sz := gowid.RenderFixed{}

c1 := w.Render(sz, gowid.NotSelected, gwtest.D)
log.Infof("Canvas is %s", c1.String())
// "0 [^v]"
assert.Equal(t, 1, c1.BoxRows())

clickat := func(x, y int) {
w.UserInput(evclick(x, y), sz, gowid.Focused, gwtest.D)
gwtest.D.SetLastMouseState(gowid.MouseState{true, false, false})
w.UserInput(evunclick(x, y), sz, gowid.Focused, gwtest.D)
gwtest.D.SetLastMouseState(gowid.MouseState{false, false, false})
}

clickat(2, 0)
assert.Equal(t, v, w.Value)

clickat(3, 0)
assert.Equal(t, v+1, w.Value)

clickat(4, 0)
clickat(4, 0)
assert.Equal(t, v+1-2, w.Value)
}

//======================================================================
// Local Variables:
// mode: Go
// fill-column: 110
// End:

0 comments on commit 5137bd3

Please sign in to comment.