feat(tracker/gioui): add tooltips

Currently, only iconbtns and numeric updowns have tooltips. Closes #84
This commit is contained in:
5684185+vsariola@users.noreply.github.com 2023-07-18 23:20:52 +03:00
parent cafb43f8c8
commit 5884a8d195
6 changed files with 116 additions and 80 deletions

View File

@ -5,10 +5,28 @@ import (
"gioui.org/unit" "gioui.org/unit"
"gioui.org/widget" "gioui.org/widget"
"gioui.org/widget/material" "gioui.org/widget/material"
"gioui.org/x/component"
) )
func IconButton(th *material.Theme, w *widget.Clickable, icon []byte, enabled bool) material.IconButtonStyle { type TipClickable struct {
ret := material.IconButton(th, w, widgetForIcon(icon), "") Clickable widget.Clickable
TipArea component.TipArea
}
type TipIconButtonStyle struct {
IconButtonStyle material.IconButtonStyle
Tooltip component.Tooltip
tipArea *component.TipArea
}
func Tooltip(th *material.Theme, tip string) component.Tooltip {
tooltip := component.PlatformTooltip(th, tip)
tooltip.Bg = black
return tooltip
}
func IconButton(th *material.Theme, w *TipClickable, icon []byte, enabled bool, tip string) TipIconButtonStyle {
ret := material.IconButton(th, &w.Clickable, widgetForIcon(icon), "")
ret.Background = transparent ret.Background = transparent
ret.Inset = layout.UniformInset(unit.Dp(6)) ret.Inset = layout.UniformInset(unit.Dp(6))
if enabled { if enabled {
@ -16,7 +34,15 @@ func IconButton(th *material.Theme, w *widget.Clickable, icon []byte, enabled bo
} else { } else {
ret.Color = disabledTextColor ret.Color = disabledTextColor
} }
return ret return TipIconButtonStyle{
IconButtonStyle: ret,
Tooltip: Tooltip(th, tip),
tipArea: &w.TipArea,
}
}
func (t *TipIconButtonStyle) Layout(gtx C) D {
return t.tipArea.Layout(gtx, t.Tooltip, t.IconButtonStyle.Layout)
} }
func LowEmphasisButton(th *material.Theme, w *widget.Clickable, text string) material.ButtonStyle { func LowEmphasisButton(th *material.Theme, w *widget.Clickable, text string) material.ButtonStyle {

View File

@ -26,14 +26,14 @@ import (
) )
type InstrumentEditor struct { type InstrumentEditor struct {
newInstrumentBtn *widget.Clickable newInstrumentBtn *TipClickable
enlargeBtn *widget.Clickable enlargeBtn *TipClickable
deleteInstrumentBtn *widget.Clickable deleteInstrumentBtn *TipClickable
copyInstrumentBtn *widget.Clickable copyInstrumentBtn *TipClickable
saveInstrumentBtn *widget.Clickable saveInstrumentBtn *TipClickable
loadInstrumentBtn *widget.Clickable loadInstrumentBtn *TipClickable
addUnitBtn *widget.Clickable addUnitBtn *TipClickable
commentExpandBtn *widget.Clickable commentExpandBtn *TipClickable
commentEditor *widget.Editor commentEditor *widget.Editor
nameEditor *widget.Editor nameEditor *widget.Editor
unitTypeEditor *widget.Editor unitTypeEditor *widget.Editor
@ -52,14 +52,14 @@ type InstrumentEditor struct {
func NewInstrumentEditor() *InstrumentEditor { func NewInstrumentEditor() *InstrumentEditor {
return &InstrumentEditor{ return &InstrumentEditor{
newInstrumentBtn: new(widget.Clickable), newInstrumentBtn: new(TipClickable),
enlargeBtn: new(widget.Clickable), enlargeBtn: new(TipClickable),
deleteInstrumentBtn: new(widget.Clickable), deleteInstrumentBtn: new(TipClickable),
copyInstrumentBtn: new(widget.Clickable), copyInstrumentBtn: new(TipClickable),
saveInstrumentBtn: new(widget.Clickable), saveInstrumentBtn: new(TipClickable),
loadInstrumentBtn: new(widget.Clickable), loadInstrumentBtn: new(TipClickable),
addUnitBtn: new(widget.Clickable), addUnitBtn: new(TipClickable),
commentExpandBtn: new(widget.Clickable), commentExpandBtn: new(TipClickable),
commentEditor: new(widget.Editor), commentEditor: new(widget.Editor),
nameEditor: &widget.Editor{SingleLine: true, Submit: true, Alignment: text.Middle}, nameEditor: &widget.Editor{SingleLine: true, Submit: true, Alignment: text.Middle},
unitTypeEditor: &widget.Editor{SingleLine: true, Submit: true, Alignment: text.Start}, unitTypeEditor: &widget.Editor{SingleLine: true, Submit: true, Alignment: text.Start},
@ -103,30 +103,28 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
}.Add(gtx.Ops) }.Add(gtx.Ops)
area.Pop() area.Pop()
var icon []byte enlargeTip := "Enlarge"
icon := icons.NavigationFullscreen
if t.InstrEnlarged() { if t.InstrEnlarged() {
icon = icons.NavigationFullscreenExit icon = icons.NavigationFullscreenExit
} else { enlargeTip = "Shrink"
icon = icons.NavigationFullscreen
} }
fullscreenBtnStyle := IconButton(t.Theme, ie.enlargeBtn, icon, true) fullscreenBtnStyle := IconButton(t.Theme, ie.enlargeBtn, icon, true, enlargeTip)
for ie.enlargeBtn.Clicked() { for ie.enlargeBtn.Clickable.Clicked() {
t.SetInstrEnlarged(!t.InstrEnlarged()) t.SetInstrEnlarged(!t.InstrEnlarged())
} }
for ie.newInstrumentBtn.Clicked() { for ie.newInstrumentBtn.Clickable.Clicked() {
t.AddInstrument(true) t.AddInstrument(true)
} }
octave := func(gtx C) D { octave := func(gtx C) D {
in := layout.UniformInset(unit.Dp(1)) in := layout.UniformInset(unit.Dp(1))
t.OctaveNumberInput.Value = t.Octave() t.OctaveNumberInput.Value = t.Octave()
numStyle := NumericUpDown(t.Theme, t.OctaveNumberInput, 0, 9) numStyle := NumericUpDown(t.Theme, t.OctaveNumberInput, 0, 9, "Octave down (<) or up (>)")
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20))
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70))
dims := in.Layout(gtx, numStyle.Layout) dims := in.Layout(gtx, numStyle.Layout)
t.SetOctave(t.OctaveNumberInput.Value) t.SetOctave(t.OctaveNumberInput.Value)
return dims return dims
} }
newBtnStyle := IconButton(t.Theme, ie.newInstrumentBtn, icons.ContentAdd, t.CanAddInstrument()) newBtnStyle := IconButton(t.Theme, ie.newInstrumentBtn, icons.ContentAdd, t.CanAddInstrument(), "Add\ninstrument")
ret := layout.Flex{Axis: layout.Vertical}.Layout(gtx, ret := layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
return layout.Flex{}.Layout( return layout.Flex{}.Layout(
@ -170,15 +168,17 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D { func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
header := func(gtx C) D { header := func(gtx C) D {
collapseIcon := icons.NavigationExpandLess collapseIcon := icons.NavigationExpandLess
commentTip := "Collapse comment"
if !ie.commentExpanded { if !ie.commentExpanded {
collapseIcon = icons.NavigationExpandMore collapseIcon = icons.NavigationExpandMore
commentTip = "Expand comment"
} }
commentExpandBtnStyle := IconButton(t.Theme, ie.commentExpandBtn, collapseIcon, true) commentExpandBtnStyle := IconButton(t.Theme, ie.commentExpandBtn, collapseIcon, true, commentTip)
copyInstrumentBtnStyle := IconButton(t.Theme, ie.copyInstrumentBtn, icons.ContentContentCopy, true) copyInstrumentBtnStyle := IconButton(t.Theme, ie.copyInstrumentBtn, icons.ContentContentCopy, true, "Copy instrument")
saveInstrumentBtnStyle := IconButton(t.Theme, ie.saveInstrumentBtn, icons.ContentSave, true) saveInstrumentBtnStyle := IconButton(t.Theme, ie.saveInstrumentBtn, icons.ContentSave, true, "Save instrument")
loadInstrumentBtnStyle := IconButton(t.Theme, ie.loadInstrumentBtn, icons.FileFolderOpen, true) loadInstrumentBtnStyle := IconButton(t.Theme, ie.loadInstrumentBtn, icons.FileFolderOpen, true, "Load instrument")
deleteInstrumentBtnStyle := IconButton(t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, t.CanDeleteInstrument()) deleteInstrumentBtnStyle := IconButton(t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, t.CanDeleteInstrument(), "Delete\ninstrument")
header := func(gtx C) D { header := func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
@ -186,9 +186,7 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
maxRemain := t.MaxInstrumentVoices() maxRemain := t.MaxInstrumentVoices()
t.InstrumentVoices.Value = t.Instrument().NumVoices t.InstrumentVoices.Value = t.Instrument().NumVoices
numStyle := NumericUpDown(t.Theme, t.InstrumentVoices, 0, maxRemain) numStyle := NumericUpDown(t.Theme, t.InstrumentVoices, 0, maxRemain, "Number of voices for this instrument")
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20))
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70))
dims := numStyle.Layout(gtx) dims := numStyle.Layout(gtx)
t.SetInstrumentVoices(t.InstrumentVoices.Value) t.SetInstrumentVoices(t.InstrumentVoices.Value)
return dims return dims
@ -200,7 +198,7 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
layout.Rigid(copyInstrumentBtnStyle.Layout), layout.Rigid(copyInstrumentBtnStyle.Layout),
layout.Rigid(deleteInstrumentBtnStyle.Layout)) layout.Rigid(deleteInstrumentBtnStyle.Layout))
} }
for ie.commentExpandBtn.Clicked() { for ie.commentExpandBtn.Clickable.Clicked() {
ie.commentExpanded = !ie.commentExpanded ie.commentExpanded = !ie.commentExpanded
if !ie.commentExpanded { if !ie.commentExpanded {
key.FocusOp{Tag: &ie.tag}.Add(gtx.Ops) // clear focus key.FocusOp{Tag: &ie.tag}.Add(gtx.Ops) // clear focus
@ -236,14 +234,14 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
} }
return header(gtx) return header(gtx)
} }
for ie.copyInstrumentBtn.Clicked() { for ie.copyInstrumentBtn.Clickable.Clicked() {
contents, err := yaml.Marshal(t.Instrument()) contents, err := yaml.Marshal(t.Instrument())
if err == nil { if err == nil {
clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops) clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops)
t.Alert.Update("Instrument copied to clipboard", Notify, time.Second*3) t.Alert.Update("Instrument copied to clipboard", Notify, time.Second*3)
} }
} }
for ie.deleteInstrumentBtn.Clicked() { for ie.deleteInstrumentBtn.Clickable.Clicked() {
if t.CanDeleteInstrument() { if t.CanDeleteInstrument() {
dialogStyle := ConfirmDialog(t.Theme, ie.confirmInstrDelete, "Are you sure you want to delete this instrument?") dialogStyle := ConfirmDialog(t.Theme, ie.confirmInstrDelete, "Are you sure you want to delete this instrument?")
ie.confirmInstrDelete.Visible = true ie.confirmInstrDelete.Visible = true
@ -257,11 +255,11 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
for ie.confirmInstrDelete.BtnCancel.Clicked() { for ie.confirmInstrDelete.BtnCancel.Clicked() {
t.ModalDialog = nil t.ModalDialog = nil
} }
for ie.saveInstrumentBtn.Clicked() { for ie.saveInstrumentBtn.Clickable.Clicked() {
t.SaveInstrument() t.SaveInstrument()
} }
for ie.loadInstrumentBtn.Clicked() { for ie.loadInstrumentBtn.Clickable.Clicked() {
t.LoadInstrument() t.LoadInstrument()
} }
return Surface{Gray: 37, Focus: ie.wasFocused}.Layout(gtx, header) return Surface{Gray: 37, Focus: ie.wasFocused}.Layout(gtx, header)
@ -362,14 +360,14 @@ func (ie *InstrumentEditor) layoutInstrumentNames(gtx C, t *Tracker) D {
return dims return dims
} }
func (ie *InstrumentEditor) layoutInstrumentEditor(gtx C, t *Tracker) D { func (ie *InstrumentEditor) layoutInstrumentEditor(gtx C, t *Tracker) D {
for ie.addUnitBtn.Clicked() { for ie.addUnitBtn.Clickable.Clicked() {
t.AddUnit(true) t.AddUnit(true)
ie.unitDragList.Focus() ie.unitDragList.Focus()
} }
addUnitBtnStyle := material.IconButton(t.Theme, ie.addUnitBtn, widgetForIcon(icons.ContentAdd), "Add unit") addUnitBtnStyle := IconButton(t.Theme, ie.addUnitBtn, icons.ContentAdd, true, "Add unit (Ctrl+Enter)")
addUnitBtnStyle.Color = t.Theme.ContrastFg addUnitBtnStyle.IconButtonStyle.Color = t.Theme.ContrastFg
addUnitBtnStyle.Background = t.Theme.Fg addUnitBtnStyle.IconButtonStyle.Background = t.Theme.Fg
addUnitBtnStyle.Inset = layout.UniformInset(unit.Dp(4)) addUnitBtnStyle.IconButtonStyle.Inset = layout.UniformInset(unit.Dp(4))
units := t.Instrument().Units units := t.Instrument().Units
for len(ie.stackUse) < len(units) { for len(ie.stackUse) < len(units) {

View File

@ -11,6 +11,7 @@ import (
"gioui.org/op/clip" "gioui.org/op/clip"
"gioui.org/op/paint" "gioui.org/op/paint"
"gioui.org/widget" "gioui.org/widget"
"gioui.org/x/component"
"gioui.org/gesture" "gioui.org/gesture"
"gioui.org/io/pointer" "gioui.org/io/pointer"
@ -27,6 +28,7 @@ type NumberInput struct {
dragStartXY float32 dragStartXY float32
clickDecrease gesture.Click clickDecrease gesture.Click
clickIncrease gesture.Click clickIncrease gesture.Click
tipArea component.TipArea
} }
type NumericUpDownStyle struct { type NumericUpDownStyle struct {
@ -43,10 +45,13 @@ type NumericUpDownStyle struct {
Border unit.Dp Border unit.Dp
ButtonWidth unit.Dp ButtonWidth unit.Dp
UnitsPerStep unit.Dp UnitsPerStep unit.Dp
Tooltip component.Tooltip
Width unit.Dp
Height unit.Dp
shaper text.Shaper shaper text.Shaper
} }
func NumericUpDown(th *material.Theme, number *NumberInput, min, max int) NumericUpDownStyle { func NumericUpDown(th *material.Theme, number *NumberInput, min, max int, tooltip string) NumericUpDownStyle {
bgColor := th.Palette.Fg bgColor := th.Palette.Fg
bgColor.R /= 4 bgColor.R /= 4
bgColor.G /= 4 bgColor.G /= 4
@ -64,12 +69,23 @@ func NumericUpDown(th *material.Theme, number *NumberInput, min, max int) Numeri
Border: unit.Dp(1), Border: unit.Dp(1),
UnitsPerStep: unit.Dp(8), UnitsPerStep: unit.Dp(8),
TextSize: th.TextSize * 14 / 16, TextSize: th.TextSize * 14 / 16,
Tooltip: Tooltip(th, tooltip),
Width: unit.Dp(70),
Height: unit.Dp(20),
shaper: *th.Shaper, shaper: *th.Shaper,
} }
} }
func (s NumericUpDownStyle) Layout(gtx C) D { func (s NumericUpDownStyle) Layout(gtx C) D {
size := gtx.Constraints.Min if s.Tooltip.Text.Text != "" {
return s.NumberInput.tipArea.Layout(gtx, s.Tooltip, s.actualLayout)
}
return s.actualLayout(gtx)
}
func (s NumericUpDownStyle) actualLayout(gtx C) D {
size := image.Pt(gtx.Dp(s.Width), gtx.Dp(s.Height))
gtx.Constraints.Min = size
rr := gtx.Dp(s.CornerRadius) rr := gtx.Dp(s.CornerRadius)
border := gtx.Dp(s.Border) border := gtx.Dp(s.Border)
c := clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops) c := clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops)

View File

@ -24,9 +24,9 @@ type ParamEditor struct {
list *layout.List list *layout.List
scrollBar *ScrollBar scrollBar *ScrollBar
Parameters []*ParameterWidget Parameters []*ParameterWidget
DeleteUnitBtn *widget.Clickable DeleteUnitBtn *TipClickable
CopyUnitBtn *widget.Clickable CopyUnitBtn *TipClickable
ClearUnitBtn *widget.Clickable ClearUnitBtn *TipClickable
ChooseUnitTypeBtns []*widget.Clickable ChooseUnitTypeBtns []*widget.Clickable
tag bool tag bool
focused bool focused bool
@ -43,9 +43,9 @@ func (pe *ParamEditor) Focused() bool {
func NewParamEditor() *ParamEditor { func NewParamEditor() *ParamEditor {
ret := &ParamEditor{ ret := &ParamEditor{
DeleteUnitBtn: new(widget.Clickable), DeleteUnitBtn: new(TipClickable),
ClearUnitBtn: new(widget.Clickable), ClearUnitBtn: new(TipClickable),
CopyUnitBtn: new(widget.Clickable), CopyUnitBtn: new(TipClickable),
list: &layout.List{Axis: layout.Vertical}, list: &layout.List{Axis: layout.Vertical},
scrollBar: &ScrollBar{Axis: layout.Vertical}, scrollBar: &ScrollBar{Axis: layout.Vertical},
} }
@ -173,17 +173,17 @@ func (pe *ParamEditor) layoutUnitSliders(gtx C, t *Tracker) D {
func (pe *ParamEditor) layoutUnitFooter(t *Tracker) layout.Widget { func (pe *ParamEditor) layoutUnitFooter(t *Tracker) layout.Widget {
return func(gtx C) D { return func(gtx C) D {
for pe.ClearUnitBtn.Clicked() { for pe.ClearUnitBtn.Clickable.Clicked() {
t.SetUnitType("") t.SetUnitType("")
op.InvalidateOp{}.Add(gtx.Ops) op.InvalidateOp{}.Add(gtx.Ops)
t.InstrumentEditor.unitDragList.Focus() t.InstrumentEditor.unitDragList.Focus()
} }
for pe.DeleteUnitBtn.Clicked() { for pe.DeleteUnitBtn.Clickable.Clicked() {
t.DeleteUnit(false) t.DeleteUnit(false)
op.InvalidateOp{}.Add(gtx.Ops) op.InvalidateOp{}.Add(gtx.Ops)
t.InstrumentEditor.unitDragList.Focus() t.InstrumentEditor.unitDragList.Focus()
} }
for pe.CopyUnitBtn.Clicked() { for pe.CopyUnitBtn.Clickable.Clicked() {
op.InvalidateOp{}.Add(gtx.Ops) op.InvalidateOp{}.Add(gtx.Ops)
contents, err := yaml.Marshal(t.Unit()) contents, err := yaml.Marshal(t.Unit())
if err == nil { if err == nil {
@ -191,8 +191,8 @@ func (pe *ParamEditor) layoutUnitFooter(t *Tracker) layout.Widget {
t.Alert.Update("Unit copied to clipboard", Notify, time.Second*3) t.Alert.Update("Unit copied to clipboard", Notify, time.Second*3)
} }
} }
copyUnitBtnStyle := IconButton(t.Theme, pe.CopyUnitBtn, icons.ContentContentCopy, true) copyUnitBtnStyle := IconButton(t.Theme, pe.CopyUnitBtn, icons.ContentContentCopy, true, "Copy unit (Ctrl+C)")
deleteUnitBtnStyle := IconButton(t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, t.CanDeleteUnit()) deleteUnitBtnStyle := IconButton(t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, t.CanDeleteUnit(), "Delete unit (Del)")
text := t.Unit().Type text := t.Unit().Type
if text == "" { if text == "" {
text = "Choose unit type" text = "Choose unit type"
@ -206,7 +206,7 @@ func (pe *ParamEditor) layoutUnitFooter(t *Tracker) layout.Widget {
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
var dims D var dims D
if t.Unit().Type != "" { if t.Unit().Type != "" {
clearUnitBtnStyle := IconButton(t.Theme, pe.ClearUnitBtn, icons.ContentClear, true) clearUnitBtnStyle := IconButton(t.Theme, pe.ClearUnitBtn, icons.ContentClear, true, "Clear unit")
dims = clearUnitBtnStyle.Layout(gtx) dims = clearUnitBtnStyle.Layout(gtx)
} }
return D{Size: image.Pt(gtx.Dp(unit.Dp(48)), dims.Size.Y)} return D{Size: image.Pt(gtx.Dp(unit.Dp(48)), dims.Size.Y)}

View File

@ -148,7 +148,7 @@ func (t *Tracker) layoutSongOptions(gtx C) D {
layout.Rigid(Label("LEN:", white)), layout.Rigid(Label("LEN:", white)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
t.SongLength.Value = t.Song().Score.Length t.SongLength.Value = t.Song().Score.Length
numStyle := NumericUpDown(t.Theme, t.SongLength, 1, math.MaxInt32) numStyle := NumericUpDown(t.Theme, t.SongLength, 1, math.MaxInt32, "Song length")
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20)) gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20))
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70)) gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70))
dims := in.Layout(gtx, numStyle.Layout) dims := in.Layout(gtx, numStyle.Layout)
@ -162,7 +162,7 @@ func (t *Tracker) layoutSongOptions(gtx C) D {
layout.Rigid(Label("BPM:", white)), layout.Rigid(Label("BPM:", white)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
t.BPM.Value = t.Song().BPM t.BPM.Value = t.Song().BPM
numStyle := NumericUpDown(t.Theme, t.BPM, 1, 999) numStyle := NumericUpDown(t.Theme, t.BPM, 1, 999, "Beats per minute")
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20)) gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20))
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70)) gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70))
dims := in.Layout(gtx, numStyle.Layout) dims := in.Layout(gtx, numStyle.Layout)
@ -176,7 +176,7 @@ func (t *Tracker) layoutSongOptions(gtx C) D {
layout.Rigid(Label("RPP:", white)), layout.Rigid(Label("RPP:", white)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
t.RowsPerPattern.Value = t.Song().Score.RowsPerPattern t.RowsPerPattern.Value = t.Song().Score.RowsPerPattern
numStyle := NumericUpDown(t.Theme, t.RowsPerPattern, 1, 255) numStyle := NumericUpDown(t.Theme, t.RowsPerPattern, 1, 255, "Rows per pattern")
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20)) gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20))
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70)) gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70))
dims := in.Layout(gtx, numStyle.Layout) dims := in.Layout(gtx, numStyle.Layout)
@ -190,7 +190,7 @@ func (t *Tracker) layoutSongOptions(gtx C) D {
layout.Rigid(Label("RPB:", white)), layout.Rigid(Label("RPB:", white)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
t.RowsPerBeat.Value = t.Song().RowsPerBeat t.RowsPerBeat.Value = t.Song().RowsPerBeat
numStyle := NumericUpDown(t.Theme, t.RowsPerBeat, 1, 32) numStyle := NumericUpDown(t.Theme, t.RowsPerBeat, 1, 32, "Rows per beat")
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20)) gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20))
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70)) gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70))
dims := in.Layout(gtx, numStyle.Layout) dims := in.Layout(gtx, numStyle.Layout)
@ -203,10 +203,8 @@ func (t *Tracker) layoutSongOptions(gtx C) D {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(Label("STP:", white)), layout.Rigid(Label("STP:", white)),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
numStyle := NumericUpDown(t.Theme, t.Step, 0, 8) numStyle := NumericUpDown(t.Theme, t.Step, 0, 8, "Cursor step")
numStyle.UnitsPerStep = unit.Dp(20) numStyle.UnitsPerStep = unit.Dp(20)
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20))
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70))
dims := in.Layout(gtx, numStyle.Layout) dims := in.Layout(gtx, numStyle.Layout)
return dims return dims
}), }),

