From f587c5d865ba9dc01270b0c56e317eaffb1e357e Mon Sep 17 00:00:00 2001 From: "5684185+vsariola@users.noreply.github.com" <5684185+vsariola@users.noreply.github.com> Date: Sat, 21 Jun 2025 23:49:07 +0300 Subject: [PATCH] drafting new buttons --- tracker/gioui/buttons.go | 426 +++++++++++++---------------- tracker/gioui/instrument_editor.go | 104 +++---- tracker/gioui/keybindings.go | 4 +- tracker/gioui/menu.go | 4 +- tracker/gioui/note_editor.go | 91 +++--- tracker/gioui/oscilloscope.go | 17 +- tracker/gioui/songpanel.go | 61 ++--- tracker/gioui/theme.go | 2 + tracker/gioui/theme.yml | 9 + tracker/gioui/tracker.go | 2 +- tracker/gioui/unit_editor.go | 28 +- 11 files changed, 346 insertions(+), 402 deletions(-) diff --git a/tracker/gioui/buttons.go b/tracker/gioui/buttons.go index 8bf45b8..b98d755 100644 --- a/tracker/gioui/buttons.go +++ b/tracker/gioui/buttons.go @@ -22,38 +22,217 @@ import ( ) type ( - TipClickable struct { + Clickable struct { + click gesture.Click + history []widget.Press + + requestClicks int + } + + ClickableTip struct { Clickable Clickable TipArea component.TipArea } - ActionClickable struct { - Action tracker.Action - TipClickable + ButtonStyle struct { + // Color is the text color. + Color color.NRGBA + Font font.Font + TextSize unit.Sp + Background color.NRGBA + CornerRadius unit.Dp + Height unit.Dp + Inset layout.Inset } - TipIconButtonStyle struct { - TipArea *component.TipArea - IconButtonStyle IconButtonStyle - Tooltip component.Tooltip + IconButtonStyle struct { + Background color.NRGBA + // Color is the icon color. + Color color.NRGBA + // Size is the icon size. + Size unit.Dp + Inset layout.Inset } - BoolClickable struct { - Clickable Clickable - TipArea component.TipArea - Bool tracker.Bool + TipIconButton struct { + th *Theme + st *IconButtonStyle + t *ClickableTip + icon []byte + tip string + enabled bool } ) -func NewActionClickable(a tracker.Action) *ActionClickable { - return &ActionClickable{ - Action: a, +func TipIconBtn(th *Theme, st *IconButtonStyle, t *ClickableTip, icon []byte, tip string, enabled bool) TipIconButton { + return TipIconButton{ + th: th, + st: st, + t: t, + icon: icon, + tip: tip, + enabled: enabled, } } -func NewBoolClickable(b tracker.Bool) *BoolClickable { - return &BoolClickable{ - Bool: b, +func (t TipIconButton) Layout(gtx C) D { + iconBtn := IconBtn(t.th, t.st, &t.t.Clickable, t.th.Icon(t.icon), t.enabled) + if t.tip != "" { + return t.t.TipArea.Layout(gtx, Tooltip(t.th, t.tip), iconBtn) + } else { + return iconBtn(gtx) + } +} + +func ActionIconBtn(act tracker.Action, th *Theme, t *ClickableTip, icon []byte, tip string) layout.Widget { + return func(gtx C) D { + for t.Clickable.Clicked(gtx) { + act.Do() + } + iconBtn := IconBtn(th, &th.IconButton, &t.Clickable, th.Icon(icon), act.Enabled()) + if tip != "" { + return t.TipArea.Layout(gtx, Tooltip(th, tip), iconBtn) + } else { + return iconBtn(gtx) + } + } +} + +func ToggleIconBtn(b tracker.Bool, th *Theme, t *ClickableTip, offIcon, onIcon []byte, offTip, onTip string) layout.Widget { + return func(gtx C) D { + icon := offIcon + tip := offTip + if b.Value() { + icon = onIcon + tip = onTip + } + for t.Clickable.Clicked(gtx) { + b.Toggle() + } + iconBtn := IconBtn(th, &th.IconButton, &t.Clickable, th.Icon(icon), b.Enabled()) + if tip != "" { + return t.TipArea.Layout(gtx, Tooltip(th, tip), iconBtn) + } else { + return iconBtn(gtx) + } + } +} + +func ActionBtn(a tracker.Action, th *Theme, c *ClickableTip, text string, tip string) layout.Widget { + return func(gtx C) D { + for c.Clickable.Clicked(gtx) { + a.Do() + } + var btn layout.Widget + if !a.Enabled() { + btn = Btn(th, &th.Button.Disabled, &c.Clickable, text) + } else { + btn = Btn(th, &th.Button.Text, &c.Clickable, text) + } + if tip != "" { + return c.TipArea.Layout(gtx, Tooltip(th, tip), btn) + } else { + return btn(gtx) + } + } +} + +func ToggleBtn(b tracker.Bool, th *Theme, c *ClickableTip, text string, tip string) layout.Widget { + return func(gtx C) D { + for c.Clickable.Clicked(gtx) { + b.Toggle() + } + var btn layout.Widget + if !b.Enabled() { + btn = Btn(th, &th.Button.Disabled, &c.Clickable, text) + } else if b.Value() { + btn = Btn(th, &th.Button.Filled, &c.Clickable, text) + } else { + btn = Btn(th, &th.Button.Text, &c.Clickable, text) + } + if tip != "" { + return c.TipArea.Layout(gtx, Tooltip(th, tip), btn) + } else { + return btn(gtx) + } + } +} + +func Btn(th *Theme, st *ButtonStyle, b *Clickable, txt string) layout.Widget { + return func(gtx C) D { + min := gtx.Constraints.Min + min.Y = gtx.Dp(st.Height) + return b.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + semantic.Button.Add(gtx.Ops) + return layout.Background{}.Layout(gtx, + func(gtx layout.Context) layout.Dimensions { + rr := gtx.Dp(st.CornerRadius) + defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop() + background := st.Background + switch { + case b.Hovered(): + background = hoveredColor(background) + } + paint.Fill(gtx.Ops, background) + for _, c := range b.History() { + drawInk(gtx, (widget.Press)(c)) + } + return layout.Dimensions{Size: gtx.Constraints.Min} + }, + func(gtx layout.Context) layout.Dimensions { + gtx.Constraints.Min = min + return layout.Center.Layout(gtx, func(gtx C) D { + return st.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + colMacro := op.Record(gtx.Ops) + paint.ColorOp{Color: st.Color}.Add(gtx.Ops) + return widget.Label{Alignment: text.Middle}.Layout(gtx, th.Material.Shaper, st.Font, st.TextSize, txt, colMacro.Stop()) + }) + }) + }, + ) + }) + } +} + +func IconBtn(th *Theme, st *IconButtonStyle, b *Clickable, icon *widget.Icon, enabled bool) layout.Widget { + return func(gtx C) D { + m := op.Record(gtx.Ops) + dims := b.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + semantic.Button.Add(gtx.Ops) + return layout.Background{}.Layout(gtx, + func(gtx layout.Context) layout.Dimensions { + rr := (gtx.Constraints.Min.X + gtx.Constraints.Min.Y) / 4 + defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop() + background := st.Background + switch { + case b.Hovered(): + background = hoveredColor(background) + } + paint.Fill(gtx.Ops, background) + for _, c := range b.History() { + drawInk(gtx, (widget.Press)(c)) + } + return layout.Dimensions{Size: gtx.Constraints.Min} + }, + func(gtx layout.Context) layout.Dimensions { + return st.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + size := gtx.Dp(st.Size) + if icon != nil { + gtx.Constraints.Min = image.Point{X: size} + icon.Layout(gtx, st.Color) + } + return layout.Dimensions{ + Size: image.Point{X: size, Y: size}, + } + }) + }, + ) + }) + c := m.Stop() + bounds := image.Rectangle{Max: dims.Size} + defer clip.Ellipse(bounds).Push(gtx.Ops).Pop() + c.Add(gtx.Ops) + return dims } } @@ -64,88 +243,6 @@ func Tooltip(th *Theme, tip string) component.Tooltip { return tooltip } -func ActionIcon(gtx C, th *Theme, w *ActionClickable, icon []byte, tip string) TipIconButtonStyle { - ret := TipIcon(th, &w.TipClickable, icon, tip) - for w.Clickable.Clicked(gtx) { - w.Action.Do() - } - if !w.Action.Enabled() { - ret.IconButtonStyle.Color = th.Button.Disabled.Color - } - return ret -} - -func TipIcon(th *Theme, w *TipClickable, icon []byte, tip string) TipIconButtonStyle { - iconButtonStyle := IconButton(th, &w.Clickable, th.Icon(icon), "") - iconButtonStyle.Color = th.Material.Palette.ContrastBg - iconButtonStyle.Background = color.NRGBA{} - iconButtonStyle.Inset = layout.UniformInset(unit.Dp(6)) - return TipIconButtonStyle{ - TipArea: &w.TipArea, - IconButtonStyle: iconButtonStyle, - Tooltip: Tooltip(th, tip), - } -} - -func ToggleIcon(gtx C, th *Theme, w *BoolClickable, offIcon, onIcon []byte, offTip, onTip string) TipIconButtonStyle { - icon := offIcon - tip := offTip - if w.Bool.Value() { - icon = onIcon - tip = onTip - } - for w.Clickable.Clicked(gtx) { - w.Bool.Toggle() - } - ibStyle := IconButton(th, &w.Clickable, th.Icon(icon), "") - ibStyle.Background = color.NRGBA{} - ibStyle.Inset = layout.UniformInset(unit.Dp(6)) - ibStyle.Color = th.Material.Palette.ContrastBg - if !w.Bool.Enabled() { - ibStyle.Color = th.Button.Disabled.Color - } - return TipIconButtonStyle{ - TipArea: &w.TipArea, - IconButtonStyle: ibStyle, - Tooltip: Tooltip(th, tip), - } -} - -func (t *TipIconButtonStyle) Layout(gtx C) D { - return t.TipArea.Layout(gtx, t.Tooltip, t.IconButtonStyle.Layout) -} - -func ActionButton(gtx C, th *Theme, style *ButtonStyle, w *ActionClickable, text string) Button { - for w.Clickable.Clicked(gtx) { - w.Action.Do() - } - if !w.Action.Enabled() { - return Btn(th, &th.Button.Disabled, &w.Clickable, text) - } - return Btn(th, style, &w.Clickable, text) -} - -func ToggleButton(gtx C, th *Theme, b *BoolClickable, text string) Button { - for b.Clickable.Clicked(gtx) { - b.Bool.Toggle() - } - if !b.Bool.Enabled() { - return Btn(th, &th.Button.Disabled, &b.Clickable, text) - } - if b.Bool.Value() { - return Btn(th, &th.Button.Filled, &b.Clickable, text) - } - return Btn(th, &th.Button.Text, &b.Clickable, text) -} - -// Clickable represents a clickable area. -type Clickable struct { - click gesture.Click - history []widget.Press - - requestClicks int -} - // Click executes a simple programmatic click. func (b *Clickable) Click() { b.requestClicks++ @@ -252,135 +349,6 @@ func (b *Clickable) update(_ event.Tag, gtx layout.Context) (widget.Click, bool) return widget.Click{}, false } -type ButtonStyle struct { - // Color is the text color. - Color color.NRGBA - Font font.Font - TextSize unit.Sp - Background color.NRGBA - CornerRadius unit.Dp - Height unit.Dp - Inset layout.Inset -} - -type Button struct { - Text string - Button *Clickable - shaper *text.Shaper - ButtonStyle -} - -type IconButtonStyle struct { - Background color.NRGBA - // Color is the icon color. - Color color.NRGBA - Icon *widget.Icon - // Size is the icon size. - Size unit.Dp - Inset layout.Inset - Button *Clickable - Description string -} - -func Btn(th *Theme, style *ButtonStyle, button *Clickable, txt string) Button { - b := Button{ - Text: txt, - ButtonStyle: *style, - Button: button, - shaper: th.Material.Shaper, - } - return b -} - -func IconButton(th *Theme, button *Clickable, icon *widget.Icon, description string) IconButtonStyle { - return IconButtonStyle{ - Background: th.Material.Palette.ContrastBg, - Color: th.Material.Palette.ContrastFg, - Icon: icon, - Size: 24, - Inset: layout.UniformInset(12), - Button: button, - Description: description, - } -} - -func (b *Button) Layout(gtx layout.Context) layout.Dimensions { - min := gtx.Constraints.Min - min.Y = gtx.Dp(b.Height) - return b.Button.Layout(gtx, func(gtx layout.Context) layout.Dimensions { - semantic.Button.Add(gtx.Ops) - return layout.Background{}.Layout(gtx, - func(gtx layout.Context) layout.Dimensions { - rr := gtx.Dp(b.CornerRadius) - defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop() - background := b.Background - switch { - case b.Button.Hovered(): - background = hoveredColor(background) - } - paint.Fill(gtx.Ops, background) - for _, c := range b.Button.History() { - drawInk(gtx, (widget.Press)(c)) - } - return layout.Dimensions{Size: gtx.Constraints.Min} - }, - func(gtx layout.Context) layout.Dimensions { - gtx.Constraints.Min = min - return layout.Center.Layout(gtx, func(gtx C) D { - return b.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { - colMacro := op.Record(gtx.Ops) - paint.ColorOp{Color: b.Color}.Add(gtx.Ops) - return widget.Label{Alignment: text.Middle}.Layout(gtx, b.shaper, b.Font, b.TextSize, b.Text, colMacro.Stop()) - }) - }) - }, - ) - }) -} - -func (b IconButtonStyle) Layout(gtx layout.Context) layout.Dimensions { - m := op.Record(gtx.Ops) - dims := b.Button.Layout(gtx, func(gtx layout.Context) layout.Dimensions { - semantic.Button.Add(gtx.Ops) - if d := b.Description; d != "" { - semantic.DescriptionOp(b.Description).Add(gtx.Ops) - } - return layout.Background{}.Layout(gtx, - func(gtx layout.Context) layout.Dimensions { - rr := (gtx.Constraints.Min.X + gtx.Constraints.Min.Y) / 4 - defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop() - background := b.Background - switch { - case b.Button.Hovered(): - background = hoveredColor(background) - } - paint.Fill(gtx.Ops, background) - for _, c := range b.Button.History() { - drawInk(gtx, (widget.Press)(c)) - } - return layout.Dimensions{Size: gtx.Constraints.Min} - }, - func(gtx layout.Context) layout.Dimensions { - return b.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { - size := gtx.Dp(b.Size) - if b.Icon != nil { - gtx.Constraints.Min = image.Point{X: size} - b.Icon.Layout(gtx, b.Color) - } - return layout.Dimensions{ - Size: image.Point{X: size, Y: size}, - } - }) - }, - ) - }) - c := m.Stop() - bounds := image.Rectangle{Max: dims.Size} - defer clip.Ellipse(bounds).Push(gtx.Ops).Pop() - c.Add(gtx.Ops) - return dims -} - func drawInk(gtx layout.Context, c widget.Press) { // duration is the number of seconds for the // completed animation: expand while fading in, then diff --git a/tracker/gioui/instrument_editor.go b/tracker/gioui/instrument_editor.go index 35bf3f6..98d354a 100644 --- a/tracker/gioui/instrument_editor.go +++ b/tracker/gioui/instrument_editor.go @@ -23,19 +23,19 @@ import ( type ( InstrumentEditor struct { - newInstrumentBtn *ActionClickable - enlargeBtn *BoolClickable - deleteInstrumentBtn *ActionClickable - linkInstrTrackBtn *BoolClickable - splitInstrumentBtn *ActionClickable - copyInstrumentBtn *TipClickable - saveInstrumentBtn *TipClickable - loadInstrumentBtn *TipClickable - addUnitBtn *ActionClickable - presetMenuBtn *TipClickable - commentExpandBtn *BoolClickable - soloBtn *BoolClickable - muteBtn *BoolClickable + newInstrumentBtn *ClickableTip + enlargeBtn *ClickableTip + deleteInstrumentBtn *ClickableTip + linkInstrTrackBtn *ClickableTip + splitInstrumentBtn *ClickableTip + copyInstrumentBtn *ClickableTip + saveInstrumentBtn *ClickableTip + loadInstrumentBtn *ClickableTip + addUnitBtn *ClickableTip + presetMenuBtn *ClickableTip + commentExpandBtn *ClickableTip + soloBtn *ClickableTip + muteBtn *ClickableTip commentEditor *Editor commentString tracker.String nameEditor *Editor @@ -68,18 +68,19 @@ type ( func NewInstrumentEditor(model *tracker.Model) *InstrumentEditor { ret := &InstrumentEditor{ - newInstrumentBtn: NewActionClickable(model.AddInstrument()), - enlargeBtn: NewBoolClickable(model.InstrEnlarged()), - deleteInstrumentBtn: NewActionClickable(model.DeleteInstrument()), - linkInstrTrackBtn: NewBoolClickable(model.LinkInstrTrack()), - splitInstrumentBtn: NewActionClickable(model.SplitInstrument()), - copyInstrumentBtn: new(TipClickable), - saveInstrumentBtn: new(TipClickable), - loadInstrumentBtn: new(TipClickable), - commentExpandBtn: NewBoolClickable(model.CommentExpanded()), - presetMenuBtn: new(TipClickable), - soloBtn: NewBoolClickable(model.Solo()), - muteBtn: NewBoolClickable(model.Mute()), + newInstrumentBtn: new(ClickableTip), + enlargeBtn: new(ClickableTip), + deleteInstrumentBtn: new(ClickableTip), + linkInstrTrackBtn: new(ClickableTip), + splitInstrumentBtn: new(ClickableTip), + copyInstrumentBtn: new(ClickableTip), + saveInstrumentBtn: new(ClickableTip), + loadInstrumentBtn: new(ClickableTip), + commentExpandBtn: new(ClickableTip), + presetMenuBtn: new(ClickableTip), + soloBtn: new(ClickableTip), + muteBtn: new(ClickableTip), + addUnitBtn: new(ClickableTip), commentEditor: NewEditor(false, false, text.Start), nameEditor: NewEditor(true, true, text.Middle), searchEditor: NewEditor(true, true, text.Start), @@ -95,7 +96,6 @@ func NewInstrumentEditor(model *tracker.Model) *InstrumentEditor { return true }) ret.addUnit = model.AddUnit(false) - ret.addUnitBtn = NewActionClickable(tracker.MakeEnabledAction(ret.AddUnitThenFocus())) ret.enlargeHint = makeHint("Enlarge", " (%s)", "InstrEnlargedToggle") ret.shrinkHint = makeHint("Shrink", " (%s)", "InstrEnlargedToggle") ret.addInstrumentHint = makeHint("Add\ninstrument", "\n(%s)", "AddInstrument") @@ -139,8 +139,6 @@ func (ie *InstrumentEditor) childFocused(gtx C) bool { func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D { ie.wasFocused = ie.Focused(gtx) || ie.childFocused(gtx) - fullscreenBtnStyle := ToggleIcon(gtx, t.Theme, ie.enlargeBtn, icons.NavigationFullscreen, icons.NavigationFullscreenExit, ie.enlargeHint, ie.shrinkHint) - linkBtnStyle := ToggleIcon(gtx, t.Theme, ie.linkInstrTrackBtn, icons.NotificationSyncDisabled, icons.NotificationSync, ie.linkDisabledHint, ie.linkEnabledHint) octave := func(gtx C) D { in := layout.UniformInset(unit.Dp(1)) @@ -149,7 +147,6 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D { }) } - newBtnStyle := ActionIcon(gtx, t.Theme, ie.newInstrumentBtn, icons.ContentAdd, ie.addInstrumentHint) ret := layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout( @@ -162,13 +159,13 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D { layout.Rigid(layout.Spacer{Width: 4}.Layout), layout.Rigid(octave), layout.Rigid(func(gtx C) D { - return layout.E.Layout(gtx, linkBtnStyle.Layout) + return layout.E.Layout(gtx, ToggleIconBtn(t.Model.LinkInstrTrack(), t.Theme, ie.linkInstrTrackBtn, icons.NotificationSyncDisabled, icons.NotificationSync, ie.linkDisabledHint, ie.linkEnabledHint)) }), layout.Rigid(func(gtx C) D { - return layout.E.Layout(gtx, fullscreenBtnStyle.Layout) + return layout.E.Layout(gtx, ToggleIconBtn(t.Model.InstrEnlarged(), t.Theme, ie.enlargeBtn, icons.NavigationFullscreen, icons.NavigationFullscreenExit, ie.enlargeHint, ie.shrinkHint)) }), layout.Rigid(func(gtx C) D { - return layout.E.Layout(gtx, newBtnStyle.Layout) + return layout.E.Layout(gtx, ActionIconBtn(t.Model.AddInstrument(), t.Theme, ie.newInstrumentBtn, icons.ContentAdd, ie.addInstrumentHint)) }), ) }), @@ -190,16 +187,6 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D { func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D { header := func(gtx C) D { - commentExpandBtnStyle := ToggleIcon(gtx, t.Theme, ie.commentExpandBtn, icons.NavigationExpandMore, icons.NavigationExpandLess, ie.expandCommentHint, ie.collapseCommentHint) - presetMenuBtnStyle := TipIcon(t.Theme, ie.presetMenuBtn, icons.NavigationMenu, "Load preset") - copyInstrumentBtnStyle := TipIcon(t.Theme, ie.copyInstrumentBtn, icons.ContentContentCopy, "Copy instrument") - saveInstrumentBtnStyle := TipIcon(t.Theme, ie.saveInstrumentBtn, icons.ContentSave, "Save instrument") - loadInstrumentBtnStyle := TipIcon(t.Theme, ie.loadInstrumentBtn, icons.FileFolderOpen, "Load instrument") - deleteInstrumentBtnStyle := ActionIcon(gtx, t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, ie.deleteInstrumentHint) - splitInstrumentBtnStyle := ActionIcon(gtx, t.Theme, ie.splitInstrumentBtn, icons.CommunicationCallSplit, ie.splitInstrumentHint) - soloBtnStyle := ToggleIcon(gtx, t.Theme, ie.soloBtn, icons.SocialGroup, icons.SocialPerson, ie.soloHint, ie.unsoloHint) - muteBtnStyle := ToggleIcon(gtx, t.Theme, ie.muteBtn, icons.AVVolumeUp, icons.AVVolumeOff, ie.muteHint, ie.unmuteHint) - m := PopupMenu(t.Theme, &t.Theme.Menu.Text, &ie.presetMenu) for ie.copyInstrumentBtn.Clickable.Clicked(gtx) { @@ -233,31 +220,31 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D { layout.Rigid(func(gtx layout.Context) layout.Dimensions { return t.InstrumentVoices.Layout(gtx, t.Model.InstrumentVoices(), t.Theme, &t.Theme.NumericUpDown, "Number of voices for this instrument") }), - layout.Rigid(splitInstrumentBtnStyle.Layout), + layout.Rigid(ActionIconBtn(t.SplitInstrument(), t.Theme, ie.splitInstrumentBtn, icons.CommunicationCallSplit, ie.splitInstrumentHint)), layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }), - layout.Rigid(commentExpandBtnStyle.Layout), - layout.Rigid(soloBtnStyle.Layout), - layout.Rigid(muteBtnStyle.Layout), + layout.Rigid(ToggleIconBtn(t.CommentExpanded(), t.Theme, ie.commentExpandBtn, icons.NavigationExpandMore, icons.NavigationExpandLess, ie.expandCommentHint, ie.collapseCommentHint)), + layout.Rigid(ToggleIconBtn(t.Solo(), t.Theme, ie.soloBtn, icons.SocialGroup, icons.SocialPerson, ie.soloHint, ie.unsoloHint)), + layout.Rigid(ToggleIconBtn(t.Mute(), t.Theme, ie.muteBtn, icons.AVVolumeUp, icons.AVVolumeOff, ie.muteHint, ie.unmuteHint)), layout.Rigid(func(gtx C) D { - //defer op.Offset(image.Point{}).Push(gtx.Ops).Pop() - dims := presetMenuBtnStyle.Layout(gtx) + dims := TipIconBtn(t.Theme, &t.Theme.IconButton, ie.presetMenuBtn, icons.NavigationMenu, "Load preset", true).Layout(gtx) op.Offset(image.Pt(0, dims.Size.Y)).Add(gtx.Ops) gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(500)) gtx.Constraints.Max.X = gtx.Dp(unit.Dp(180)) m.Layout(gtx, ie.presetMenuItems...) return dims }), - layout.Rigid(saveInstrumentBtnStyle.Layout), - layout.Rigid(loadInstrumentBtnStyle.Layout), - layout.Rigid(copyInstrumentBtnStyle.Layout), - layout.Rigid(deleteInstrumentBtnStyle.Layout)) + layout.Rigid(TipIconBtn(t.Theme, &t.Theme.IconButton, ie.saveInstrumentBtn, icons.ContentSave, "Save instrument", true).Layout), + layout.Rigid(TipIconBtn(t.Theme, &t.Theme.IconButton, ie.loadInstrumentBtn, icons.FileFolderOpen, "Load instrument", true).Layout), + layout.Rigid(TipIconBtn(t.Theme, &t.Theme.IconButton, ie.copyInstrumentBtn, icons.ContentContentCopy, "Copy instrument", true).Layout), + layout.Rigid(ActionIconBtn(t.DeleteInstrument(), t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, ie.deleteInstrumentHint)), + ) } for ie.presetMenuBtn.Clickable.Clicked(gtx) { ie.presetMenu.Visible = true } - if ie.commentExpandBtn.Bool.Value() || gtx.Source.Focused(ie.commentEditor) { // we draw once the widget after it manages to lose focus + if t.CommentExpanded().Value() || gtx.Source.Focused(ie.commentEditor) { // we draw once the widget after it manages to lose focus ret := layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(header), layout.Rigid(func(gtx C) D { @@ -356,12 +343,6 @@ func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D { } func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D { - // TODO: how to ie.unitDragList.Focus() - addUnitBtnStyle := ActionIcon(gtx, t.Theme, ie.addUnitBtn, icons.ContentAdd, "Add unit (Enter)") - addUnitBtnStyle.IconButtonStyle.Color = t.Theme.Material.ContrastFg - addUnitBtnStyle.IconButtonStyle.Background = t.Theme.Material.ContrastBg - addUnitBtnStyle.IconButtonStyle.Inset = layout.UniformInset(unit.Dp(4)) - var units [256]tracker.UnitListItem for i, item := range (*tracker.Units)(t.Model).Iterate { if i >= 256 { @@ -482,8 +463,11 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D { return dims }), layout.Stacked(func(gtx C) D { + for ie.addUnitBtn.Clickable.Clicked(gtx) { + t.AddUnit(false).Do() + } margin := layout.Inset{Right: unit.Dp(20), Bottom: unit.Dp(1)} - return margin.Layout(gtx, addUnitBtnStyle.Layout) + return margin.Layout(gtx, TipIconBtn(t.Theme, &t.Theme.AddUnit, ie.addUnitBtn, icons.ContentAdd, "Add unit (Enter)", true).Layout) }), ) }) diff --git a/tracker/gioui/keybindings.go b/tracker/gioui/keybindings.go index 9afdb81..5e6e93a 100644 --- a/tracker/gioui/keybindings.go +++ b/tracker/gioui/keybindings.go @@ -263,7 +263,7 @@ func (t *Tracker) KeyEvent(e key.Event, gtx C) { case t.TrackEditor.scrollTable.Focused(gtx): t.OrderEditor.scrollTable.Focus() case t.InstrumentEditor.Focused(gtx): - if t.InstrumentEditor.enlargeBtn.Bool.Value() { + if t.InstrEnlarged().Value() { t.InstrumentEditor.unitEditor.sliderList.Focus() } else { t.TrackEditor.scrollTable.Focus() @@ -280,7 +280,7 @@ func (t *Tracker) KeyEvent(e key.Event, gtx C) { case t.InstrumentEditor.Focused(gtx): t.InstrumentEditor.unitEditor.sliderList.Focus() default: - if t.InstrumentEditor.enlargeBtn.Bool.Value() { + if t.InstrEnlarged().Value() { t.InstrumentEditor.Focus() } else { t.OrderEditor.scrollTable.Focus() diff --git a/tracker/gioui/menu.go b/tracker/gioui/menu.go index d5ccbe1..efcc7b2 100644 --- a/tracker/gioui/menu.go +++ b/tracker/gioui/menu.go @@ -168,9 +168,7 @@ func (tr *Tracker) layoutMenu(gtx C, title string, clickable *Clickable, menu *M m := PopupMenu(tr.Theme, &tr.Theme.Menu.Text, menu) return func(gtx C) D { defer op.Offset(image.Point{}).Push(gtx.Ops).Pop() - titleBtn := Btn(tr.Theme, &tr.Theme.Button.Menu, clickable, title) - titleBtn.CornerRadius = unit.Dp(0) - dims := titleBtn.Layout(gtx) + dims := Btn(tr.Theme, &tr.Theme.Button.Menu, clickable, title)(gtx) op.Offset(image.Pt(0, dims.Size.Y)).Add(gtx.Ops) gtx.Constraints.Max.X = gtx.Dp(width) gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(300)) diff --git a/tracker/gioui/note_editor.go b/tracker/gioui/note_editor.go index 7ba62dc..1db1612 100644 --- a/tracker/gioui/note_editor.go +++ b/tracker/gioui/note_editor.go @@ -52,18 +52,18 @@ func init() { type NoteEditor struct { TrackVoices *NumericUpDown - NewTrackBtn *ActionClickable - DeleteTrackBtn *ActionClickable - SplitTrackBtn *ActionClickable + NewTrackBtn *ClickableTip + DeleteTrackBtn *ClickableTip + SplitTrackBtn *ClickableTip - AddSemitoneBtn *ActionClickable - SubtractSemitoneBtn *ActionClickable - AddOctaveBtn *ActionClickable - SubtractOctaveBtn *ActionClickable - NoteOffBtn *ActionClickable - EffectBtn *BoolClickable - UniqueBtn *BoolClickable - TrackMidiInBtn *BoolClickable + AddSemitoneBtn *ClickableTip + SubtractSemitoneBtn *ClickableTip + AddOctaveBtn *ClickableTip + SubtractOctaveBtn *ClickableTip + NoteOffBtn *ClickableTip + EffectBtn *ClickableTip + UniqueBtn *ClickableTip + TrackMidiInBtn *ClickableTip scrollTable *ScrollTable eventFilters []event.Filter @@ -77,17 +77,17 @@ type NoteEditor struct { func NewNoteEditor(model *tracker.Model) *NoteEditor { ret := &NoteEditor{ TrackVoices: NewNumericUpDown(), - NewTrackBtn: NewActionClickable(model.AddTrack()), - DeleteTrackBtn: NewActionClickable(model.DeleteTrack()), - SplitTrackBtn: NewActionClickable(model.SplitTrack()), - AddSemitoneBtn: NewActionClickable(model.AddSemitone()), - SubtractSemitoneBtn: NewActionClickable(model.SubtractSemitone()), - AddOctaveBtn: NewActionClickable(model.AddOctave()), - SubtractOctaveBtn: NewActionClickable(model.SubtractOctave()), - NoteOffBtn: NewActionClickable(model.EditNoteOff()), - EffectBtn: NewBoolClickable(model.Effect()), - UniqueBtn: NewBoolClickable(model.UniquePatterns()), - TrackMidiInBtn: NewBoolClickable(model.TrackMidiIn()), + NewTrackBtn: new(ClickableTip), + DeleteTrackBtn: new(ClickableTip), + SplitTrackBtn: new(ClickableTip), + AddSemitoneBtn: new(ClickableTip), + SubtractSemitoneBtn: new(ClickableTip), + AddOctaveBtn: new(ClickableTip), + SubtractOctaveBtn: new(ClickableTip), + NoteOffBtn: new(ClickableTip), + EffectBtn: new(ClickableTip), + UniqueBtn: new(ClickableTip), + TrackMidiInBtn: new(ClickableTip), scrollTable: NewScrollTable( model.Notes().Table(), model.Tracks().List(), @@ -164,42 +164,41 @@ func (te *NoteEditor) Layout(gtx layout.Context, t *Tracker) layout.Dimensions { func (te *NoteEditor) layoutButtons(gtx C, t *Tracker) D { return Surface{Gray: 37, Focus: te.scrollTable.Focused(gtx) || te.scrollTable.ChildFocused(gtx)}.Layout(gtx, func(gtx C) D { - addSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.AddSemitoneBtn, "+1") - subtractSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.SubtractSemitoneBtn, "-1") - addOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.AddOctaveBtn, "+12") - subtractOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.SubtractOctaveBtn, "-12") - noteOffBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.NoteOffBtn, "Note Off") - deleteTrackBtnStyle := ActionIcon(gtx, t.Theme, te.DeleteTrackBtn, icons.ActionDelete, te.deleteTrackHint) - splitTrackBtnStyle := ActionIcon(gtx, t.Theme, te.SplitTrackBtn, icons.CommunicationCallSplit, te.splitTrackHint) - newTrackBtnStyle := ActionIcon(gtx, t.Theme, te.NewTrackBtn, icons.ContentAdd, te.addTrackHint) + addSemitoneBtnStyle := ActionBtn(t.AddSemitone(), t.Theme, te.AddSemitoneBtn, "+1", "Add semitone") + subtractSemitoneBtnStyle := ActionBtn(t.SubtractSemitone(), t.Theme, te.SubtractSemitoneBtn, "-1", "Subtract semitone") + addOctaveBtnStyle := ActionBtn(t.AddOctave(), t.Theme, te.AddOctaveBtn, "+12", "Add octave") + subtractOctaveBtnStyle := ActionBtn(t.SubtractOctave(), t.Theme, te.SubtractOctaveBtn, "-12", "Subtract octave") + noteOffBtnStyle := ActionBtn(t.EditNoteOff(), t.Theme, te.NoteOffBtn, "Note Off", "") + deleteTrackBtnStyle := ActionIconBtn(t.DeleteTrack(), t.Theme, te.DeleteTrackBtn, icons.ActionDelete, te.deleteTrackHint) + splitTrackBtnStyle := ActionIconBtn(t.SplitTrack(), t.Theme, te.SplitTrackBtn, icons.CommunicationCallSplit, te.splitTrackHint) + newTrackBtnStyle := ActionIconBtn(t.AddTrack(), t.Theme, te.NewTrackBtn, icons.ContentAdd, te.addTrackHint) in := layout.UniformInset(unit.Dp(1)) voiceUpDown := func(gtx C) D { return in.Layout(gtx, func(gtx C) D { return te.TrackVoices.Layout(gtx, t.Model.TrackVoices(), t.Theme, &t.Theme.NumericUpDown, "Track voices") }) } - effectBtnStyle := ToggleButton(gtx, t.Theme, te.EffectBtn, "Hex") - uniqueBtnStyle := ToggleIcon(gtx, t.Theme, te.UniqueBtn, icons.ToggleStarBorder, icons.ToggleStar, te.uniqueOffTip, te.uniqueOnTip) - midiInBtnStyle := ToggleButton(gtx, t.Theme, te.TrackMidiInBtn, "MIDI") + effectBtnStyle := ToggleBtn(t.Effect(), t.Theme, te.EffectBtn, "Hex", "Input notes as hex values") + uniqueBtnStyle := ToggleIconBtn(t.UniquePatterns(), t.Theme, te.UniqueBtn, icons.ToggleStarBorder, icons.ToggleStar, te.uniqueOffTip, te.uniqueOnTip) + midiInBtnStyle := ToggleBtn(t.TrackMidiIn(), t.Theme, te.TrackMidiInBtn, "MIDI", "Input notes from MIDI keyboard") return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, layout.Rigid(func(gtx C) D { return layout.Dimensions{Size: image.Pt(gtx.Dp(unit.Dp(12)), 0)} }), - layout.Rigid(addSemitoneBtnStyle.Layout), - layout.Rigid(subtractSemitoneBtnStyle.Layout), - layout.Rigid(addOctaveBtnStyle.Layout), - layout.Rigid(subtractOctaveBtnStyle.Layout), - layout.Rigid(noteOffBtnStyle.Layout), - layout.Rigid(effectBtnStyle.Layout), - layout.Rigid(uniqueBtnStyle.Layout), + layout.Rigid(addSemitoneBtnStyle), + layout.Rigid(subtractSemitoneBtnStyle), + layout.Rigid(addOctaveBtnStyle), + layout.Rigid(subtractOctaveBtnStyle), + layout.Rigid(noteOffBtnStyle), + layout.Rigid(effectBtnStyle), + layout.Rigid(uniqueBtnStyle), layout.Rigid(layout.Spacer{Width: 10}.Layout), layout.Rigid(Label(t.Theme, &t.Theme.NoteEditor.Header, "Voices").Layout), layout.Rigid(layout.Spacer{Width: 4}.Layout), layout.Rigid(voiceUpDown), - layout.Rigid(splitTrackBtnStyle.Layout), + layout.Rigid(splitTrackBtnStyle), + layout.Rigid(midiInBtnStyle), layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }), - layout.Rigid(midiInBtnStyle.Layout), - layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }), - layout.Rigid(deleteTrackBtnStyle.Layout), - layout.Rigid(newTrackBtnStyle.Layout)) + layout.Rigid(deleteTrackBtnStyle), + layout.Rigid(newTrackBtnStyle)) }) } @@ -280,7 +279,7 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D { cursor := te.scrollTable.Table.Cursor() drawSelection := cursor != te.scrollTable.Table.Cursor2() selection := te.scrollTable.Table.Range() - hasTrackMidiIn := te.TrackMidiInBtn.Bool.Value() + hasTrackMidiIn := t.Model.TrackMidiIn().Value() patternNoOp := colorOp(gtx, t.Theme.NoteEditor.PatternNo.Color) uniqueOp := colorOp(gtx, t.Theme.NoteEditor.Unique.Color) diff --git a/tracker/gioui/oscilloscope.go b/tracker/gioui/oscilloscope.go index 0fa3256..f9a7a5c 100644 --- a/tracker/gioui/oscilloscope.go +++ b/tracker/gioui/oscilloscope.go @@ -17,8 +17,8 @@ import ( type ( OscilloscopeState struct { - onceBtn *BoolClickable - wrapBtn *BoolClickable + onceBtn *ClickableTip + wrapBtn *ClickableTip lengthInBeatsNumber *NumericUpDown triggerChannelNumber *NumericUpDown xScale int @@ -38,17 +38,14 @@ type ( func NewOscilloscope(model *tracker.Model) *OscilloscopeState { return &OscilloscopeState{ - onceBtn: NewBoolClickable(model.SignalAnalyzer().Once()), - wrapBtn: NewBoolClickable(model.SignalAnalyzer().Wrap()), + onceBtn: new(ClickableTip), + wrapBtn: new(ClickableTip), lengthInBeatsNumber: NewNumericUpDown(), triggerChannelNumber: NewNumericUpDown(), } } -func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, wave tracker.RingBuffer[[2]float32], th *Theme, st *OscilloscopeStyle) D { - wrapBtnStyle := ToggleButton(gtx, th, s.wrapBtn, "Wrap") - onceBtnStyle := ToggleButton(gtx, th, s.onceBtn, "Once") - +func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, once, wrap tracker.Bool, wave tracker.RingBuffer[[2]float32], th *Theme, st *OscilloscopeStyle) D { leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout rightSpacer := layout.Spacer{Width: unit.Dp(6)}.Layout @@ -59,7 +56,7 @@ func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, wave tracker. layout.Rigid(leftSpacer), layout.Rigid(Label(th, &th.SongPanel.RowHeader, "Trigger").Layout), layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }), - layout.Rigid(onceBtnStyle.Layout), + layout.Rigid(ToggleBtn(once, th, s.onceBtn, "Once", "Trigger once on next event")), layout.Rigid(func(gtx C) D { return s.triggerChannelNumber.Layout(gtx, vtrig, th, &th.NumericUpDown, "Trigger channel") }), @@ -71,7 +68,7 @@ func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, wave tracker. layout.Rigid(leftSpacer), layout.Rigid(Label(th, &th.SongPanel.RowHeader, "Buffer").Layout), layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }), - layout.Rigid(wrapBtnStyle.Layout), + layout.Rigid(ToggleBtn(wrap, th, s.wrapBtn, "Wrap", "Wrap buffer when full")), layout.Rigid(func(gtx C) D { return s.lengthInBeatsNumber.Layout(gtx, vlen, th, &th.NumericUpDown, "Buffer length in beats") }), diff --git a/tracker/gioui/songpanel.go b/tracker/gioui/songpanel.go index a27e5fb..381f692 100644 --- a/tracker/gioui/songpanel.go +++ b/tracker/gioui/songpanel.go @@ -46,7 +46,7 @@ func NewSongPanel(model *tracker.Model) *SongPanel { SongLength: NewNumericUpDown(), Scope: NewOscilloscope(model), MenuBar: NewMenuBar(model), - PlayBar: NewPlayBar(model), + PlayBar: NewPlayBar(), WeightingTypeBtn: &Clickable{}, OversamplingBtn: &Clickable{}, @@ -75,7 +75,7 @@ func (s *SongPanel) Layout(gtx C, t *Tracker) D { return s.MenuBar.Layout(gtx, t) }), layout.Rigid(func(gtx C) D { - return s.PlayBar.Layout(gtx, t.Theme) + return s.PlayBar.Layout(gtx, t) }), layout.Rigid(func(gtx C) D { return s.layoutSongOptions(gtx, t) @@ -164,7 +164,7 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D { }), layout.Rigid(func(gtx C) D { gtx.Constraints.Min.X = 0 - return weightingBtn.Layout(gtx) + return weightingBtn(gtx) }), ) }, @@ -193,7 +193,7 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D { }), layout.Rigid(func(gtx C) D { gtx.Constraints.Min.X = 0 - return oversamplingBtn.Layout(gtx) + return oversamplingBtn(gtx) }), ) }, @@ -201,7 +201,7 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D { }), layout.Flexed(1, func(gtx C) D { return t.ScopeExpander.Layout(gtx, tr.Theme, "Oscilloscope", func(gtx C) D { return D{} }, func(gtx C) D { - return t.Scope.Layout(gtx, tr.Model.SignalAnalyzer().TriggerChannel(), tr.Model.SignalAnalyzer().LengthInBeats(), tr.Model.SignalAnalyzer().Waveform(), tr.Theme, &tr.Theme.Oscilloscope) + return t.Scope.Layout(gtx, tr.Model.SignalAnalyzer().TriggerChannel(), tr.Model.SignalAnalyzer().LengthInBeats(), tr.Model.SignalAnalyzer().Once(), tr.Model.SignalAnalyzer().Wrap(), tr.Model.SignalAnalyzer().Waveform(), tr.Theme, &tr.Theme.Oscilloscope) }) }), layout.Rigid(Label(tr.Theme, &tr.Theme.SongPanel.Version, version.VersionOrHash).Layout), @@ -304,14 +304,14 @@ type MenuBar struct { midiMenuItems []MenuItem panicHint string - PanicBtn *BoolClickable + PanicBtn *ClickableTip } func NewMenuBar(model *tracker.Model) *MenuBar { ret := &MenuBar{ Clickables: make([]Clickable, 3), Menus: make([]Menu, 3), - PanicBtn: NewBoolClickable(model.Panic()), + PanicBtn: new(ClickableTip), panicHint: makeHint("Panic", " (%s)", "PanicToggle"), } ret.fileMenuItems = []MenuItem{ @@ -343,15 +343,12 @@ func (t *MenuBar) Layout(gtx C, tr *Tracker) D { gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(36)) gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(36)) - panicBtnStyle := ToggleIcon(gtx, tr.Theme, t.PanicBtn, icons.AlertErrorOutline, icons.AlertError, t.panicHint, t.panicHint) - if t.PanicBtn.Bool.Value() { - panicBtnStyle.IconButtonStyle.Color = tr.Theme.SongPanel.ErrorColor - } + panicBtnStyle := ToggleIconBtn(tr.Panic(), tr.Theme, t.PanicBtn, icons.AlertErrorOutline, icons.AlertError, t.panicHint, t.panicHint) flex := layout.Flex{Axis: layout.Horizontal, Alignment: layout.End} fileFC := layout.Rigid(tr.layoutMenu(gtx, "File", &t.Clickables[0], &t.Menus[0], unit.Dp(200), t.fileMenuItems...)) editFC := layout.Rigid(tr.layoutMenu(gtx, "Edit", &t.Clickables[1], &t.Menus[1], unit.Dp(200), t.editMenuItems...)) midiFC := layout.Rigid(tr.layoutMenu(gtx, "MIDI", &t.Clickables[2], &t.Menus[2], unit.Dp(200), t.midiMenuItems...)) - panicFC := layout.Flexed(1, func(gtx C) D { return layout.E.Layout(gtx, panicBtnStyle.Layout) }) + panicFC := layout.Flexed(1, func(gtx C) D { return layout.E.Layout(gtx, panicBtnStyle) }) if len(t.midiMenuItems) > 0 { return flex.Layout(gtx, fileFC, editFC, midiFC, panicFC) } @@ -359,11 +356,11 @@ func (t *MenuBar) Layout(gtx C, tr *Tracker) D { } type PlayBar struct { - RewindBtn *ActionClickable - PlayingBtn *BoolClickable - RecordBtn *BoolClickable - FollowBtn *BoolClickable - LoopBtn *BoolClickable + RewindBtn *ClickableTip + PlayingBtn *ClickableTip + RecordBtn *ClickableTip + FollowBtn *ClickableTip + LoopBtn *ClickableTip // Hints rewindHint string playHint, stopHint string @@ -372,13 +369,13 @@ type PlayBar struct { loopOffHint, loopOnHint string } -func NewPlayBar(model *tracker.Model) *PlayBar { +func NewPlayBar() *PlayBar { ret := &PlayBar{ - LoopBtn: NewBoolClickable(model.LoopToggle()), - RecordBtn: NewBoolClickable(model.IsRecording()), - FollowBtn: NewBoolClickable(model.Follow()), - PlayingBtn: NewBoolClickable(model.Playing()), - RewindBtn: NewActionClickable(model.PlaySongStart()), + LoopBtn: new(ClickableTip), + RecordBtn: new(ClickableTip), + FollowBtn: new(ClickableTip), + PlayingBtn: new(ClickableTip), + RewindBtn: new(ClickableTip), } ret.rewindHint = makeHint("Rewind", "\n(%s)", "PlaySongStartUnfollow") ret.playHint = makeHint("Play", " (%s)", "PlayCurrentPosUnfollow") @@ -392,20 +389,14 @@ func NewPlayBar(model *tracker.Model) *PlayBar { return ret } -func (pb *PlayBar) Layout(gtx C, th *Theme) D { - rewindBtnStyle := ActionIcon(gtx, th, pb.RewindBtn, icons.AVFastRewind, pb.rewindHint) - playBtnStyle := ToggleIcon(gtx, th, pb.PlayingBtn, icons.AVPlayArrow, icons.AVStop, pb.playHint, pb.stopHint) - recordBtnStyle := ToggleIcon(gtx, th, pb.RecordBtn, icons.AVFiberManualRecord, icons.AVFiberSmartRecord, pb.recordHint, pb.stopRecordHint) - noteTrackBtnStyle := ToggleIcon(gtx, th, pb.FollowBtn, icons.ActionSpeakerNotesOff, icons.ActionSpeakerNotes, pb.followOffHint, pb.followOnHint) - loopBtnStyle := ToggleIcon(gtx, th, pb.LoopBtn, icons.NavigationArrowForward, icons.AVLoop, pb.loopOffHint, pb.loopOnHint) - +func (pb *PlayBar) Layout(gtx C, tr *Tracker) D { return Surface{Gray: 37}.Layout(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, - layout.Flexed(1, playBtnStyle.Layout), - layout.Rigid(rewindBtnStyle.Layout), - layout.Rigid(recordBtnStyle.Layout), - layout.Rigid(noteTrackBtnStyle.Layout), - layout.Rigid(loopBtnStyle.Layout), + layout.Flexed(1, ToggleIconBtn(tr.Playing(), tr.Theme, pb.PlayingBtn, icons.AVPlayArrow, icons.AVStop, pb.playHint, pb.stopHint)), + layout.Rigid(ActionIconBtn(tr.PlaySongStart(), tr.Theme, pb.RewindBtn, icons.AVFastRewind, pb.rewindHint)), + layout.Rigid(ToggleIconBtn(tr.IsRecording(), tr.Theme, pb.RecordBtn, icons.AVFiberManualRecord, icons.AVFiberSmartRecord, pb.recordHint, pb.stopRecordHint)), + layout.Rigid(ToggleIconBtn(tr.Follow(), tr.Theme, pb.FollowBtn, icons.ActionSpeakerNotesOff, icons.ActionSpeakerNotes, pb.followOffHint, pb.followOnHint)), + layout.Rigid(ToggleIconBtn(tr.LoopToggle(), tr.Theme, pb.LoopBtn, icons.NavigationArrowForward, icons.AVLoop, pb.loopOffHint, pb.loopOnHint)), ) }) } diff --git a/tracker/gioui/theme.go b/tracker/gioui/theme.go index 1077fe3..a4a858c 100644 --- a/tracker/gioui/theme.go +++ b/tracker/gioui/theme.go @@ -19,6 +19,8 @@ type Theme struct { Disabled ButtonStyle Menu ButtonStyle } + IconButton IconButtonStyle + AddUnit IconButtonStyle Oscilloscope OscilloscopeStyle NumericUpDown NumericUpDownStyle DialogTitle LabelStyle diff --git a/tracker/gioui/theme.yml b/tracker/gioui/theme.yml index c47eb22..f182d1a 100644 --- a/tracker/gioui/theme.yml +++ b/tracker/gioui/theme.yml @@ -54,6 +54,15 @@ button: cornerradius: 0 height: *buttonheight inset: *buttoninset +iconbutton: + color: *primarycolor + size: 24 + inset: { top: 6, bottom: 6, left: 6, right: 6 } +addunit: + color: *contrastfg + background: *primarycolor + size: 24 + inset: { top: 6, bottom: 6, left: 6, right: 6 } oscilloscope: curvecolors: [*primarycolor, *secondarycolor] limitcolor: { r: 255, g: 255, b: 255, a: 8 } diff --git a/tracker/gioui/tracker.go b/tracker/gioui/tracker.go index 1f58467..d24d16e 100644 --- a/tracker/gioui/tracker.go +++ b/tracker/gioui/tracker.go @@ -210,7 +210,7 @@ func (t *Tracker) Layout(gtx layout.Context, w *app.Window) { paint.Fill(gtx.Ops, t.Theme.Material.Bg) event.Op(gtx.Ops, t) // area for capturing scroll events - if t.InstrumentEditor.enlargeBtn.Bool.Value() { + if t.InstrEnlarged().Value() { t.layoutTop(gtx) } else { t.VerticalSplit.Layout(gtx, diff --git a/tracker/gioui/unit_editor.go b/tracker/gioui/unit_editor.go index 602a914..6973e10 100644 --- a/tracker/gioui/unit_editor.go +++ b/tracker/gioui/unit_editor.go @@ -30,10 +30,10 @@ type UnitEditor struct { sliderList *DragList searchList *DragList Parameters []*ParameterWidget - DeleteUnitBtn *ActionClickable - CopyUnitBtn *TipClickable - ClearUnitBtn *ActionClickable - DisableUnitBtn *BoolClickable + DeleteUnitBtn *ClickableTip + CopyUnitBtn *ClickableTip + ClearUnitBtn *ClickableTip + DisableUnitBtn *ClickableTip SelectTypeBtn *Clickable commentEditor *Editor caser cases.Caser @@ -45,10 +45,10 @@ type UnitEditor struct { func NewUnitEditor(m *tracker.Model) *UnitEditor { ret := &UnitEditor{ - DeleteUnitBtn: NewActionClickable(m.DeleteUnit()), - ClearUnitBtn: NewActionClickable(m.ClearUnit()), - DisableUnitBtn: NewBoolClickable(m.UnitDisabled()), - CopyUnitBtn: new(TipClickable), + DeleteUnitBtn: new(ClickableTip), + ClearUnitBtn: new(ClickableTip), + DisableUnitBtn: new(ClickableTip), + CopyUnitBtn: new(ClickableTip), SelectTypeBtn: new(Clickable), commentEditor: NewEditor(true, true, text.Start), sliderList: NewDragList(m.Params().List(), layout.Vertical), @@ -133,9 +133,6 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D { t.Alerts().Add("Unit copied to clipboard", tracker.Info) } } - copyUnitBtnStyle := TipIcon(t.Theme, pe.CopyUnitBtn, icons.ContentContentCopy, pe.copyHint) - deleteUnitBtnStyle := ActionIcon(gtx, t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)") - disableUnitBtnStyle := ToggleIcon(gtx, t.Theme, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, pe.disableUnitHint, pe.enableUnitHint) text := t.Units().SelectedType() if text == "" { text = "Choose unit type" @@ -144,14 +141,13 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D { } hintText := Label(t.Theme, &t.Theme.UnitEditor.Hint, text) return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, - layout.Rigid(deleteUnitBtnStyle.Layout), - layout.Rigid(copyUnitBtnStyle.Layout), - layout.Rigid(disableUnitBtnStyle.Layout), + layout.Rigid(ActionIconBtn(t.DeleteUnit(), t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)")), + layout.Rigid(TipIconBtn(t.Theme, &t.Theme.IconButton, pe.CopyUnitBtn, icons.ContentContentCopy, pe.copyHint, true).Layout), + layout.Rigid(ToggleIconBtn(t.UnitDisabled(), t.Theme, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, pe.disableUnitHint, pe.enableUnitHint)), layout.Rigid(func(gtx C) D { var dims D if t.Units().SelectedType() != "" { - clearUnitBtnStyle := ActionIcon(gtx, t.Theme, pe.ClearUnitBtn, icons.ContentClear, "Clear unit") - dims = clearUnitBtnStyle.Layout(gtx) + dims = ActionIconBtn(t.ClearUnit(), t.Theme, pe.ClearUnitBtn, icons.ContentClear, "Clear unit")(gtx) } return D{Size: image.Pt(gtx.Dp(unit.Dp(48)), dims.Size.Y)} }),