mirror of
https://github.com/vsariola/sointu.git
synced 2025-07-22 15:04:36 -04:00
drafting new buttons
This commit is contained in:
parent
f587c5d865
commit
64ec5f17f3
@ -30,8 +30,8 @@ type (
|
||||
}
|
||||
|
||||
ClickableTip struct {
|
||||
Clickable Clickable
|
||||
TipArea component.TipArea
|
||||
Clickable
|
||||
component.TipArea
|
||||
}
|
||||
|
||||
ButtonStyle struct {
|
||||
@ -54,186 +54,237 @@ type (
|
||||
Inset layout.Inset
|
||||
}
|
||||
|
||||
TipIconButton struct {
|
||||
th *Theme
|
||||
st *IconButtonStyle
|
||||
t *ClickableTip
|
||||
icon []byte
|
||||
tip string
|
||||
enabled bool
|
||||
Button struct {
|
||||
Theme *Theme
|
||||
Style ButtonStyle
|
||||
Text string
|
||||
Tip string
|
||||
*ClickableTip
|
||||
}
|
||||
|
||||
ActionButton struct {
|
||||
act tracker.Action
|
||||
DisabledStyle ButtonStyle
|
||||
Button
|
||||
}
|
||||
|
||||
ToggleButton struct {
|
||||
b tracker.Bool
|
||||
DisabledStyle ButtonStyle
|
||||
OffStyle ButtonStyle
|
||||
Button
|
||||
}
|
||||
|
||||
IconButton struct {
|
||||
Theme *Theme
|
||||
Style IconButtonStyle
|
||||
Icon *widget.Icon
|
||||
Tip string
|
||||
*ClickableTip
|
||||
}
|
||||
|
||||
ActionIconButton struct {
|
||||
act tracker.Action
|
||||
DisabledStyle IconButtonStyle
|
||||
IconButton
|
||||
}
|
||||
|
||||
ToggleIconButton struct {
|
||||
b tracker.Bool
|
||||
DisabledStyle IconButtonStyle
|
||||
OffIcon *widget.Icon
|
||||
OffTip string
|
||||
IconButton
|
||||
}
|
||||
)
|
||||
|
||||
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 Btn(th *Theme, st *ButtonStyle, b *ClickableTip, txt string, tip string) Button {
|
||||
return Button{
|
||||
Theme: th,
|
||||
Style: *st,
|
||||
ClickableTip: b,
|
||||
Text: txt,
|
||||
Tip: tip,
|
||||
}
|
||||
}
|
||||
|
||||
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 ActionBtn(act tracker.Action, th *Theme, b *ClickableTip, txt string, tip string) ActionButton {
|
||||
return ActionButton{
|
||||
act: act,
|
||||
DisabledStyle: th.Button.Disabled,
|
||||
Button: Btn(th, &th.Button.Text, b, txt, tip),
|
||||
}
|
||||
}
|
||||
|
||||
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 ToggleBtn(b tracker.Bool, th *Theme, c *ClickableTip, text string, tip string) ToggleButton {
|
||||
return ToggleButton{
|
||||
b: b,
|
||||
DisabledStyle: th.Button.Disabled,
|
||||
OffStyle: th.Button.Text,
|
||||
Button: Btn(th, &th.Button.Filled, c, text, tip),
|
||||
}
|
||||
}
|
||||
|
||||
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 IconBtn(th *Theme, st *IconButtonStyle, b *ClickableTip, icon []byte, tip string) IconButton {
|
||||
return IconButton{
|
||||
Theme: th,
|
||||
Style: *st,
|
||||
ClickableTip: b,
|
||||
Icon: th.Icon(icon),
|
||||
Tip: tip,
|
||||
}
|
||||
}
|
||||
|
||||
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 ActionIconBtn(act tracker.Action, th *Theme, b *ClickableTip, icon []byte, tip string) ActionIconButton {
|
||||
return ActionIconButton{
|
||||
act: act,
|
||||
DisabledStyle: th.IconButton.Disabled,
|
||||
IconButton: IconBtn(th, &th.IconButton.Enabled, b, icon, tip),
|
||||
}
|
||||
}
|
||||
|
||||
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 ToggleIconBtn(b tracker.Bool, th *Theme, c *ClickableTip, offIcon, onIcon []byte, offTip, onTip string) ToggleIconButton {
|
||||
return ToggleIconButton{
|
||||
b: b,
|
||||
DisabledStyle: th.IconButton.Disabled,
|
||||
OffIcon: th.Icon(offIcon),
|
||||
OffTip: offTip,
|
||||
IconButton: IconBtn(th, &th.IconButton.Enabled, c, onIcon, onTip),
|
||||
}
|
||||
}
|
||||
|
||||
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 (b *Button) Layout(gtx C) D {
|
||||
if b.Tip != "" {
|
||||
return b.ClickableTip.TipArea.Layout(gtx, Tooltip(b.Theme, b.Tip), b.actualLayout)
|
||||
}
|
||||
return b.actualLayout(gtx)
|
||||
}
|
||||
|
||||
func (b *Button) actualLayout(gtx C) D {
|
||||
min := gtx.Constraints.Min
|
||||
min.Y = gtx.Dp(b.Style.Height)
|
||||
return b.Clickable.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.Style.CornerRadius)
|
||||
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop()
|
||||
background := b.Style.Background
|
||||
switch {
|
||||
case b.Clickable.Hovered():
|
||||
background = hoveredColor(background)
|
||||
}
|
||||
paint.Fill(gtx.Ops, background)
|
||||
for _, c := range b.Clickable.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.Style.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
colMacro := op.Record(gtx.Ops)
|
||||
paint.ColorOp{Color: b.Style.Color}.Add(gtx.Ops)
|
||||
return widget.Label{Alignment: text.Middle}.Layout(gtx, b.Theme.Material.Shaper, b.Style.Font, b.Style.TextSize, b.Text, 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
|
||||
func (b *ActionButton) Layout(gtx C) D {
|
||||
for b.Clickable.Clicked(gtx) {
|
||||
b.act.Do()
|
||||
}
|
||||
if !b.act.Enabled() {
|
||||
b.Style = b.DisabledStyle
|
||||
}
|
||||
return b.Button.Layout(gtx)
|
||||
}
|
||||
|
||||
func (b *ToggleButton) Layout(gtx C) D {
|
||||
for b.Clickable.Clicked(gtx) {
|
||||
b.b.Toggle()
|
||||
}
|
||||
if !b.b.Enabled() {
|
||||
b.Style = b.DisabledStyle
|
||||
} else if !b.b.Value() {
|
||||
b.Style = b.OffStyle
|
||||
}
|
||||
return b.Button.Layout(gtx)
|
||||
}
|
||||
|
||||
func (i *IconButton) Layout(gtx C) D {
|
||||
if i.Tip != "" {
|
||||
return i.ClickableTip.TipArea.Layout(gtx, Tooltip(i.Theme, i.Tip), i.actualLayout)
|
||||
}
|
||||
return i.actualLayout(gtx)
|
||||
}
|
||||
|
||||
func (i *IconButton) actualLayout(gtx C) D {
|
||||
m := op.Record(gtx.Ops)
|
||||
dims := i.Clickable.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 := i.Style.Background
|
||||
switch {
|
||||
case i.Clickable.Hovered():
|
||||
background = hoveredColor(background)
|
||||
}
|
||||
paint.Fill(gtx.Ops, background)
|
||||
for _, c := range i.Clickable.History() {
|
||||
drawInk(gtx, (widget.Press)(c))
|
||||
}
|
||||
return layout.Dimensions{Size: gtx.Constraints.Min}
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return i.Style.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
size := gtx.Dp(i.Style.Size)
|
||||
if i.Icon != nil {
|
||||
gtx.Constraints.Min = image.Point{X: size}
|
||||
i.Icon.Layout(gtx, i.Style.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 (i *ActionIconButton) Layout(gtx C) D {
|
||||
for i.Clickable.Clicked(gtx) {
|
||||
i.act.Do()
|
||||
}
|
||||
if !i.act.Enabled() {
|
||||
i.Style = i.DisabledStyle
|
||||
}
|
||||
return i.IconButton.Layout(gtx)
|
||||
}
|
||||
|
||||
func (i *ToggleIconButton) Layout(gtx C) D {
|
||||
for i.Clickable.Clicked(gtx) {
|
||||
i.b.Toggle()
|
||||
}
|
||||
if !i.b.Enabled() {
|
||||
i.Style = i.DisabledStyle
|
||||
}
|
||||
if !i.b.Value() {
|
||||
i.Icon = i.OffIcon
|
||||
i.Tip = i.OffTip
|
||||
}
|
||||
return i.IconButton.Layout(gtx)
|
||||
}
|
||||
|
||||
func Tooltip(th *Theme, tip string) component.Tooltip {
|
||||
|
@ -159,13 +159,16 @@ 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, ToggleIconBtn(t.Model.LinkInstrTrack(), t.Theme, ie.linkInstrTrackBtn, icons.NotificationSyncDisabled, icons.NotificationSync, ie.linkDisabledHint, ie.linkEnabledHint))
|
||||
linkInstrTrack := ToggleIconBtn(t.Model.LinkInstrTrack(), t.Theme, ie.linkInstrTrackBtn, icons.NotificationSyncDisabled, icons.NotificationSync, ie.linkDisabledHint, ie.linkEnabledHint)
|
||||
return layout.E.Layout(gtx, linkInstrTrack.Layout)
|
||||
}),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return layout.E.Layout(gtx, ToggleIconBtn(t.Model.InstrEnlarged(), t.Theme, ie.enlargeBtn, icons.NavigationFullscreen, icons.NavigationFullscreenExit, ie.enlargeHint, ie.shrinkHint))
|
||||
instrEnlarged := ToggleIconBtn(t.Model.InstrEnlarged(), t.Theme, ie.enlargeBtn, icons.NavigationFullscreen, icons.NavigationFullscreenExit, ie.enlargeHint, ie.shrinkHint)
|
||||
return layout.E.Layout(gtx, instrEnlarged.Layout)
|
||||
}),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return layout.E.Layout(gtx, ActionIconBtn(t.Model.AddInstrument(), t.Theme, ie.newInstrumentBtn, icons.ContentAdd, ie.addInstrumentHint))
|
||||
addInstrumetn := ActionIconBtn(t.Model.AddInstrument(), t.Theme, ie.newInstrumentBtn, icons.ContentAdd, ie.addInstrumentHint)
|
||||
return layout.E.Layout(gtx, addInstrumetn.Layout)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
@ -212,6 +215,15 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
||||
t.LoadInstrument(reader)
|
||||
}
|
||||
|
||||
splitInstrument := ActionIconBtn(t.SplitInstrument(), t.Theme, ie.splitInstrumentBtn, icons.CommunicationCallSplit, ie.splitInstrumentHint)
|
||||
commentExpanded := ToggleIconBtn(t.CommentExpanded(), t.Theme, ie.commentExpandBtn, icons.NavigationExpandMore, icons.NavigationExpandLess, ie.expandCommentHint, ie.collapseCommentHint)
|
||||
solo := ToggleIconBtn(t.Solo(), t.Theme, ie.soloBtn, icons.SocialGroup, icons.SocialPerson, ie.soloHint, ie.unsoloHint)
|
||||
mute := ToggleIconBtn(t.Mute(), t.Theme, ie.muteBtn, icons.AVVolumeUp, icons.AVVolumeOff, ie.muteHint, ie.unmuteHint)
|
||||
saveInstrument := IconBtn(t.Theme, &t.Theme.IconButton.Enabled, ie.saveInstrumentBtn, icons.ContentSave, "Save instrument")
|
||||
loadInstrument := IconBtn(t.Theme, &t.Theme.IconButton.Enabled, ie.loadInstrumentBtn, icons.FileFolderOpen, "Load instrument")
|
||||
copyInstrument := IconBtn(t.Theme, &t.Theme.IconButton.Enabled, ie.copyInstrumentBtn, icons.ContentContentCopy, "Copy instrument")
|
||||
deleteInstrument := ActionIconBtn(t.DeleteInstrument(), t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, ie.deleteInstrumentHint)
|
||||
|
||||
header := func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(layout.Spacer{Width: 6}.Layout),
|
||||
@ -220,23 +232,24 @@ 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(ActionIconBtn(t.SplitInstrument(), t.Theme, ie.splitInstrumentBtn, icons.CommunicationCallSplit, ie.splitInstrumentHint)),
|
||||
layout.Rigid(splitInstrument.Layout),
|
||||
layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }),
|
||||
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(commentExpanded.Layout),
|
||||
layout.Rigid(solo.Layout),
|
||||
layout.Rigid(mute.Layout),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
dims := TipIconBtn(t.Theme, &t.Theme.IconButton, ie.presetMenuBtn, icons.NavigationMenu, "Load preset", true).Layout(gtx)
|
||||
preset := IconBtn(t.Theme, &t.Theme.IconButton.Enabled, ie.presetMenuBtn, icons.NavigationMenu, "Load preset")
|
||||
dims := preset.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(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)),
|
||||
layout.Rigid(saveInstrument.Layout),
|
||||
layout.Rigid(loadInstrument.Layout),
|
||||
layout.Rigid(copyInstrument.Layout),
|
||||
layout.Rigid(deleteInstrument.Layout),
|
||||
)
|
||||
}
|
||||
|
||||
@ -467,7 +480,8 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
|
||||
t.AddUnit(false).Do()
|
||||
}
|
||||
margin := layout.Inset{Right: unit.Dp(20), Bottom: unit.Dp(1)}
|
||||
return margin.Layout(gtx, TipIconBtn(t.Theme, &t.Theme.AddUnit, ie.addUnitBtn, icons.ContentAdd, "Add unit (Enter)", true).Layout)
|
||||
addUnit := IconBtn(t.Theme, &t.Theme.IconButton.Emphasis, ie.addUnitBtn, icons.ContentAdd, "Add unit (Enter)")
|
||||
return margin.Layout(gtx, addUnit.Layout)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
@ -161,14 +161,15 @@ func PopupMenu(th *Theme, s *LabelStyle, menu *Menu) MenuStyle {
|
||||
}
|
||||
}
|
||||
|
||||
func (tr *Tracker) layoutMenu(gtx C, title string, clickable *Clickable, menu *Menu, width unit.Dp, items ...MenuItem) layout.Widget {
|
||||
for clickable.Clicked(gtx) {
|
||||
func (tr *Tracker) layoutMenu(gtx C, title string, ct *ClickableTip, menu *Menu, width unit.Dp, items ...MenuItem) layout.Widget {
|
||||
for ct.Clicked(gtx) {
|
||||
menu.Visible = true
|
||||
}
|
||||
m := PopupMenu(tr.Theme, &tr.Theme.Menu.Text, menu)
|
||||
return func(gtx C) D {
|
||||
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
|
||||
dims := Btn(tr.Theme, &tr.Theme.Button.Menu, clickable, title)(gtx)
|
||||
btn := Btn(tr.Theme, &tr.Theme.Button.Menu, ct, title, "")
|
||||
dims := btn.Layout(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))
|
||||
|
@ -183,22 +183,22 @@ func (te *NoteEditor) layoutButtons(gtx C, t *Tracker) D {
|
||||
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.Rigid(subtractSemitoneBtnStyle),
|
||||
layout.Rigid(addOctaveBtnStyle),
|
||||
layout.Rigid(subtractOctaveBtnStyle),
|
||||
layout.Rigid(noteOffBtnStyle),
|
||||
layout.Rigid(effectBtnStyle),
|
||||
layout.Rigid(uniqueBtnStyle),
|
||||
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(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.Rigid(midiInBtnStyle),
|
||||
layout.Rigid(splitTrackBtnStyle.Layout),
|
||||
layout.Rigid(midiInBtnStyle.Layout),
|
||||
layout.Flexed(1, func(gtx C) D { return layout.Dimensions{Size: gtx.Constraints.Min} }),
|
||||
layout.Rigid(deleteTrackBtnStyle),
|
||||
layout.Rigid(newTrackBtnStyle))
|
||||
layout.Rigid(deleteTrackBtnStyle.Layout),
|
||||
layout.Rigid(newTrackBtnStyle.Layout))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,9 @@ func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, once, wrap tr
|
||||
leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout
|
||||
rightSpacer := layout.Spacer{Width: unit.Dp(6)}.Layout
|
||||
|
||||
onceBtn := ToggleBtn(once, th, s.onceBtn, "Once", "Trigger once on next event")
|
||||
wrapBtn := ToggleBtn(wrap, th, s.wrapBtn, "Wrap", "Wrap buffer when full")
|
||||
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Flexed(1, func(gtx C) D { return s.layoutWave(gtx, wave, th) }),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
@ -56,7 +59,7 @@ func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, once, wrap tr
|
||||
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(ToggleBtn(once, th, s.onceBtn, "Once", "Trigger once on next event")),
|
||||
layout.Rigid(onceBtn.Layout),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return s.triggerChannelNumber.Layout(gtx, vtrig, th, &th.NumericUpDown, "Trigger channel")
|
||||
}),
|
||||
@ -68,7 +71,7 @@ func (s *OscilloscopeState) Layout(gtx C, vtrig, vlen tracker.Int, once, wrap tr
|
||||
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(ToggleBtn(wrap, th, s.wrapBtn, "Wrap", "Wrap buffer when full")),
|
||||
layout.Rigid(wrapBtn.Layout),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return s.lengthInBeatsNumber.Layout(gtx, vlen, th, &th.NumericUpDown, "Buffer length in beats")
|
||||
}),
|
||||
|
@ -22,8 +22,8 @@ type SongPanel struct {
|
||||
LoudnessExpander *Expander
|
||||
PeakExpander *Expander
|
||||
|
||||
WeightingTypeBtn *Clickable
|
||||
OversamplingBtn *Clickable
|
||||
WeightingTypeBtn *ClickableTip
|
||||
OversamplingBtn *ClickableTip
|
||||
|
||||
BPM *NumericUpDown
|
||||
RowsPerPattern *NumericUpDown
|
||||
@ -48,8 +48,8 @@ func NewSongPanel(model *tracker.Model) *SongPanel {
|
||||
MenuBar: NewMenuBar(model),
|
||||
PlayBar: NewPlayBar(),
|
||||
|
||||
WeightingTypeBtn: &Clickable{},
|
||||
OversamplingBtn: &Clickable{},
|
||||
WeightingTypeBtn: new(ClickableTip),
|
||||
OversamplingBtn: new(ClickableTip),
|
||||
|
||||
SongSettingsExpander: &Expander{Expanded: true},
|
||||
ScopeExpander: &Expander{},
|
||||
@ -98,13 +98,13 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
||||
weightingTxt = "No weight (RMS)"
|
||||
}
|
||||
|
||||
weightingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.WeightingTypeBtn, weightingTxt)
|
||||
weightingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.WeightingTypeBtn, weightingTxt, "")
|
||||
|
||||
oversamplingTxt := "Sample peak"
|
||||
if tr.Model.Oversampling().Value() {
|
||||
oversamplingTxt = "True peak"
|
||||
}
|
||||
oversamplingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.OversamplingBtn, oversamplingTxt)
|
||||
oversamplingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.OversamplingBtn, oversamplingTxt, "")
|
||||
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx C) D {
|
||||
@ -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(gtx)
|
||||
return weightingBtn.Layout(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(gtx)
|
||||
return oversamplingBtn.Layout(gtx)
|
||||
}),
|
||||
)
|
||||
},
|
||||
@ -296,7 +296,7 @@ func (e *Expander) layoutHeader(gtx C, th *Theme, title string, smallWidget layo
|
||||
}
|
||||
|
||||
type MenuBar struct {
|
||||
Clickables []Clickable
|
||||
Clickables []ClickableTip
|
||||
Menus []Menu
|
||||
|
||||
fileMenuItems []MenuItem
|
||||
@ -309,7 +309,7 @@ type MenuBar struct {
|
||||
|
||||
func NewMenuBar(model *tracker.Model) *MenuBar {
|
||||
ret := &MenuBar{
|
||||
Clickables: make([]Clickable, 3),
|
||||
Clickables: make([]ClickableTip, 3),
|
||||
Menus: make([]Menu, 3),
|
||||
PanicBtn: new(ClickableTip),
|
||||
panicHint: makeHint("Panic", " (%s)", "PanicToggle"),
|
||||
@ -343,12 +343,15 @@ 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 := ToggleIconBtn(tr.Panic(), tr.Theme, t.PanicBtn, icons.AlertErrorOutline, icons.AlertError, t.panicHint, t.panicHint)
|
||||
panicBtn := ToggleIconBtn(tr.Panic(), tr.Theme, t.PanicBtn, icons.AlertErrorOutline, icons.AlertError, t.panicHint, t.panicHint)
|
||||
if tr.Panic().Value() {
|
||||
panicBtn.Style = tr.Theme.IconButton.Error
|
||||
}
|
||||
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) })
|
||||
panicFC := layout.Flexed(1, func(gtx C) D { return layout.E.Layout(gtx, panicBtn.Layout) })
|
||||
if len(t.midiMenuItems) > 0 {
|
||||
return flex.Layout(gtx, fileFC, editFC, midiFC, panicFC)
|
||||
}
|
||||
@ -390,13 +393,19 @@ func NewPlayBar() *PlayBar {
|
||||
}
|
||||
|
||||
func (pb *PlayBar) Layout(gtx C, tr *Tracker) D {
|
||||
playBtn := ToggleIconBtn(tr.Playing(), tr.Theme, pb.PlayingBtn, icons.AVPlayArrow, icons.AVStop, pb.playHint, pb.stopHint)
|
||||
rewindBtn := ActionIconBtn(tr.PlaySongStart(), tr.Theme, pb.RewindBtn, icons.AVFastRewind, pb.rewindHint)
|
||||
recordBtn := ToggleIconBtn(tr.IsRecording(), tr.Theme, pb.RecordBtn, icons.AVFiberManualRecord, icons.AVFiberSmartRecord, pb.recordHint, pb.stopRecordHint)
|
||||
followBtn := ToggleIconBtn(tr.Follow(), tr.Theme, pb.FollowBtn, icons.ActionSpeakerNotesOff, icons.ActionSpeakerNotes, pb.followOffHint, pb.followOnHint)
|
||||
loopBtn := ToggleIconBtn(tr.LoopToggle(), tr.Theme, pb.LoopBtn, icons.NavigationArrowForward, icons.AVLoop, pb.loopOffHint, pb.loopOnHint)
|
||||
|
||||
return Surface{Gray: 37}.Layout(gtx, func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||
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)),
|
||||
layout.Flexed(1, playBtn.Layout),
|
||||
layout.Rigid(rewindBtn.Layout),
|
||||
layout.Rigid(recordBtn.Layout),
|
||||
layout.Rigid(followBtn.Layout),
|
||||
layout.Rigid(loopBtn.Layout),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -19,8 +19,12 @@ type Theme struct {
|
||||
Disabled ButtonStyle
|
||||
Menu ButtonStyle
|
||||
}
|
||||
IconButton IconButtonStyle
|
||||
AddUnit IconButtonStyle
|
||||
IconButton struct {
|
||||
Enabled IconButtonStyle
|
||||
Disabled IconButtonStyle
|
||||
Emphasis IconButtonStyle
|
||||
Error IconButtonStyle
|
||||
}
|
||||
Oscilloscope OscilloscopeStyle
|
||||
NumericUpDown NumericUpDownStyle
|
||||
DialogTitle LabelStyle
|
||||
|
@ -55,14 +55,23 @@ button:
|
||||
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 }
|
||||
enabled:
|
||||
color: *primarycolor
|
||||
size: 24
|
||||
inset: { top: 6, bottom: 6, left: 6, right: 6 }
|
||||
disabled:
|
||||
color: *disabled
|
||||
size: 24
|
||||
inset: { top: 6, bottom: 6, left: 6, right: 6 }
|
||||
emphasis:
|
||||
color: *contrastfg
|
||||
background: *primarycolor
|
||||
size: 24
|
||||
inset: { top: 6, bottom: 6, left: 6, right: 6 }
|
||||
error:
|
||||
color: *errorcolor
|
||||
size: 24
|
||||
inset: { top: 6, bottom: 6, left: 6, right: 6 }
|
||||
oscilloscope:
|
||||
curvecolors: [*primarycolor, *secondarycolor]
|
||||
limitcolor: { r: 255, g: 255, b: 255, a: 8 }
|
||||
|
@ -140,14 +140,19 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
|
||||
text = pe.caser.String(text)
|
||||
}
|
||||
hintText := Label(t.Theme, &t.Theme.UnitEditor.Hint, text)
|
||||
deleteUnit := ActionIconBtn(t.DeleteUnit(), t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)")
|
||||
copyUnit := IconBtn(t.Theme, &t.Theme.IconButton.Enabled, pe.CopyUnitBtn, icons.ContentContentCopy, pe.copyHint)
|
||||
disableUnit := ToggleIconBtn(t.UnitDisabled(), t.Theme, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, pe.disableUnitHint, pe.enableUnitHint)
|
||||
|
||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||
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(deleteUnit.Layout),
|
||||
layout.Rigid(copyUnit.Layout),
|
||||
layout.Rigid(disableUnit.Layout),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
var dims D
|
||||
if t.Units().SelectedType() != "" {
|
||||
dims = ActionIconBtn(t.ClearUnit(), t.Theme, pe.ClearUnitBtn, icons.ContentClear, "Clear unit")(gtx)
|
||||
clearUnit := ActionIconBtn(t.ClearUnit(), t.Theme, pe.ClearUnitBtn, icons.ContentClear, "Clear unit")
|
||||
dims = clearUnit.Layout(gtx)
|
||||
}
|
||||
return D{Size: image.Pt(gtx.Dp(unit.Dp(48)), dims.Size.Y)}
|
||||
}),
|
||||
@ -218,9 +223,9 @@ func (pe *UnitEditor) command(e key.Event, t *Tracker) {
|
||||
type ParameterWidget struct {
|
||||
floatWidget widget.Float
|
||||
boolWidget widget.Bool
|
||||
instrBtn Clickable
|
||||
instrBtn ClickableTip
|
||||
instrMenu Menu
|
||||
unitBtn Clickable
|
||||
unitBtn ClickableTip
|
||||
unitMenu Menu
|
||||
Parameter tracker.Parameter
|
||||
tipArea component.TipArea
|
||||
|
Reference in New Issue
Block a user