View File

@ -25,8 +25,8 @@ const patmarkWidth = 16
type TrackEditor struct { type TrackEditor struct {
TrackVoices *NumberInput TrackVoices *NumberInput
NewTrackBtn *widget.Clickable NewTrackBtn *TipClickable
DeleteTrackBtn *widget.Clickable DeleteTrackBtn *TipClickable
AddSemitoneBtn *widget.Clickable AddSemitoneBtn *widget.Clickable
SubtractSemitoneBtn *widget.Clickable SubtractSemitoneBtn *widget.Clickable
AddOctaveBtn *widget.Clickable AddOctaveBtn *widget.Clickable
@ -42,8 +42,8 @@ type TrackEditor struct {
func NewTrackEditor() *TrackEditor { func NewTrackEditor() *TrackEditor {
return &TrackEditor{ return &TrackEditor{
TrackVoices: new(NumberInput), TrackVoices: new(NumberInput),
NewTrackBtn: new(widget.Clickable), NewTrackBtn: new(TipClickable),
DeleteTrackBtn: new(widget.Clickable), DeleteTrackBtn: new(TipClickable),
AddSemitoneBtn: new(widget.Clickable), AddSemitoneBtn: new(widget.Clickable),
SubtractSemitoneBtn: new(widget.Clickable), SubtractSemitoneBtn: new(widget.Clickable),
AddOctaveBtn: new(widget.Clickable), AddOctaveBtn: new(widget.Clickable),
@ -61,7 +61,7 @@ func (te *TrackEditor) Focused() bool {
} }
func (te *TrackEditor) ChildFocused() bool { func (te *TrackEditor) ChildFocused() bool {
return te.AddOctaveBtn.Focused() || te.AddSemitoneBtn.Focused() || te.DeleteTrackBtn.Focused() || te.NewTrackBtn.Focused() || te.NoteOffBtn.Focused() || te.SubtractOctaveBtn.Focused() || te.SubtractSemitoneBtn.Focused() || te.SubtractSemitoneBtn.Focused() || te.SubtractSemitoneBtn.Focused() return te.AddOctaveBtn.Focused() || te.AddSemitoneBtn.Focused() || te.DeleteTrackBtn.Clickable.Focused() || te.NewTrackBtn.Clickable.Focused() || te.NoteOffBtn.Focused() || te.SubtractOctaveBtn.Focused() || te.SubtractSemitoneBtn.Focused() || te.SubtractSemitoneBtn.Focused() || te.SubtractSemitoneBtn.Focused()
} }
var trackerEditorKeys key.Set = "+|-|←|→|↑|↓|Ctrl-←|Ctrl-→|Ctrl-↑|Ctrl-↓|Shift-←|Shift-→|Shift-↑|Shift-↓|⏎|⇱|⇲|⌫|⌦|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|,|." var trackerEditorKeys key.Set = "+|-|←|→|↑|↓|Ctrl-←|Ctrl-→|Ctrl-↑|Ctrl-↓|Shift-←|Shift-→|Shift-↑|Shift-↓|⏎|⇱|⇲|⌫|⌦|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|,|."
@ -191,11 +191,11 @@ func (te *TrackEditor) Layout(gtx layout.Context, t *Tracker) layout.Dimensions
rowMarkers := layout.Rigid(t.layoutRowMarkers) rowMarkers := layout.Rigid(t.layoutRowMarkers)
for te.NewTrackBtn.Clicked() { for te.NewTrackBtn.Clickable.Clicked() {
t.AddTrack(true) t.AddTrack(true)
} }
for te.DeleteTrackBtn.Clicked() { for te.DeleteTrackBtn.Clickable.Clicked() {
t.DeleteTrack(false) t.DeleteTrack(false)
} }
@ -234,15 +234,13 @@ func (te *TrackEditor) Layout(gtx layout.Context, t *Tracker) layout.Dimensions
addOctaveBtnStyle := LowEmphasisButton(t.Theme, te.AddOctaveBtn, "+12") addOctaveBtnStyle := LowEmphasisButton(t.Theme, te.AddOctaveBtn, "+12")
subtractOctaveBtnStyle := LowEmphasisButton(t.Theme, te.SubtractOctaveBtn, "-12") subtractOctaveBtnStyle := LowEmphasisButton(t.Theme, te.SubtractOctaveBtn, "-12")
noteOffBtnStyle := LowEmphasisButton(t.Theme, te.NoteOffBtn, "Note Off") noteOffBtnStyle := LowEmphasisButton(t.Theme, te.NoteOffBtn, "Note Off")
deleteTrackBtnStyle := IconButton(t.Theme, te.DeleteTrackBtn, icons.ActionDelete, t.CanDeleteTrack()) deleteTrackBtnStyle := IconButton(t.Theme, te.DeleteTrackBtn, icons.ActionDelete, t.CanDeleteTrack(), "Delete track")
newTrackBtnStyle := IconButton(t.Theme, te.NewTrackBtn, icons.ContentAdd, t.CanAddTrack()) newTrackBtnStyle := IconButton(t.Theme, te.NewTrackBtn, icons.ContentAdd, t.CanAddTrack(), "Add track")
n := t.Song().Score.Tracks[t.Cursor().Track].NumVoices n := t.Song().Score.Tracks[t.Cursor().Track].NumVoices
te.TrackVoices.Value = n te.TrackVoices.Value = n
in := layout.UniformInset(unit.Dp(1)) in := layout.UniformInset(unit.Dp(1))
voiceUpDown := func(gtx C) D { voiceUpDown := func(gtx C) D {
numStyle := NumericUpDown(t.Theme, te.TrackVoices, 1, t.MaxTrackVoices()) numStyle := NumericUpDown(t.Theme, te.TrackVoices, 1, t.MaxTrackVoices(), "Number of voices for this track")
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20))
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70))
return in.Layout(gtx, numStyle.Layout) return in.Layout(gtx, numStyle.Layout)
} }
t.TrackHexCheckBox.Value = t.Song().Score.Tracks[t.Cursor().Track].Effect t.TrackHexCheckBox.Value = t.Song().Score.Tracks[t.Cursor().Track].Effect