This commit is contained in:
5684185+vsariola@users.noreply.github.com
2025-05-20 10:15:39 +03:00
parent d96c2ea945
commit 80797b023c
14 changed files with 228 additions and 217 deletions

View File

@ -17,7 +17,6 @@ import (
"gioui.org/text" "gioui.org/text"
"gioui.org/unit" "gioui.org/unit"
"gioui.org/widget" "gioui.org/widget"
"gioui.org/widget/material"
"gioui.org/x/component" "gioui.org/x/component"
"github.com/vsariola/sointu/tracker" "github.com/vsariola/sointu/tracker"
) )
@ -58,28 +57,28 @@ func NewBoolClickable(b tracker.Bool) *BoolClickable {
} }
} }
func Tooltip(th *material.Theme, tip string) component.Tooltip { func Tooltip(th *Theme, tip string) component.Tooltip {
tooltip := component.PlatformTooltip(th, tip) tooltip := component.PlatformTooltip(&th.Material, tip)
tooltip.Bg = black tooltip.Bg = th.Tooltip.Bg
tooltip.Text.Color = white tooltip.Text.Color = th.Tooltip.Color
return tooltip return tooltip
} }
func ActionIcon(gtx C, th *material.Theme, w *ActionClickable, icon []byte, tip string) TipIconButtonStyle { func ActionIcon(gtx C, th *Theme, w *ActionClickable, icon []byte, tip string) TipIconButtonStyle {
ret := TipIcon(th, &w.TipClickable, icon, tip) ret := TipIcon(th, &w.TipClickable, icon, tip)
for w.Clickable.Clicked(gtx) { for w.Clickable.Clicked(gtx) {
w.Action.Do() w.Action.Do()
} }
if !w.Action.Allowed() { if !w.Action.Allowed() {
ret.IconButtonStyle.Color = disabledTextColor ret.IconButtonStyle.Color = th.Button.Disabled.Color
} }
return ret return ret
} }
func TipIcon(th *material.Theme, w *TipClickable, icon []byte, tip string) TipIconButtonStyle { func TipIcon(th *Theme, w *TipClickable, icon []byte, tip string) TipIconButtonStyle {
iconButtonStyle := IconButton(th, &w.Clickable, widgetForIcon(icon), "") iconButtonStyle := IconButton(th, &w.Clickable, widgetForIcon(icon), "")
iconButtonStyle.Color = th.Palette.ContrastBg iconButtonStyle.Color = th.Material.Palette.ContrastBg
iconButtonStyle.Background = transparent iconButtonStyle.Background = color.NRGBA{}
iconButtonStyle.Inset = layout.UniformInset(unit.Dp(6)) iconButtonStyle.Inset = layout.UniformInset(unit.Dp(6))
return TipIconButtonStyle{ return TipIconButtonStyle{
TipArea: &w.TipArea, TipArea: &w.TipArea,
@ -88,7 +87,7 @@ func TipIcon(th *material.Theme, w *TipClickable, icon []byte, tip string) TipIc
} }
} }
func ToggleIcon(gtx C, th *material.Theme, w *BoolClickable, offIcon, onIcon []byte, offTip, onTip string) TipIconButtonStyle { func ToggleIcon(gtx C, th *Theme, w *BoolClickable, offIcon, onIcon []byte, offTip, onTip string) TipIconButtonStyle {
icon := offIcon icon := offIcon
tip := offTip tip := offTip
if w.Bool.Value() { if w.Bool.Value() {
@ -99,11 +98,11 @@ func ToggleIcon(gtx C, th *material.Theme, w *BoolClickable, offIcon, onIcon []b
w.Bool.Toggle() w.Bool.Toggle()
} }
ibStyle := IconButton(th, &w.Clickable, widgetForIcon(icon), "") ibStyle := IconButton(th, &w.Clickable, widgetForIcon(icon), "")
ibStyle.Background = transparent ibStyle.Background = color.NRGBA{}
ibStyle.Inset = layout.UniformInset(unit.Dp(6)) ibStyle.Inset = layout.UniformInset(unit.Dp(6))
ibStyle.Color = th.Palette.ContrastBg ibStyle.Color = th.Material.Palette.ContrastBg
if !w.Bool.Enabled() { if !w.Bool.Enabled() {
ibStyle.Color = disabledTextColor ibStyle.Color = th.Button.Disabled.Color
} }
return TipIconButtonStyle{ return TipIconButtonStyle{
TipArea: &w.TipArea, TipArea: &w.TipArea,
@ -121,7 +120,7 @@ func ActionButton(gtx C, th *Theme, style *ButtonStyle, w *ActionClickable, text
w.Action.Do() w.Action.Do()
} }
if !w.Action.Allowed() { if !w.Action.Allowed() {
return Btn(th, &th.DisabledButton, &w.Clickable, text) return Btn(th, &th.Button.Disabled, &w.Clickable, text)
} }
return Btn(th, style, &w.Clickable, text) return Btn(th, style, &w.Clickable, text)
} }
@ -131,12 +130,12 @@ func ToggleButton(gtx C, th *Theme, b *BoolClickable, text string) Button {
b.Bool.Toggle() b.Bool.Toggle()
} }
if !b.Bool.Enabled() { if !b.Bool.Enabled() {
return Btn(th, &th.DisabledButton, &b.Clickable, text) return Btn(th, &th.Button.Disabled, &b.Clickable, text)
} }
if b.Bool.Value() { if b.Bool.Value() {
return Btn(th, &th.FilledButton, &b.Clickable, text) return Btn(th, &th.Button.Filled, &b.Clickable, text)
} }
return Btn(th, &th.TextButton, &b.Clickable, text) return Btn(th, &th.Button.Text, &b.Clickable, text)
} }
// Clickable represents a clickable area. // Clickable represents a clickable area.
@ -293,10 +292,10 @@ func Btn(th *Theme, style *ButtonStyle, button *Clickable, txt string) Button {
return b return b
} }
func IconButton(th *material.Theme, button *Clickable, icon *widget.Icon, description string) IconButtonStyle { func IconButton(th *Theme, button *Clickable, icon *widget.Icon, description string) IconButtonStyle {
return IconButtonStyle{ return IconButtonStyle{
Background: th.Palette.ContrastBg, Background: th.Material.Palette.ContrastBg,
Color: th.Palette.ContrastFg, Color: th.Material.Palette.ContrastFg,
Icon: icon, Icon: icon,
Size: 24, Size: 24,
Inset: layout.UniformInset(12), Inset: layout.UniformInset(12),

View File

@ -1,6 +1,8 @@
package gioui package gioui
import ( import (
"image/color"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op/paint" "gioui.org/op/paint"
@ -49,7 +51,7 @@ func ConfirmDialog(gtx C, th *Theme, dialog *Dialog, title, text string) DialogS
Theme: th, Theme: th,
} }
for _, b := range [...]*material.ButtonStyle{&ret.AltStyle, &ret.OkStyle, &ret.CancelStyle} { for _, b := range [...]*material.ButtonStyle{&ret.AltStyle, &ret.OkStyle, &ret.CancelStyle} {
b.Background = transparent b.Background = color.NRGBA{}
b.Inset = layout.UniformInset(unit.Dp(6)) b.Inset = layout.UniformInset(unit.Dp(6))
b.Color = th.Material.Palette.Fg b.Color = th.Material.Palette.Fg
} }
@ -108,7 +110,7 @@ func (d *DialogStyle) Layout(gtx C) D {
paint.Fill(gtx.Ops, d.Theme.Dialog.Bg) paint.Fill(gtx.Ops, d.Theme.Dialog.Bg)
visible := true visible := true
return layout.Center.Layout(gtx, func(gtx C) D { return layout.Center.Layout(gtx, func(gtx C) D {
return Popup(&visible).Layout(gtx, func(gtx C) D { return Popup(d.Theme, &visible).Layout(gtx, func(gtx C) D {
return d.Inset.Layout(gtx, func(gtx C) D { return d.Inset.Layout(gtx, func(gtx C) D {
return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx, return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(Label(d.Theme, &d.Theme.Dialog.Title, d.Title).Layout), layout.Rigid(Label(d.Theme, &d.Theme.Dialog.Title, d.Title).Layout),

View File

@ -15,7 +15,6 @@ import (
"gioui.org/op" "gioui.org/op"
"gioui.org/op/clip" "gioui.org/op/clip"
"gioui.org/op/paint" "gioui.org/op/paint"
"gioui.org/unit"
"github.com/vsariola/sointu/tracker" "github.com/vsariola/sointu/tracker"
) )
@ -33,18 +32,12 @@ type DragList struct {
} }
type FilledDragListStyle struct { type FilledDragListStyle struct {
dragList *DragList dragList *DragList
HoverColor color.NRGBA HoverColor color.NRGBA
Cursor struct { Cursor CursorStyle
Active color.NRGBA Selection CursorStyle
Inactive color.NRGBA ScrollBar ScrollBarStyle
} element, bg func(gtx C, i int) D
Selection struct {
Active color.NRGBA
Inactive color.NRGBA
}
ScrollBarWidth unit.Dp
element, bg func(gtx C, i int) D
} }
func NewDragList(model tracker.List, axis layout.Axis) *DragList { func NewDragList(model tracker.List, axis layout.Axis) *DragList {
@ -53,13 +46,13 @@ func NewDragList(model tracker.List, axis layout.Axis) *DragList {
func FilledDragList(th *Theme, dragList *DragList, element, bg func(gtx C, i int) D) FilledDragListStyle { func FilledDragList(th *Theme, dragList *DragList, element, bg func(gtx C, i int) D) FilledDragListStyle {
return FilledDragListStyle{ return FilledDragListStyle{
dragList: dragList, dragList: dragList,
element: element, element: element,
bg: bg, bg: bg,
HoverColor: hoveredColor(th.Selection.Active), HoverColor: hoveredColor(th.Selection.Active),
Cursor: th.Cursor, Cursor: th.Cursor,
Selection: th.Selection, Selection: th.Selection,
ScrollBarWidth: unit.Dp(10), ScrollBar: th.ScrollBar,
} }
} }
@ -72,7 +65,7 @@ func (d *DragList) Focused() bool {
} }
func (s FilledDragListStyle) LayoutScrollBar(gtx C) D { func (s FilledDragListStyle) LayoutScrollBar(gtx C) D {
return s.dragList.ScrollBar.Layout(gtx, s.ScrollBarWidth, s.dragList.TrackerList.Count(), &s.dragList.List.Position) return s.dragList.ScrollBar.Layout(gtx, &s.ScrollBar, s.dragList.TrackerList.Count(), &s.dragList.List.Position)
} }
func (s FilledDragListStyle) Layout(gtx C) D { func (s FilledDragListStyle) Layout(gtx C) D {

View File

@ -124,8 +124,8 @@ func (ie *InstrumentEditor) childFocused(gtx C) bool {
func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D { func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
ie.wasFocused = ie.Focused() || ie.childFocused(gtx) ie.wasFocused = ie.Focused() || ie.childFocused(gtx)
fullscreenBtnStyle := ToggleIcon(gtx, &t.Theme.Material, ie.enlargeBtn, icons.NavigationFullscreen, icons.NavigationFullscreenExit, ie.enlargeHint, ie.shrinkHint) fullscreenBtnStyle := ToggleIcon(gtx, t.Theme, ie.enlargeBtn, icons.NavigationFullscreen, icons.NavigationFullscreenExit, ie.enlargeHint, ie.shrinkHint)
linkBtnStyle := ToggleIcon(gtx, &t.Theme.Material, ie.linkInstrTrackBtn, icons.NotificationSyncDisabled, icons.NotificationSync, ie.linkDisabledHint, ie.linkEnabledHint) linkBtnStyle := ToggleIcon(gtx, t.Theme, ie.linkInstrTrackBtn, icons.NotificationSyncDisabled, icons.NotificationSync, ie.linkDisabledHint, ie.linkEnabledHint)
octave := func(gtx C) D { octave := func(gtx C) D {
in := layout.UniformInset(unit.Dp(1)) in := layout.UniformInset(unit.Dp(1))
@ -134,7 +134,7 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
return dims return dims
} }
newBtnStyle := ActionIcon(gtx, &t.Theme.Material, ie.newInstrumentBtn, icons.ContentAdd, ie.addInstrumentHint) newBtnStyle := ActionIcon(gtx, t.Theme, ie.newInstrumentBtn, icons.ContentAdd, ie.addInstrumentHint)
ret := layout.Flex{Axis: layout.Vertical}.Layout(gtx, ret := layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout( return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(
@ -175,17 +175,17 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D { func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
header := func(gtx C) D { header := func(gtx C) D {
commentExpandBtnStyle := ToggleIcon(gtx, &t.Theme.Material, ie.commentExpandBtn, icons.NavigationExpandMore, icons.NavigationExpandLess, ie.expandCommentHint, ie.collapseCommentHint) commentExpandBtnStyle := ToggleIcon(gtx, t.Theme, ie.commentExpandBtn, icons.NavigationExpandMore, icons.NavigationExpandLess, ie.expandCommentHint, ie.collapseCommentHint)
presetMenuBtnStyle := TipIcon(&t.Theme.Material, ie.presetMenuBtn, icons.NavigationMenu, "Load preset") presetMenuBtnStyle := TipIcon(t.Theme, ie.presetMenuBtn, icons.NavigationMenu, "Load preset")
copyInstrumentBtnStyle := TipIcon(&t.Theme.Material, ie.copyInstrumentBtn, icons.ContentContentCopy, "Copy instrument") copyInstrumentBtnStyle := TipIcon(t.Theme, ie.copyInstrumentBtn, icons.ContentContentCopy, "Copy instrument")
saveInstrumentBtnStyle := TipIcon(&t.Theme.Material, ie.saveInstrumentBtn, icons.ContentSave, "Save instrument") saveInstrumentBtnStyle := TipIcon(t.Theme, ie.saveInstrumentBtn, icons.ContentSave, "Save instrument")
loadInstrumentBtnStyle := TipIcon(&t.Theme.Material, ie.loadInstrumentBtn, icons.FileFolderOpen, "Load instrument") loadInstrumentBtnStyle := TipIcon(t.Theme, ie.loadInstrumentBtn, icons.FileFolderOpen, "Load instrument")
deleteInstrumentBtnStyle := ActionIcon(gtx, &t.Theme.Material, ie.deleteInstrumentBtn, icons.ActionDelete, ie.deleteInstrumentHint) deleteInstrumentBtnStyle := ActionIcon(gtx, t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, ie.deleteInstrumentHint)
splitInstrumentBtnStyle := ActionIcon(gtx, &t.Theme.Material, ie.splitInstrumentBtn, icons.CommunicationCallSplit, ie.splitInstrumentHint) splitInstrumentBtnStyle := ActionIcon(gtx, t.Theme, ie.splitInstrumentBtn, icons.CommunicationCallSplit, ie.splitInstrumentHint)
soloBtnStyle := ToggleIcon(gtx, &t.Theme.Material, ie.soloBtn, icons.SocialGroup, icons.SocialPerson, ie.soloHint, ie.unsoloHint) soloBtnStyle := ToggleIcon(gtx, t.Theme, ie.soloBtn, icons.SocialGroup, icons.SocialPerson, ie.soloHint, ie.unsoloHint)
muteBtnStyle := ToggleIcon(gtx, &t.Theme.Material, ie.muteBtn, icons.AVVolumeUp, icons.AVVolumeOff, ie.muteHint, ie.unmuteHint) muteBtnStyle := ToggleIcon(gtx, t.Theme, ie.muteBtn, icons.AVVolumeUp, icons.AVVolumeOff, ie.muteHint, ie.unmuteHint)
m := PopupMenu(t.Theme, &ie.presetMenu) m := PopupMenu(t.Theme, &t.Theme.Menu.Text, &ie.presetMenu)
for ie.copyInstrumentBtn.Clickable.Clicked(gtx) { for ie.copyInstrumentBtn.Clickable.Clicked(gtx) {
if contents, ok := t.Instruments().List().CopyElements(); ok { if contents, ok := t.Instruments().List().CopyElements(); ok {
@ -314,7 +314,7 @@ func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D {
} }
instrumentList := FilledDragList(t.Theme, ie.instrumentDragList, element, nil) instrumentList := FilledDragList(t.Theme, ie.instrumentDragList, element, nil)
instrumentList.ScrollBarWidth = unit.Dp(6) instrumentList.ScrollBar = t.Theme.InstrumentEditor.InstrumentList.ScrollBar
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop() defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops).Pop() defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops).Pop()
@ -351,7 +351,7 @@ func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D {
func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D { func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
// TODO: how to ie.unitDragList.Focus() // TODO: how to ie.unitDragList.Focus()
addUnitBtnStyle := ActionIcon(gtx, &t.Theme.Material, ie.addUnitBtn, icons.ContentAdd, "Add unit (Enter)") addUnitBtnStyle := ActionIcon(gtx, t.Theme, ie.addUnitBtn, icons.ContentAdd, "Add unit (Enter)")
addUnitBtnStyle.IconButtonStyle.Color = t.Theme.Material.ContrastFg addUnitBtnStyle.IconButtonStyle.Color = t.Theme.Material.ContrastFg
addUnitBtnStyle.IconButtonStyle.Background = t.Theme.Material.ContrastBg addUnitBtnStyle.IconButtonStyle.Background = t.Theme.Material.ContrastBg
addUnitBtnStyle.IconButtonStyle.Inset = layout.UniformInset(unit.Dp(4)) addUnitBtnStyle.IconButtonStyle.Inset = layout.UniformInset(unit.Dp(4))

View File

@ -26,13 +26,11 @@ type Menu struct {
type MenuStyle struct { type MenuStyle struct {
Menu *Menu Menu *Menu
Title string Title string
IconColor color.NRGBA
TextColor color.NRGBA
ShortCutColor color.NRGBA ShortCutColor color.NRGBA
FontSize unit.Sp
IconSize unit.Dp
HoverColor color.NRGBA HoverColor color.NRGBA
Theme *Theme Theme *Theme
LabelStyle LabelStyle
Disabled color.NRGBA
} }
type MenuItem struct { type MenuItem struct {
@ -99,21 +97,21 @@ func (m *MenuStyle) Layout(gtx C, items ...MenuItem) D {
macro = op.Record(gtx.Ops) macro = op.Record(gtx.Ops)
} }
icon := widgetForIcon(item.IconBytes) icon := widgetForIcon(item.IconBytes)
iconColor := m.IconColor iconColor := m.LabelStyle.Color
if !item.Doer.Allowed() {
iconColor = mediumEmphasisTextColor
}
iconInset := layout.Inset{Left: unit.Dp(12), Right: unit.Dp(6)} iconInset := layout.Inset{Left: unit.Dp(12), Right: unit.Dp(6)}
textLabel := Label(m.Theme, &m.Theme.Menu.Text, item.Text) textLabel := Label(m.Theme, &m.Theme.Menu.Text, item.Text)
shortcutLabel := Label(m.Theme, &m.Theme.Menu.Text, item.ShortcutText)
shortcutLabel.Color = m.ShortCutColor
if !item.Doer.Allowed() { if !item.Doer.Allowed() {
textLabel.Color = mediumEmphasisTextColor iconColor = m.Disabled
textLabel.Color = m.Disabled
shortcutLabel.Color = m.Disabled
} }
shortcutLabel := Label(m.Theme, &m.Theme.Menu.ShortCut, item.ShortcutText)
shortcutInset := layout.Inset{Left: unit.Dp(12), Right: unit.Dp(12), Bottom: unit.Dp(2), Top: unit.Dp(2)} shortcutInset := layout.Inset{Left: unit.Dp(12), Right: unit.Dp(12), Bottom: unit.Dp(2), Top: unit.Dp(2)}
dims := layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, dims := layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
return iconInset.Layout(gtx, func(gtx C) D { return iconInset.Layout(gtx, func(gtx C) D {
p := gtx.Dp(unit.Dp(m.IconSize)) p := gtx.Dp(unit.Dp(m.LabelStyle.TextSize))
gtx.Constraints.Min = image.Pt(p, p) gtx.Constraints.Min = image.Pt(p, p)
return icon.Layout(gtx, iconColor) return icon.Layout(gtx, iconColor)
}) })
@ -141,26 +139,24 @@ func (m *MenuStyle) Layout(gtx C, items ...MenuItem) D {
}) })
}), }),
layout.Expanded(func(gtx C) D { layout.Expanded(func(gtx C) D {
return m.Menu.scrollBar.Layout(gtx, unit.Dp(10), len(items), &m.Menu.list.Position) return m.Menu.scrollBar.Layout(gtx, &m.Theme.ScrollBar, len(items), &m.Menu.list.Position)
}), }),
) )
} }
popup := Popup(&m.Menu.Visible) popup := Popup(m.Theme, &m.Menu.Visible)
popup.NE = unit.Dp(0) popup.NE = unit.Dp(0)
popup.ShadowN = unit.Dp(0) popup.ShadowN = unit.Dp(0)
popup.NW = unit.Dp(0) popup.NW = unit.Dp(0)
return popup.Layout(gtx, contents) return popup.Layout(gtx, contents)
} }
func PopupMenu(th *Theme, menu *Menu) MenuStyle { func PopupMenu(th *Theme, s *LabelStyle, menu *Menu) MenuStyle {
return MenuStyle{ return MenuStyle{
Menu: menu, Menu: menu,
IconColor: white, ShortCutColor: th.Menu.ShortCut,
TextColor: white, LabelStyle: *s,
ShortCutColor: mediumEmphasisTextColor, HoverColor: th.Menu.Hover,
FontSize: unit.Sp(16), Disabled: th.Menu.Disabled,
IconSize: unit.Dp(16),
HoverColor: menuHoverColor,
Theme: th, Theme: th,
} }
} }
@ -169,12 +165,10 @@ func (tr *Tracker) layoutMenu(gtx C, title string, clickable *Clickable, menu *M
for clickable.Clicked(gtx) { for clickable.Clicked(gtx) {
menu.Visible = true menu.Visible = true
} }
m := PopupMenu(tr.Theme, menu) m := PopupMenu(tr.Theme, &tr.Theme.Menu.Text, menu)
return func(gtx C) D { return func(gtx C) D {
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop() defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
titleBtn := Btn(tr.Theme, &tr.Theme.MenuButton, clickable, title) titleBtn := Btn(tr.Theme, &tr.Theme.Button.Menu, clickable, title)
titleBtn.Color = white
titleBtn.Background = transparent
titleBtn.CornerRadius = unit.Dp(0) titleBtn.CornerRadius = unit.Dp(0)
dims := titleBtn.Layout(gtx) dims := titleBtn.Layout(gtx)
op.Offset(image.Pt(0, dims.Size.Y)).Add(gtx.Ops) op.Offset(image.Pt(0, dims.Size.Y)).Add(gtx.Ops)

View File

@ -150,21 +150,21 @@ func (te *NoteEditor) Layout(gtx layout.Context, t *Tracker) layout.Dimensions {
func (te *NoteEditor) layoutButtons(gtx C, t *Tracker) D { func (te *NoteEditor) layoutButtons(gtx C, t *Tracker) D {
return Surface{Gray: 37, Focus: te.scrollTable.Focused() || te.scrollTable.ChildFocused()}.Layout(gtx, func(gtx C) D { return Surface{Gray: 37, Focus: te.scrollTable.Focused() || te.scrollTable.ChildFocused()}.Layout(gtx, func(gtx C) D {
addSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.AddSemitoneBtn, "+1") addSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.AddSemitoneBtn, "+1")
subtractSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.SubtractSemitoneBtn, "-1") subtractSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.SubtractSemitoneBtn, "-1")
addOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.AddOctaveBtn, "+12") addOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.AddOctaveBtn, "+12")
subtractOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.SubtractOctaveBtn, "-12") subtractOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.SubtractOctaveBtn, "-12")
noteOffBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.TextButton, te.NoteOffBtn, "Note Off") noteOffBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.NoteOffBtn, "Note Off")
deleteTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.DeleteTrackBtn, icons.ActionDelete, te.deleteTrackHint) deleteTrackBtnStyle := ActionIcon(gtx, t.Theme, te.DeleteTrackBtn, icons.ActionDelete, te.deleteTrackHint)
splitTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.SplitTrackBtn, icons.CommunicationCallSplit, te.splitTrackHint) splitTrackBtnStyle := ActionIcon(gtx, t.Theme, te.SplitTrackBtn, icons.CommunicationCallSplit, te.splitTrackHint)
newTrackBtnStyle := ActionIcon(gtx, &t.Theme.Material, te.NewTrackBtn, icons.ContentAdd, te.addTrackHint) newTrackBtnStyle := ActionIcon(gtx, t.Theme, te.NewTrackBtn, icons.ContentAdd, te.addTrackHint)
in := layout.UniformInset(unit.Dp(1)) in := layout.UniformInset(unit.Dp(1))
voiceUpDown := func(gtx C) D { voiceUpDown := func(gtx C) D {
numStyle := NumUpDown(t.Theme, te.TrackVoices, "Track voices") numStyle := NumUpDown(t.Theme, te.TrackVoices, "Track voices")
return in.Layout(gtx, numStyle.Layout) return in.Layout(gtx, numStyle.Layout)
} }
effectBtnStyle := ToggleButton(gtx, t.Theme, te.EffectBtn, "Hex") effectBtnStyle := ToggleButton(gtx, t.Theme, te.EffectBtn, "Hex")
uniqueBtnStyle := ToggleIcon(gtx, &t.Theme.Material, te.UniqueBtn, icons.ToggleStarBorder, icons.ToggleStar, te.uniqueOffTip, te.uniqueOnTip) uniqueBtnStyle := ToggleIcon(gtx, t.Theme, te.UniqueBtn, icons.ToggleStarBorder, icons.ToggleStar, te.uniqueOffTip, te.uniqueOnTip)
midiInBtnStyle := ToggleButton(gtx, t.Theme, te.TrackMidiInBtn, "MIDI") midiInBtnStyle := ToggleButton(gtx, t.Theme, te.TrackMidiInBtn, "MIDI")
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, 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(func(gtx C) D { return layout.Dimensions{Size: image.Pt(gtx.Dp(unit.Dp(12)), 0)} }),
@ -230,12 +230,12 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D {
rowTitleBg := func(gtx C, j int) D { rowTitleBg := func(gtx C, j int) D {
if mod(j, beatMarkerDensity*2) == 0 { if mod(j, beatMarkerDensity*2) == 0 {
paint.FillShape(gtx.Ops, twoBeatHighlight, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, pxHeight)}.Op()) paint.FillShape(gtx.Ops, t.Theme.NoteEditor.TwoBeat, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, pxHeight)}.Op())
} else if mod(j, beatMarkerDensity) == 0 { } else if mod(j, beatMarkerDensity) == 0 {
paint.FillShape(gtx.Ops, oneBeatHighlight, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, pxHeight)}.Op()) paint.FillShape(gtx.Ops, t.Theme.NoteEditor.OneBeat, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, pxHeight)}.Op())
} }
if t.Model.Playing().Value() && j == playSongRow { if t.Model.Playing().Value() && j == playSongRow {
paint.FillShape(gtx.Ops, trackerPlayColor, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, pxHeight)}.Op()) paint.FillShape(gtx.Ops, t.Theme.NoteEditor.Play, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, pxHeight)}.Op())
} }
return D{} return D{}
} }
@ -288,7 +288,7 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D {
c = t.Theme.Cursor.Active c = t.Theme.Cursor.Active
} }
if hasTrackMidiIn { if hasTrackMidiIn {
c = cursorForTrackMidiInColor c = t.Theme.Cursor.ActiveAlt
} }
te.paintColumnCell(gtx, x, t, c) te.paintColumnCell(gtx, x, t, c)
} }
@ -296,7 +296,7 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D {
if hasTrackMidiIn { if hasTrackMidiIn {
for _, trackIndex := range t.Model.TracksWithSameInstrumentAsCurrent() { for _, trackIndex := range t.Model.TracksWithSameInstrumentAsCurrent() {
if x == trackIndex && y == cursor.Y { if x == trackIndex && y == cursor.Y {
te.paintColumnCell(gtx, x, t, cursorNeighborForTrackMidiInColor) te.paintColumnCell(gtx, x, t, t.Theme.Selection.ActiveAlt)
} }
} }
} }

View File

@ -60,7 +60,7 @@ func NumUpDown(th *Theme, number *NumberInput, tooltip string) NumericUpDown {
return NumericUpDown{ return NumericUpDown{
NumberInput: number, NumberInput: number,
Shaper: th.Material.Shaper, Shaper: th.Material.Shaper,
Tooltip: Tooltip(&th.Material, tooltip), Tooltip: Tooltip(th, tooltip),
NumericUpDownStyle: th.NumericUpDown, NumericUpDownStyle: th.NumericUpDown,
} }
} }

View File

@ -74,7 +74,7 @@ func (oe *OrderEditor) Layout(gtx C, t *Tracker) D {
rowTitleBg := func(gtx C, j int) D { rowTitleBg := func(gtx C, j int) D {
if t.Model.Playing().Value() && j == t.PlayPosition().OrderRow { if t.Model.Playing().Value() && j == t.PlayPosition().OrderRow {
paint.FillShape(gtx.Ops, patternPlayColor, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, gtx.Dp(patternCellHeight))}.Op()) paint.FillShape(gtx.Ops, t.Theme.OrderEditor.Play, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, gtx.Dp(patternCellHeight))}.Op())
} }
return D{} return D{}
} }
@ -98,7 +98,7 @@ func (oe *OrderEditor) Layout(gtx C, t *Tracker) D {
cell := func(gtx C, x, y int) D { cell := func(gtx C, x, y int) D {
val := patternIndexToString(t.Model.Order().Value(tracker.Point{X: x, Y: y})) val := patternIndexToString(t.Model.Order().Value(tracker.Point{X: x, Y: y}))
color := patternCellColor color := t.Theme.OrderEditor.CellBg
point := tracker.Point{X: x, Y: y} point := tracker.Point{X: x, Y: y}
if selection.Contains(point) { if selection.Contains(point) {
color = t.Theme.Selection.Inactive color = t.Theme.Selection.Inactive

View File

@ -24,11 +24,11 @@ type PopupStyle struct {
SE, SW, NW, NE unit.Dp SE, SW, NW, NE unit.Dp
} }
func Popup(visible *bool) PopupStyle { func Popup(th *Theme, visible *bool) PopupStyle {
return PopupStyle{ return PopupStyle{
Visible: visible, Visible: visible,
SurfaceColor: popupSurfaceColor, SurfaceColor: th.Popup.Bg,
ShadowColor: popupShadowColor, ShadowColor: th.Popup.Shadow,
ShadowN: unit.Dp(2), ShadowN: unit.Dp(2),
ShadowE: unit.Dp(2), ShadowE: unit.Dp(2),
ShadowS: unit.Dp(2), ShadowS: unit.Dp(2),

View File

@ -2,6 +2,7 @@ package gioui
import ( import (
"image" "image"
"image/color"
"gioui.org/f32" "gioui.org/f32"
"gioui.org/io/event" "gioui.org/io/event"
@ -21,19 +22,27 @@ type ScrollBar struct {
tag bool tag bool
} }
func (s *ScrollBar) Layout(gtx C, width unit.Dp, numItems int, pos *layout.Position) D { type ScrollBarStyle struct {
Color color.NRGBA
Width unit.Dp
Gradient color.NRGBA
}
func (s *ScrollBar) Layout(gtx C, style *ScrollBarStyle, numItems int, pos *layout.Position) D {
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop() defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
defer clip.Rect{Max: gtx.Constraints.Min}.Push(gtx.Ops).Pop() defer clip.Rect{Max: gtx.Constraints.Min}.Push(gtx.Ops).Pop()
gradientSize := gtx.Dp(unit.Dp(4)) gradientSize := gtx.Dp(unit.Dp(4))
var totalPixelsEstimate, scrollBarRelLength float32 var totalPixelsEstimate, scrollBarRelLength float32
transparent := style.Gradient
transparent.A = 0
switch s.Axis { switch s.Axis {
case layout.Vertical: case layout.Vertical:
if pos.First > 0 || pos.Offset > 0 { if pos.First > 0 || pos.Offset > 0 {
paint.LinearGradientOp{Color1: black, Color2: transparent, Stop2: f32.Pt(0, float32(gradientSize))}.Add(gtx.Ops) paint.LinearGradientOp{Color1: style.Gradient, Color2: transparent, Stop2: f32.Pt(0, float32(gradientSize))}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops)
} }
if pos.BeforeEnd { if pos.BeforeEnd {
paint.LinearGradientOp{Color1: black, Color2: transparent, Stop1: f32.Pt(0, float32(gtx.Constraints.Min.Y)), Stop2: f32.Pt(0, float32(gtx.Constraints.Min.Y-gradientSize))}.Add(gtx.Ops) paint.LinearGradientOp{Color1: style.Gradient, Color2: transparent, Stop1: f32.Pt(0, float32(gtx.Constraints.Min.Y)), Stop2: f32.Pt(0, float32(gtx.Constraints.Min.Y-gradientSize))}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops)
} }
totalPixelsEstimate = float32(gtx.Constraints.Min.Y+pos.Offset-pos.OffsetLast) * float32(numItems) / float32(pos.Count) totalPixelsEstimate = float32(gtx.Constraints.Min.Y+pos.Offset-pos.OffsetLast) * float32(numItems) / float32(pos.Count)
@ -41,11 +50,11 @@ func (s *ScrollBar) Layout(gtx C, width unit.Dp, numItems int, pos *layout.Posit
case layout.Horizontal: case layout.Horizontal:
if pos.First > 0 || pos.Offset > 0 { if pos.First > 0 || pos.Offset > 0 {
paint.LinearGradientOp{Color1: black, Color2: transparent, Stop2: f32.Pt(float32(gradientSize), 0)}.Add(gtx.Ops) paint.LinearGradientOp{Color1: style.Gradient, Color2: transparent, Stop2: f32.Pt(float32(gradientSize), 0)}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops)
} }
if pos.BeforeEnd { if pos.BeforeEnd {
paint.LinearGradientOp{Color1: black, Color2: transparent, Stop1: f32.Pt(float32(gtx.Constraints.Min.X), 0), Stop2: f32.Pt(float32(gtx.Constraints.Min.X-gradientSize), 0)}.Add(gtx.Ops) paint.LinearGradientOp{Color1: style.Gradient, Color2: transparent, Stop1: f32.Pt(float32(gtx.Constraints.Min.X), 0), Stop2: f32.Pt(float32(gtx.Constraints.Min.X-gradientSize), 0)}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops)
} }
totalPixelsEstimate = float32(gtx.Constraints.Min.X+pos.Offset-pos.OffsetLast) * float32(numItems) / float32(pos.Count) totalPixelsEstimate = float32(gtx.Constraints.Min.X+pos.Offset-pos.OffsetLast) * float32(numItems) / float32(pos.Count)
@ -56,7 +65,7 @@ func (s *ScrollBar) Layout(gtx C, width unit.Dp, numItems int, pos *layout.Posit
} }
scrollBarRelStart := (float32(pos.First)*totalPixelsEstimate/float32(numItems) + float32(pos.Offset)) / totalPixelsEstimate scrollBarRelStart := (float32(pos.First)*totalPixelsEstimate/float32(numItems) + float32(pos.Offset)) / totalPixelsEstimate
scrWidth := gtx.Dp(width) scrWidth := gtx.Dp(style.Width)
stack := op.Offset(image.Point{}).Push(gtx.Ops) stack := op.Offset(image.Point{}).Push(gtx.Ops)
var area clip.Stack var area clip.Stack
@ -65,7 +74,7 @@ func (s *ScrollBar) Layout(gtx C, width unit.Dp, numItems int, pos *layout.Posit
if scrollBarRelLength < 1 && (s.dragging || s.hovering) { if scrollBarRelLength < 1 && (s.dragging || s.hovering) {
y1 := int(scrollBarRelStart * float32(gtx.Constraints.Min.Y)) y1 := int(scrollBarRelStart * float32(gtx.Constraints.Min.Y))
y2 := int((scrollBarRelStart + scrollBarRelLength) * float32(gtx.Constraints.Min.Y)) y2 := int((scrollBarRelStart + scrollBarRelLength) * float32(gtx.Constraints.Min.Y))
paint.FillShape(gtx.Ops, scrollBarColor, clip.Rect{Min: image.Pt(gtx.Constraints.Min.X-scrWidth, y1), Max: image.Pt(gtx.Constraints.Min.X, y2)}.Op()) paint.FillShape(gtx.Ops, style.Color, clip.Rect{Min: image.Pt(gtx.Constraints.Min.X-scrWidth, y1), Max: image.Pt(gtx.Constraints.Min.X, y2)}.Op())
} }
rect := image.Rect(gtx.Constraints.Min.X-scrWidth, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y) rect := image.Rect(gtx.Constraints.Min.X-scrWidth, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)
area = clip.Rect(rect).Push(gtx.Ops) area = clip.Rect(rect).Push(gtx.Ops)
@ -73,7 +82,7 @@ func (s *ScrollBar) Layout(gtx C, width unit.Dp, numItems int, pos *layout.Posit
if scrollBarRelLength < 1 && (s.dragging || s.hovering) { if scrollBarRelLength < 1 && (s.dragging || s.hovering) {
x1 := int(scrollBarRelStart * float32(gtx.Constraints.Min.X)) x1 := int(scrollBarRelStart * float32(gtx.Constraints.Min.X))
x2 := int((scrollBarRelStart + scrollBarRelLength) * float32(gtx.Constraints.Min.X)) x2 := int((scrollBarRelStart + scrollBarRelLength) * float32(gtx.Constraints.Min.X))
paint.FillShape(gtx.Ops, scrollBarColor, clip.Rect{Min: image.Pt(x1, gtx.Constraints.Min.Y-scrWidth), Max: image.Pt(x2, gtx.Constraints.Min.Y)}.Op()) paint.FillShape(gtx.Ops, style.Color, clip.Rect{Min: image.Pt(x1, gtx.Constraints.Min.Y-scrWidth), Max: image.Pt(x2, gtx.Constraints.Min.Y)}.Op())
} }
rect := image.Rect(0, gtx.Constraints.Min.Y-scrWidth, gtx.Constraints.Min.X, gtx.Constraints.Min.Y) rect := image.Rect(0, gtx.Constraints.Min.Y-scrWidth, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)
area = clip.Rect(rect).Push(gtx.Ops) area = clip.Rect(rect).Push(gtx.Ops)

View File

@ -11,7 +11,6 @@ import (
"gioui.org/op/clip" "gioui.org/op/clip"
"gioui.org/op/paint" "gioui.org/op/paint"
"gioui.org/unit" "gioui.org/unit"
"gioui.org/widget/material"
"github.com/vsariola/sointu/tracker" "github.com/vsariola/sointu/tracker"
"github.com/vsariola/sointu/version" "github.com/vsariola/sointu/version"
"golang.org/x/exp/shiny/materialdesign/icons" "golang.org/x/exp/shiny/materialdesign/icons"
@ -76,7 +75,7 @@ func (s *SongPanel) Layout(gtx C, t *Tracker) D {
return s.MenuBar.Layout(gtx, t) return s.MenuBar.Layout(gtx, t)
}), }),
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
return s.PlayBar.Layout(gtx, &t.Theme.Material) return s.PlayBar.Layout(gtx, t.Theme)
}), }),
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
return s.layoutSongOptions(gtx, t) return s.layoutSongOptions(gtx, t)
@ -99,13 +98,13 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
weightingTxt = "No weight (RMS)" weightingTxt = "No weight (RMS)"
} }
weightingBtn := Btn(tr.Theme, &tr.Theme.TextButton, t.WeightingTypeBtn, weightingTxt) weightingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.WeightingTypeBtn, weightingTxt)
oversamplingTxt := "Sample peak" oversamplingTxt := "Sample peak"
if tr.Model.Oversampling().Value() { if tr.Model.Oversampling().Value() {
oversamplingTxt = "True peak" oversamplingTxt = "True peak"
} }
oversamplingBtn := Btn(tr.Theme, &tr.Theme.TextButton, t.OversamplingBtn, oversamplingTxt) oversamplingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.OversamplingBtn, oversamplingTxt)
return layout.Flex{Axis: layout.Vertical}.Layout(gtx, return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
@ -334,7 +333,7 @@ func (t *MenuBar) Layout(gtx C, tr *Tracker) D {
gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(36)) gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(36))
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(36)) gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(36))
panicBtnStyle := ToggleIcon(gtx, &tr.Theme.Material, t.PanicBtn, icons.AlertErrorOutline, icons.AlertError, t.panicHint, t.panicHint) panicBtnStyle := ToggleIcon(gtx, tr.Theme, t.PanicBtn, icons.AlertErrorOutline, icons.AlertError, t.panicHint, t.panicHint)
if t.PanicBtn.Bool.Value() { if t.PanicBtn.Bool.Value() {
panicBtnStyle.IconButtonStyle.Color = tr.Theme.SongPanel.ErrorColor panicBtnStyle.IconButtonStyle.Color = tr.Theme.SongPanel.ErrorColor
} }
@ -388,7 +387,7 @@ func NewPlayBar(model *tracker.Model) *PlayBar {
return ret return ret
} }
func (pb *PlayBar) Layout(gtx C, th *material.Theme) D { func (pb *PlayBar) Layout(gtx C, th *Theme) D {
rewindBtnStyle := ActionIcon(gtx, th, pb.RewindBtn, icons.AVFastRewind, pb.rewindHint) rewindBtnStyle := ActionIcon(gtx, th, pb.RewindBtn, icons.AVFastRewind, pb.rewindHint)
playBtnStyle := ToggleIcon(gtx, th, pb.PlayingBtn, icons.AVPlayArrow, icons.AVStop, pb.playHint, pb.stopHint) 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) recordBtnStyle := ToggleIcon(gtx, th, pb.RecordBtn, icons.AVFiberManualRecord, icons.AVFiberSmartRecord, pb.recordHint, pb.stopRecordHint)

View File

@ -13,16 +13,19 @@ import (
) )
type Theme struct { type Theme struct {
Material material.Theme Define any // this is just needed for yaml.UnmarshalStrict, so we can have "defines" in the yaml
FilledButton ButtonStyle Material material.Theme
TextButton ButtonStyle Button struct {
DisabledButton ButtonStyle Filled ButtonStyle
MenuButton ButtonStyle Text ButtonStyle
Oscilloscope OscilloscopeStyle Disabled ButtonStyle
NumericUpDown NumericUpDownStyle Menu ButtonStyle
DialogTitle LabelStyle }
DialogText LabelStyle Oscilloscope OscilloscopeStyle
SongPanel struct { NumericUpDown NumericUpDownStyle
DialogTitle LabelStyle
DialogText LabelStyle
SongPanel struct {
RowHeader LabelStyle RowHeader LabelStyle
RowValue LabelStyle RowValue LabelStyle
Expander LabelStyle Expander LabelStyle
@ -44,6 +47,9 @@ type Theme struct {
Unique LabelStyle Unique LabelStyle
Loop color.NRGBA Loop color.NRGBA
Header LabelStyle Header LabelStyle
Play color.NRGBA
OneBeat color.NRGBA
TwoBeat color.NRGBA
} }
Dialog struct { Dialog struct {
Bg color.NRGBA Bg color.NRGBA
@ -55,10 +61,14 @@ type Theme struct {
RowTitle LabelStyle RowTitle LabelStyle
Cell LabelStyle Cell LabelStyle
Loop color.NRGBA Loop color.NRGBA
CellBg color.NRGBA
Play color.NRGBA
} }
Menu struct { Menu struct {
Text LabelStyle Text LabelStyle
ShortCut LabelStyle ShortCut color.NRGBA
Hover color.NRGBA
Disabled color.NRGBA
} }
InstrumentEditor struct { InstrumentEditor struct {
Octave LabelStyle Octave LabelStyle
@ -69,6 +79,7 @@ type Theme struct {
Number LabelStyle Number LabelStyle
Name LabelStyle Name LabelStyle
NameMuted LabelStyle NameMuted LabelStyle
ScrollBar ScrollBarStyle
} }
UnitList struct { UnitList struct {
Name LabelStyle Name LabelStyle
@ -84,15 +95,26 @@ type Theme struct {
Hint LabelStyle Hint LabelStyle
Chooser LabelStyle Chooser LabelStyle
ParameterName LabelStyle ParameterName LabelStyle
InvalidParam color.NRGBA
SendTarget color.NRGBA
} }
Cursor struct { Cursor CursorStyle
Active color.NRGBA Selection CursorStyle
Inactive color.NRGBA Tooltip struct {
Color color.NRGBA
Bg color.NRGBA
} }
Selection struct { Popup struct {
Active color.NRGBA Bg color.NRGBA
Inactive color.NRGBA Shadow color.NRGBA
} }
ScrollBar ScrollBarStyle
}
type CursorStyle struct {
Active color.NRGBA
ActiveAlt color.NRGBA // alternative color for the cursor, used e.g. when the midi input is active
Inactive color.NRGBA
} }
//go:embed theme.yml //go:embed theme.yml
@ -100,12 +122,10 @@ var defaultTheme []byte
func NewTheme() *Theme { func NewTheme() *Theme {
var theme Theme var theme Theme
err := yaml.Unmarshal(defaultTheme, &theme) err := yaml.UnmarshalStrict(defaultTheme, &theme)
if err != nil { if err != nil {
panic(fmt.Errorf("failed to default theme: %w", err)) panic(fmt.Errorf("failed to default theme: %w", err))
} }
str, _ := yaml.Marshal(theme)
fmt.Printf(string(str))
ReadCustomConfigYml("theme.yml", &theme) ReadCustomConfigYml("theme.yml", &theme)
theme.Material.Shaper = &text.Shaper{} theme.Material.Shaper = &text.Shaper{}
theme.Material.Icon.CheckBoxChecked = must(widget.NewIcon(icons.ToggleCheckBox)) theme.Material.Icon.CheckBoxChecked = must(widget.NewIcon(icons.ToggleCheckBox))
@ -121,30 +141,3 @@ func must[T any](ic T, err error) T {
} }
return ic return ic
} }
var white = color.NRGBA{R: 255, G: 255, B: 255, A: 255}
var black = color.NRGBA{R: 0, G: 0, B: 0, A: 255}
var transparent = color.NRGBA{A: 0}
var mediumEmphasisTextColor = color.NRGBA{R: 153, G: 153, B: 153, A: 153}
var disabledTextColor = color.NRGBA{R: 255, G: 255, B: 255, A: 97}
var trackerPlayColor = color.NRGBA{R: 55, G: 55, B: 61, A: 255}
var oneBeatHighlight = color.NRGBA{R: 31, G: 37, B: 38, A: 255}
var twoBeatHighlight = color.NRGBA{R: 31, G: 51, B: 53, A: 255}
var patternPlayColor = color.NRGBA{R: 55, G: 55, B: 61, A: 255}
var patternCellColor = color.NRGBA{R: 255, G: 255, B: 255, A: 3}
var popupSurfaceColor = color.NRGBA{R: 50, G: 50, B: 51, A: 255}
var popupShadowColor = color.NRGBA{R: 0, G: 0, B: 0, A: 192}
var cursorForTrackMidiInColor = color.NRGBA{R: 255, G: 100, B: 140, A: 48}
var cursorNeighborForTrackMidiInColor = color.NRGBA{R: 255, G: 100, B: 140, A: 24}
var menuHoverColor = color.NRGBA{R: 30, G: 31, B: 38, A: 255}
var scrollBarColor = color.NRGBA{R: 255, G: 255, B: 255, A: 32}
var paramIsSendTargetColor = color.NRGBA{R: 120, G: 120, B: 210, A: 255}
var paramValueInvalidColor = color.NRGBA{R: 120, G: 120, B: 120, A: 190}

View File

@ -1,16 +1,20 @@
# following definitions are not in theme.go, but defined here just for # Because we use yaml.UnmarshalStrict, we needed to have "Define any" field for
# reusability, with &... *... references # all the defines; UnmarshalStrict thrwows an error if a field is not defined
primarycolor: &primarycolor { r: 206, g: 147, b: 216, a: 255 } # { r: 243, g: 187, b: 252, a: 255 } define:
secondarycolor: &secondarycolor { r: 128, g: 222, b: 234, a: 255 } # { r: 187, g: 245, b: 252, a: 255 } [
transparentcolor: &transparentcolor { r: 0, g: 0, b: 0, a: 0 } &primarycolor { r: 206, g: 147, b: 216, a: 255 },
mediumemphasis: &mediumemphasis { r: 153, g: 153, b: 153, a: 255 } &secondarycolor { r: 128, g: 222, b: 234, a: 255 },
highemphasis: &highemphasis { r: 222, g: 222, b: 222, a: 255 } &transparentcolor { r: 0, g: 0, b: 0, a: 0 },
disabled: &disabled { r: 255, g: 255, b: 255, a: 97 } &mediumemphasis { r: 153, g: 153, b: 153, a: 255 },
errorcolor: &errorcolor { r: 207, g: 102, b: 121, a: 255 } &highemphasis { r: 222, g: 222, b: 222, a: 255 },
warningcolor: &warningcolor { r: 251, g: 192, b: 45, a: 255 } &disabled { r: 255, g: 255, b: 255, a: 97 },
white: &white { r: 255, g: 255, b: 255, a: 255 } &errorcolor { r: 207, g: 102, b: 121, a: 255 },
black: &black { r: 0, g: 0, b: 0, a: 255 } &warningcolor { r: 251, g: 192, b: 45, a: 255 },
loopcolor: &loopcolor { r: 252, g: 186, b: 3, a: 255 } &white { r: 255, g: 255, b: 255, a: 255 },
&black { r: 0, g: 0, b: 0, a: 255 },
&loopcolor { r: 252, g: 186, b: 3, a: 255 },
&scrollbarcolor { r: 255, g: 255, b: 255, a: 32 },
]
# from here on starts the structs defined in the theme.go # from here on starts the structs defined in the theme.go
material: material:
@ -21,40 +25,41 @@ material:
fg: &fg { r: 255, g: 255, b: 255, a: 255 } fg: &fg { r: 255, g: 255, b: 255, a: 255 }
contrastbg: *primarycolor contrastbg: *primarycolor
contrastfg: &contrastfg { r: 0, g: 0, b: 0, a: 255 } contrastfg: &contrastfg { r: 0, g: 0, b: 0, a: 255 }
filledbutton: button:
background: *primarycolor filled:
color: *contrastfg background: *primarycolor
textsize: &buttontextsize 14 color: *contrastfg
cornerradius: &buttoncornerradius 18 textsize: &buttontextsize 14
height: &buttonheight 36 cornerradius: &buttoncornerradius 18
inset: &buttoninset { top: 0, bottom: 0, left: 6, right: 6 } height: &buttonheight 36
textbutton: inset: &buttoninset { top: 0, bottom: 0, left: 6, right: 6 }
background: *transparentcolor text:
color: *primarycolor background: *transparentcolor
textsize: *buttontextsize color: *primarycolor
cornerradius: *buttoncornerradius textsize: *buttontextsize
height: *buttonheight cornerradius: *buttoncornerradius
inset: *buttoninset height: *buttonheight
disabledbutton: inset: *buttoninset
background: { r: 53, g: 51, b: 55, a: 255 } disabled:
color: { r: 120, g: 116, b: 121, a: 255 } background: { r: 53, g: 51, b: 55, a: 255 }
textsize: *buttontextsize color: { r: 120, g: 116, b: 121, a: 255 }
cornerradius: *buttoncornerradius textsize: *buttontextsize
height: *buttonheight cornerradius: *buttoncornerradius
inset: *buttoninset height: *buttonheight
menubutton: inset: *buttoninset
background: *transparentcolor menu:
color: { r: 255, g: 255, b: 255, a: 255 } background: *transparentcolor
textsize: *buttontextsize color: { r: 255, g: 255, b: 255, a: 255 }
cornerradius: 0 textsize: *buttontextsize
height: *buttonheight cornerradius: 0
inset: *buttoninset height: *buttonheight
inset: *buttoninset
oscilloscope: oscilloscope:
curvecolors: [*primarycolor, *secondarycolor] curvecolors: [*primarycolor, *secondarycolor]
limitcolor: { r: 255, g: 255, b: 255, a: 8 } limitcolor: { r: 255, g: 255, b: 255, a: 8 }
cursorcolor: { r: 252, g: 186, b: 3, a: 255 } cursorcolor: { r: 252, g: 186, b: 3, a: 255 }
numericupdown: numericupdown:
bgcolor: { r: 255, g: 255, b: 255, A: 3 } bgcolor: { r: 255, g: 255, b: 255, a: 3 }
textcolor: *fg textcolor: *fg
iconcolor: *primarycolor iconcolor: *primarycolor
cornerradius: 4 cornerradius: 4
@ -64,7 +69,7 @@ numericupdown:
width: 70 width: 70
height: 20 height: 20
songpanel: songpanel:
bg: { r: 24, g: 24, b: 24, A: 255 } bg: { r: 24, g: 24, b: 24, a: 255 }
rowheader: rowheader:
textsize: 14 textsize: 14
color: *mediumemphasis color: *mediumemphasis
@ -78,7 +83,7 @@ songpanel:
version: version:
textsize: 12 textsize: 12
color: *mediumemphasis color: *mediumemphasis
alerts: alert:
error: error:
bg: *errorcolor bg: *errorcolor
text: { textsize: 16, color: *black } text: { textsize: 16, color: *black }
@ -98,8 +103,10 @@ ordereditor:
{ textsize: 16, color: *secondarycolor, font: { typeface: "Go Mono" } } { textsize: 16, color: *secondarycolor, font: { typeface: "Go Mono" } }
cell: { textsize: 16, color: *primarycolor, font: { typeface: "Go Mono" } } cell: { textsize: 16, color: *primarycolor, font: { typeface: "Go Mono" } }
loop: *loopcolor loop: *loopcolor
cellbg: { r: 255, g: 255, b: 255, a: 3 }
play: { r: 55, g: 55, b: 61, a: 255 }
noteeditor: noteeditor:
tracktitle: { textsize: 12, color: *mediumemphasis } tracktitle: { textsize: 12, color: *mediumemphasis, alignment: 2 }
orderrow: orderrow:
{ textsize: 16, color: *secondarycolor, font: { typeface: "Go Mono" } } { textsize: 16, color: *secondarycolor, font: { typeface: "Go Mono" } }
patternrow: patternrow:
@ -111,9 +118,14 @@ noteeditor:
{ textsize: 16, color: *secondarycolor, font: { typeface: "Go Mono" } } { textsize: 16, color: *secondarycolor, font: { typeface: "Go Mono" } }
loop: *loopcolor loop: *loopcolor
header: { textsize: 14, color: *disabled } header: { textsize: 14, color: *disabled }
play: { r: 55, g: 55, b: 61, a: 255 }
onebeat: { r: 31, g: 37, b: 38, a: 255 }
twobeat: { r: 31, g: 51, b: 53, a: 255 }
menu: menu:
text: { textsize: 16, color: *highemphasis, shadowcolor: *black } text: { textsize: 16, color: *highemphasis, shadowcolor: *black }
shortcut: { textsize: 16, color: *mediumemphasis, shadowcolor: *black } shortcut: *mediumemphasis
hover: { r: 100, g: 140, b: 255, a: 48 }
disabled: *disabled
instrumenteditor: instrumenteditor:
octave: { textsize: 14, color: *disabled } octave: { textsize: 14, color: *disabled }
voices: { textsize: 14, color: *disabled } voices: { textsize: 14, color: *disabled }
@ -123,6 +135,7 @@ instrumenteditor:
number: { textsize: 10, color: *mediumemphasis, shadowcolor: *black } number: { textsize: 10, color: *mediumemphasis, shadowcolor: *black }
name: { textsize: 12, color: *white, shadowcolor: *black } name: { textsize: 12, color: *white, shadowcolor: *black }
namemuted: { textsize: 12, color: *disabled } namemuted: { textsize: 12, color: *disabled }
scrollbar: { width: 6, color: *scrollbarcolor }
unitlist: unitlist:
name: { textsize: 12, color: *white, shadowcolor: *black } name: { textsize: 12, color: *white, shadowcolor: *black }
namedisabled: namedisabled:
@ -139,9 +152,18 @@ uniteditor:
hint: { textsize: 16, color: *highemphasis, shadowcolor: *black } hint: { textsize: 16, color: *highemphasis, shadowcolor: *black }
chooser: { textsize: 12, color: *white, shadowcolor: *black } chooser: { textsize: 12, color: *white, shadowcolor: *black }
parametername: { textsize: 16, color: *white, shadowcolor: *black } parametername: { textsize: 16, color: *white, shadowcolor: *black }
invalidparam: { r: 120, g: 120, b: 120, a: 190 }
sendtarget: { r: 120, g: 120, b: 210, a: 255 }
cursor: cursor:
active: { r: 100, g: 140, b: 255, a: 48 } active: { r: 100, g: 140, b: 255, a: 48 }
activealt: { r: 255, g: 100, b: 140, a: 48 }
inactive: { r: 140, g: 140, b: 140, a: 48 } inactive: { r: 140, g: 140, b: 140, a: 48 }
selection: selection:
active: { r: 100, g: 140, b: 255, a: 16 } active: { r: 100, g: 140, b: 255, a: 16 }
activealt: { r: 255, g: 100, b: 140, a: 24 }
inactive: { r: 140, g: 140, b: 140, a: 16 } inactive: { r: 140, g: 140, b: 140, a: 16 }
scrollbar: { width: 10, color: *scrollbarcolor, gradient: *black }
tooltip: { color: *white, bg: *black }
popup:
bg: { r: 50, g: 50, b: 51, a: 255 }
shadow: { r: 0, g: 0, b: 0, a: 192 }

View File

@ -132,9 +132,9 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
t.Alerts().Add("Unit copied to clipboard", tracker.Info) t.Alerts().Add("Unit copied to clipboard", tracker.Info)
} }
} }
copyUnitBtnStyle := TipIcon(&t.Theme.Material, pe.CopyUnitBtn, icons.ContentContentCopy, pe.copyHint) copyUnitBtnStyle := TipIcon(t.Theme, pe.CopyUnitBtn, icons.ContentContentCopy, pe.copyHint)
deleteUnitBtnStyle := ActionIcon(gtx, &t.Theme.Material, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)") deleteUnitBtnStyle := ActionIcon(gtx, t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)")
disableUnitBtnStyle := ToggleIcon(gtx, &t.Theme.Material, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, pe.disableUnitHint, pe.enableUnitHint) disableUnitBtnStyle := ToggleIcon(gtx, t.Theme, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, pe.disableUnitHint, pe.enableUnitHint)
text := t.Units().SelectedType() text := t.Units().SelectedType()
if text == "" { if text == "" {
text = "Choose unit type" text = "Choose unit type"
@ -149,7 +149,7 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
var dims D var dims D
if t.Units().SelectedType() != "" { if t.Units().SelectedType() != "" {
clearUnitBtnStyle := ActionIcon(gtx, &t.Theme.Material, pe.ClearUnitBtn, icons.ContentClear, "Clear unit") clearUnitBtnStyle := ActionIcon(gtx, t.Theme, pe.ClearUnitBtn, icons.ContentClear, "Clear unit")
dims = clearUnitBtnStyle.Layout(gtx) dims = clearUnitBtnStyle.Layout(gtx)
} }
return D{Size: image.Pt(gtx.Dp(unit.Dp(48)), dims.Size.Y)} return D{Size: image.Pt(gtx.Dp(unit.Dp(48)), dims.Size.Y)}
@ -253,7 +253,7 @@ type ParameterStyle struct {
func (t *Tracker) ParamStyle(th *Theme, paramWidget *ParameterWidget) ParameterStyle { func (t *Tracker) ParamStyle(th *Theme, paramWidget *ParameterWidget) ParameterStyle {
sendTargetTheme := th.Material.WithPalette(material.Palette{ sendTargetTheme := th.Material.WithPalette(material.Palette{
Bg: th.Material.Bg, Bg: th.Material.Bg,
Fg: paramIsSendTargetColor, Fg: th.UnitEditor.SendTarget,
ContrastBg: th.Material.ContrastBg, ContrastBg: th.Material.ContrastBg,
ContrastFg: th.Material.ContrastFg, ContrastFg: th.Material.ContrastFg,
}) })
@ -297,7 +297,7 @@ func (p ParameterStyle) Layout(gtx C) D {
} }
sliderStyle := material.Slider(&p.Theme.Material, &p.w.floatWidget) sliderStyle := material.Slider(&p.Theme.Material, &p.w.floatWidget)
if isSendTarget { if isSendTarget {
sliderStyle.Color = paramIsSendTargetColor sliderStyle.Color = p.Theme.UnitEditor.SendTarget
} }
r := image.Rectangle{Max: gtx.Constraints.Min} r := image.Rectangle{Max: gtx.Constraints.Min}
defer clip.Rect(r).Push(gtx.Ops).Pop() defer clip.Rect(r).Push(gtx.Ops).Pop()
@ -372,7 +372,7 @@ func (p ParameterStyle) Layout(gtx C) D {
hint := p.w.Parameter.Hint() hint := p.w.Parameter.Hint()
label := Label(p.tracker.Theme, &p.tracker.Theme.UnitEditor.Hint, hint.Label) label := Label(p.tracker.Theme, &p.tracker.Theme.UnitEditor.Hint, hint.Label)
if !hint.Valid { if !hint.Valid {
label.Color = paramValueInvalidColor label.Color = p.tracker.Theme.UnitEditor.InvalidParam
} }
if info == "" { if info == "" {
return label.Layout(gtx) return label.Layout(gtx)