From 5884a8d19532a7ba55238d591bc4a30b73b5f43f Mon Sep 17 00:00:00 2001 From: "5684185+vsariola@users.noreply.github.com" <5684185+vsariola@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:20:52 +0300 Subject: [PATCH] feat(tracker/gioui): add tooltips Currently, only iconbtns and numeric updowns have tooltips. Closes #84 --- tracker/gioui/buttons.go | 32 ++++++++++-- tracker/gioui/instrumenteditor.go | 86 +++++++++++++++---------------- tracker/gioui/numericupdown.go | 20 ++++++- tracker/gioui/parameditor.go | 24 ++++----- tracker/gioui/songpanel.go | 12 ++--- tracker/gioui/trackeditor.go | 22 ++++---- 6 files changed, 116 insertions(+), 80 deletions(-) diff --git a/tracker/gioui/buttons.go b/tracker/gioui/buttons.go index 7468e77..434595a 100644 --- a/tracker/gioui/buttons.go +++ b/tracker/gioui/buttons.go @@ -5,10 +5,28 @@ import ( "gioui.org/unit" "gioui.org/widget" "gioui.org/widget/material" + "gioui.org/x/component" ) -func IconButton(th *material.Theme, w *widget.Clickable, icon []byte, enabled bool) material.IconButtonStyle { - ret := material.IconButton(th, w, widgetForIcon(icon), "") +type TipClickable struct { + 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.Inset = layout.UniformInset(unit.Dp(6)) if enabled { @@ -16,7 +34,15 @@ func IconButton(th *material.Theme, w *widget.Clickable, icon []byte, enabled bo } else { 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 { diff --git a/tracker/gioui/instrumenteditor.go b/tracker/gioui/instrumenteditor.go index b778bfd..8c27fa3 100644 --- a/tracker/gioui/instrumenteditor.go +++ b/tracker/gioui/instrumenteditor.go @@ -26,14 +26,14 @@ import ( ) type InstrumentEditor struct { - newInstrumentBtn *widget.Clickable - enlargeBtn *widget.Clickable - deleteInstrumentBtn *widget.Clickable - copyInstrumentBtn *widget.Clickable - saveInstrumentBtn *widget.Clickable - loadInstrumentBtn *widget.Clickable - addUnitBtn *widget.Clickable - commentExpandBtn *widget.Clickable + newInstrumentBtn *TipClickable + enlargeBtn *TipClickable + deleteInstrumentBtn *TipClickable + copyInstrumentBtn *TipClickable + saveInstrumentBtn *TipClickable + loadInstrumentBtn *TipClickable + addUnitBtn *TipClickable + commentExpandBtn *TipClickable commentEditor *widget.Editor nameEditor *widget.Editor unitTypeEditor *widget.Editor @@ -52,14 +52,14 @@ type InstrumentEditor struct { func NewInstrumentEditor() *InstrumentEditor { return &InstrumentEditor{ - newInstrumentBtn: new(widget.Clickable), - enlargeBtn: new(widget.Clickable), - deleteInstrumentBtn: new(widget.Clickable), - copyInstrumentBtn: new(widget.Clickable), - saveInstrumentBtn: new(widget.Clickable), - loadInstrumentBtn: new(widget.Clickable), - addUnitBtn: new(widget.Clickable), - commentExpandBtn: new(widget.Clickable), + newInstrumentBtn: new(TipClickable), + enlargeBtn: new(TipClickable), + deleteInstrumentBtn: new(TipClickable), + copyInstrumentBtn: new(TipClickable), + saveInstrumentBtn: new(TipClickable), + loadInstrumentBtn: new(TipClickable), + addUnitBtn: new(TipClickable), + commentExpandBtn: new(TipClickable), commentEditor: new(widget.Editor), nameEditor: &widget.Editor{SingleLine: true, Submit: true, Alignment: text.Middle}, 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) area.Pop() - var icon []byte + enlargeTip := "Enlarge" + icon := icons.NavigationFullscreen if t.InstrEnlarged() { icon = icons.NavigationFullscreenExit - } else { - icon = icons.NavigationFullscreen + enlargeTip = "Shrink" } - fullscreenBtnStyle := IconButton(t.Theme, ie.enlargeBtn, icon, true) - for ie.enlargeBtn.Clicked() { + fullscreenBtnStyle := IconButton(t.Theme, ie.enlargeBtn, icon, true, enlargeTip) + for ie.enlargeBtn.Clickable.Clicked() { t.SetInstrEnlarged(!t.InstrEnlarged()) } - for ie.newInstrumentBtn.Clicked() { + for ie.newInstrumentBtn.Clickable.Clicked() { t.AddInstrument(true) } octave := func(gtx C) D { in := layout.UniformInset(unit.Dp(1)) t.OctaveNumberInput.Value = t.Octave() - numStyle := NumericUpDown(t.Theme, t.OctaveNumberInput, 0, 9) - gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20)) - gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70)) + numStyle := NumericUpDown(t.Theme, t.OctaveNumberInput, 0, 9, "Octave down (<) or up (>)") dims := in.Layout(gtx, numStyle.Layout) t.SetOctave(t.OctaveNumberInput.Value) 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, layout.Rigid(func(gtx C) D { 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 { header := func(gtx C) D { collapseIcon := icons.NavigationExpandLess + commentTip := "Collapse comment" if !ie.commentExpanded { collapseIcon = icons.NavigationExpandMore + commentTip = "Expand comment" } - commentExpandBtnStyle := IconButton(t.Theme, ie.commentExpandBtn, collapseIcon, true) - copyInstrumentBtnStyle := IconButton(t.Theme, ie.copyInstrumentBtn, icons.ContentContentCopy, true) - saveInstrumentBtnStyle := IconButton(t.Theme, ie.saveInstrumentBtn, icons.ContentSave, true) - loadInstrumentBtnStyle := IconButton(t.Theme, ie.loadInstrumentBtn, icons.FileFolderOpen, true) - deleteInstrumentBtnStyle := IconButton(t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, t.CanDeleteInstrument()) + commentExpandBtnStyle := IconButton(t.Theme, ie.commentExpandBtn, collapseIcon, true, commentTip) + copyInstrumentBtnStyle := IconButton(t.Theme, ie.copyInstrumentBtn, icons.ContentContentCopy, true, "Copy instrument") + saveInstrumentBtnStyle := IconButton(t.Theme, ie.saveInstrumentBtn, icons.ContentSave, true, "Save instrument") + loadInstrumentBtnStyle := IconButton(t.Theme, ie.loadInstrumentBtn, icons.FileFolderOpen, true, "Load instrument") + deleteInstrumentBtnStyle := IconButton(t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, t.CanDeleteInstrument(), "Delete\ninstrument") header := func(gtx C) D { 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 { maxRemain := t.MaxInstrumentVoices() t.InstrumentVoices.Value = t.Instrument().NumVoices - numStyle := NumericUpDown(t.Theme, t.InstrumentVoices, 0, maxRemain) - gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20)) - gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70)) + numStyle := NumericUpDown(t.Theme, t.InstrumentVoices, 0, maxRemain, "Number of voices for this instrument") dims := numStyle.Layout(gtx) t.SetInstrumentVoices(t.InstrumentVoices.Value) return dims @@ -200,7 +198,7 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D { layout.Rigid(copyInstrumentBtnStyle.Layout), layout.Rigid(deleteInstrumentBtnStyle.Layout)) } - for ie.commentExpandBtn.Clicked() { + for ie.commentExpandBtn.Clickable.Clicked() { ie.commentExpanded = !ie.commentExpanded if !ie.commentExpanded { 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) } - for ie.copyInstrumentBtn.Clicked() { + for ie.copyInstrumentBtn.Clickable.Clicked() { contents, err := yaml.Marshal(t.Instrument()) if err == nil { clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops) t.Alert.Update("Instrument copied to clipboard", Notify, time.Second*3) } } - for ie.deleteInstrumentBtn.Clicked() { + for ie.deleteInstrumentBtn.Clickable.Clicked() { if t.CanDeleteInstrument() { dialogStyle := ConfirmDialog(t.Theme, ie.confirmInstrDelete, "Are you sure you want to delete this instrument?") ie.confirmInstrDelete.Visible = true @@ -257,11 +255,11 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D { for ie.confirmInstrDelete.BtnCancel.Clicked() { t.ModalDialog = nil } - for ie.saveInstrumentBtn.Clicked() { + for ie.saveInstrumentBtn.Clickable.Clicked() { t.SaveInstrument() } - for ie.loadInstrumentBtn.Clicked() { + for ie.loadInstrumentBtn.Clickable.Clicked() { t.LoadInstrument() } 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 } func (ie *InstrumentEditor) layoutInstrumentEditor(gtx C, t *Tracker) D { - for ie.addUnitBtn.Clicked() { + for ie.addUnitBtn.Clickable.Clicked() { t.AddUnit(true) ie.unitDragList.Focus() } - addUnitBtnStyle := material.IconButton(t.Theme, ie.addUnitBtn, widgetForIcon(icons.ContentAdd), "Add unit") - addUnitBtnStyle.Color = t.Theme.ContrastFg - addUnitBtnStyle.Background = t.Theme.Fg - addUnitBtnStyle.Inset = layout.UniformInset(unit.Dp(4)) + addUnitBtnStyle := IconButton(t.Theme, ie.addUnitBtn, icons.ContentAdd, true, "Add unit (Ctrl+Enter)") + addUnitBtnStyle.IconButtonStyle.Color = t.Theme.ContrastFg + addUnitBtnStyle.IconButtonStyle.Background = t.Theme.Fg + addUnitBtnStyle.IconButtonStyle.Inset = layout.UniformInset(unit.Dp(4)) units := t.Instrument().Units for len(ie.stackUse) < len(units) { diff --git a/tracker/gioui/numericupdown.go b/tracker/gioui/numericupdown.go index 8c85d4d..23c7635 100644 --- a/tracker/gioui/numericupdown.go +++ b/tracker/gioui/numericupdown.go @@ -11,6 +11,7 @@ import ( "gioui.org/op/clip" "gioui.org/op/paint" "gioui.org/widget" + "gioui.org/x/component" "gioui.org/gesture" "gioui.org/io/pointer" @@ -27,6 +28,7 @@ type NumberInput struct { dragStartXY float32 clickDecrease gesture.Click clickIncrease gesture.Click + tipArea component.TipArea } type NumericUpDownStyle struct { @@ -43,10 +45,13 @@ type NumericUpDownStyle struct { Border unit.Dp ButtonWidth unit.Dp UnitsPerStep unit.Dp + Tooltip component.Tooltip + Width unit.Dp + Height unit.Dp 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.R /= 4 bgColor.G /= 4 @@ -64,12 +69,23 @@ func NumericUpDown(th *material.Theme, number *NumberInput, min, max int) Numeri Border: unit.Dp(1), UnitsPerStep: unit.Dp(8), TextSize: th.TextSize * 14 / 16, + Tooltip: Tooltip(th, tooltip), + Width: unit.Dp(70), + Height: unit.Dp(20), shaper: *th.Shaper, } } 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) border := gtx.Dp(s.Border) c := clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops) diff --git a/tracker/gioui/parameditor.go b/tracker/gioui/parameditor.go index 09c4454..657057d 100644 --- a/tracker/gioui/parameditor.go +++ b/tracker/gioui/parameditor.go @@ -24,9 +24,9 @@ type ParamEditor struct { list *layout.List scrollBar *ScrollBar Parameters []*ParameterWidget - DeleteUnitBtn *widget.Clickable - CopyUnitBtn *widget.Clickable - ClearUnitBtn *widget.Clickable + DeleteUnitBtn *TipClickable + CopyUnitBtn *TipClickable + ClearUnitBtn *TipClickable ChooseUnitTypeBtns []*widget.Clickable tag bool focused bool @@ -43,9 +43,9 @@ func (pe *ParamEditor) Focused() bool { func NewParamEditor() *ParamEditor { ret := &ParamEditor{ - DeleteUnitBtn: new(widget.Clickable), - ClearUnitBtn: new(widget.Clickable), - CopyUnitBtn: new(widget.Clickable), + DeleteUnitBtn: new(TipClickable), + ClearUnitBtn: new(TipClickable), + CopyUnitBtn: new(TipClickable), list: &layout.List{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 { return func(gtx C) D { - for pe.ClearUnitBtn.Clicked() { + for pe.ClearUnitBtn.Clickable.Clicked() { t.SetUnitType("") op.InvalidateOp{}.Add(gtx.Ops) t.InstrumentEditor.unitDragList.Focus() } - for pe.DeleteUnitBtn.Clicked() { + for pe.DeleteUnitBtn.Clickable.Clicked() { t.DeleteUnit(false) op.InvalidateOp{}.Add(gtx.Ops) t.InstrumentEditor.unitDragList.Focus() } - for pe.CopyUnitBtn.Clicked() { + for pe.CopyUnitBtn.Clickable.Clicked() { op.InvalidateOp{}.Add(gtx.Ops) contents, err := yaml.Marshal(t.Unit()) 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) } } - copyUnitBtnStyle := IconButton(t.Theme, pe.CopyUnitBtn, icons.ContentContentCopy, true) - deleteUnitBtnStyle := IconButton(t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, t.CanDeleteUnit()) + copyUnitBtnStyle := IconButton(t.Theme, pe.CopyUnitBtn, icons.ContentContentCopy, true, "Copy unit (Ctrl+C)") + deleteUnitBtnStyle := IconButton(t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, t.CanDeleteUnit(), "Delete unit (Del)") text := t.Unit().Type if text == "" { text = "Choose unit type" @@ -206,7 +206,7 @@ func (pe *ParamEditor) layoutUnitFooter(t *Tracker) layout.Widget { layout.Rigid(func(gtx C) D { var dims D 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) } return D{Size: image.Pt(gtx.Dp(unit.Dp(48)), dims.Size.Y)} diff --git a/tracker/gioui/songpanel.go b/tracker/gioui/songpanel.go index 767fe74..bbb5bf0 100644 --- a/tracker/gioui/songpanel.go +++ b/tracker/gioui/songpanel.go @@ -148,7 +148,7 @@ func (t *Tracker) layoutSongOptions(gtx C) D { layout.Rigid(Label("LEN:", white)), layout.Rigid(func(gtx layout.Context) layout.Dimensions { 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.X = gtx.Dp(unit.Dp(70)) dims := in.Layout(gtx, numStyle.Layout) @@ -162,7 +162,7 @@ func (t *Tracker) layoutSongOptions(gtx C) D { layout.Rigid(Label("BPM:", white)), layout.Rigid(func(gtx layout.Context) layout.Dimensions { 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.X = gtx.Dp(unit.Dp(70)) dims := in.Layout(gtx, numStyle.Layout) @@ -176,7 +176,7 @@ func (t *Tracker) layoutSongOptions(gtx C) D { layout.Rigid(Label("RPP:", white)), layout.Rigid(func(gtx layout.Context) layout.Dimensions { 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.X = gtx.Dp(unit.Dp(70)) dims := in.Layout(gtx, numStyle.Layout) @@ -190,7 +190,7 @@ func (t *Tracker) layoutSongOptions(gtx C) D { layout.Rigid(Label("RPB:", white)), layout.Rigid(func(gtx layout.Context) layout.Dimensions { 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.X = gtx.Dp(unit.Dp(70)) 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, layout.Rigid(Label("STP:", white)), 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) - gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20)) - gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70)) dims := in.Layout(gtx, numStyle.Layout) return dims }), diff --git a/tracker/gioui/trackeditor.go b/tracker/gioui/trackeditor.go index 7bf27a1..ca92a9b 100644 --- a/tracker/gioui/trackeditor.go +++ b/tracker/gioui/trackeditor.go @@ -25,8 +25,8 @@ const patmarkWidth = 16 type TrackEditor struct { TrackVoices *NumberInput - NewTrackBtn *widget.Clickable - DeleteTrackBtn *widget.Clickable + NewTrackBtn *TipClickable + DeleteTrackBtn *TipClickable AddSemitoneBtn *widget.Clickable SubtractSemitoneBtn *widget.Clickable AddOctaveBtn *widget.Clickable @@ -42,8 +42,8 @@ type TrackEditor struct { func NewTrackEditor() *TrackEditor { return &TrackEditor{ TrackVoices: new(NumberInput), - NewTrackBtn: new(widget.Clickable), - DeleteTrackBtn: new(widget.Clickable), + NewTrackBtn: new(TipClickable), + DeleteTrackBtn: new(TipClickable), AddSemitoneBtn: new(widget.Clickable), SubtractSemitoneBtn: new(widget.Clickable), AddOctaveBtn: new(widget.Clickable), @@ -61,7 +61,7 @@ func (te *TrackEditor) Focused() 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|,|." @@ -191,11 +191,11 @@ func (te *TrackEditor) Layout(gtx layout.Context, t *Tracker) layout.Dimensions rowMarkers := layout.Rigid(t.layoutRowMarkers) - for te.NewTrackBtn.Clicked() { + for te.NewTrackBtn.Clickable.Clicked() { t.AddTrack(true) } - for te.DeleteTrackBtn.Clicked() { + for te.DeleteTrackBtn.Clickable.Clicked() { 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") subtractOctaveBtnStyle := LowEmphasisButton(t.Theme, te.SubtractOctaveBtn, "-12") noteOffBtnStyle := LowEmphasisButton(t.Theme, te.NoteOffBtn, "Note Off") - deleteTrackBtnStyle := IconButton(t.Theme, te.DeleteTrackBtn, icons.ActionDelete, t.CanDeleteTrack()) - newTrackBtnStyle := IconButton(t.Theme, te.NewTrackBtn, icons.ContentAdd, t.CanAddTrack()) + deleteTrackBtnStyle := IconButton(t.Theme, te.DeleteTrackBtn, icons.ActionDelete, t.CanDeleteTrack(), "Delete track") + newTrackBtnStyle := IconButton(t.Theme, te.NewTrackBtn, icons.ContentAdd, t.CanAddTrack(), "Add track") n := t.Song().Score.Tracks[t.Cursor().Track].NumVoices te.TrackVoices.Value = n in := layout.UniformInset(unit.Dp(1)) voiceUpDown := func(gtx C) D { - numStyle := NumericUpDown(t.Theme, te.TrackVoices, 1, t.MaxTrackVoices()) - gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(20)) - gtx.Constraints.Min.X = gtx.Dp(unit.Dp(70)) + numStyle := NumericUpDown(t.Theme, te.TrackVoices, 1, t.MaxTrackVoices(), "Number of voices for this track") return in.Layout(gtx, numStyle.Layout) } t.TrackHexCheckBox.Value = t.Song().Score.Tracks[t.Cursor().Track].Effect