mirror of
https://github.com/vsariola/sointu.git
synced 2025-06-04 01:28:45 -04:00
feat(tracker/gioui): add theme.yml which contains all styling
This commit is contained in:
parent
8245fbda24
commit
afb1fee4ed
@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
- Theme can be user configured, in theme.yml. This theme.yml should be placed in
|
||||||
|
the usual sointu config directory (i.e.
|
||||||
|
`os.UserConfigDir()/sointu/theme.yml`). See
|
||||||
|
[theme.yml](tracker/gioui/theme.yml) for the default theme, and
|
||||||
|
[theme.go](tracker/gioui/theme.go) for what can be changed.
|
||||||
- Ctrl + scroll wheel adjusts the global scaling of the GUI ([#153][i153])
|
- Ctrl + scroll wheel adjusts the global scaling of the GUI ([#153][i153])
|
||||||
- The loudness detection supports LUFS, A-weighting, C-weighting or
|
- The loudness detection supports LUFS, A-weighting, C-weighting or
|
||||||
RMS-weighting, and peak detection supports true peak or sample peak detection.
|
RMS-weighting, and peak detection supports true peak or sample peak detection.
|
||||||
|
@ -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,27 +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 = 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 = primaryColor
|
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,
|
||||||
@ -87,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() {
|
||||||
@ -98,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 = primaryColor
|
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,
|
||||||
@ -115,51 +115,27 @@ func (t *TipIconButtonStyle) Layout(gtx C) D {
|
|||||||
return t.TipArea.Layout(gtx, t.Tooltip, t.IconButtonStyle.Layout)
|
return t.TipArea.Layout(gtx, t.Tooltip, t.IconButtonStyle.Layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ActionButton(gtx C, th *material.Theme, w *ActionClickable, text string) ButtonStyle {
|
func ActionButton(gtx C, th *Theme, style *ButtonStyle, w *ActionClickable, text string) Button {
|
||||||
for w.Clickable.Clicked(gtx) {
|
for w.Clickable.Clicked(gtx) {
|
||||||
w.Action.Do()
|
w.Action.Do()
|
||||||
}
|
}
|
||||||
ret := Button(th, &w.Clickable, text)
|
|
||||||
ret.Color = th.Palette.Fg
|
|
||||||
if !w.Action.Allowed() {
|
if !w.Action.Allowed() {
|
||||||
ret.Color = disabledTextColor
|
return Btn(th, &th.Button.Disabled, &w.Clickable, text)
|
||||||
}
|
}
|
||||||
ret.Background = transparent
|
return Btn(th, style, &w.Clickable, text)
|
||||||
ret.Inset = layout.UniformInset(unit.Dp(6))
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToggleButton(gtx C, th *material.Theme, b *BoolClickable, text string) ButtonStyle {
|
func ToggleButton(gtx C, th *Theme, b *BoolClickable, text string) Button {
|
||||||
for b.Clickable.Clicked(gtx) {
|
for b.Clickable.Clicked(gtx) {
|
||||||
b.Bool.Toggle()
|
b.Bool.Toggle()
|
||||||
}
|
}
|
||||||
ret := Button(th, &b.Clickable, text)
|
if !b.Bool.Enabled() {
|
||||||
ret.Background = transparent
|
return Btn(th, &th.Button.Disabled, &b.Clickable, text)
|
||||||
ret.Inset = layout.UniformInset(unit.Dp(6))
|
|
||||||
if b.Bool.Value() {
|
|
||||||
ret.Color = th.Palette.ContrastFg
|
|
||||||
ret.Background = th.Palette.Fg
|
|
||||||
} else {
|
|
||||||
ret.Color = th.Palette.Fg
|
|
||||||
ret.Background = transparent
|
|
||||||
}
|
}
|
||||||
return ret
|
if b.Bool.Value() {
|
||||||
}
|
return Btn(th, &th.Button.Filled, &b.Clickable, text)
|
||||||
|
}
|
||||||
func LowEmphasisButton(th *material.Theme, w *Clickable, text string) ButtonStyle {
|
return Btn(th, &th.Button.Text, &b.Clickable, text)
|
||||||
ret := Button(th, w, text)
|
|
||||||
ret.Color = th.Palette.Fg
|
|
||||||
ret.Background = transparent
|
|
||||||
ret.Inset = layout.UniformInset(unit.Dp(6))
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func HighEmphasisButton(th *material.Theme, w *Clickable, text string) ButtonStyle {
|
|
||||||
ret := Button(th, w, text)
|
|
||||||
ret.Color = th.Palette.ContrastFg
|
|
||||||
ret.Background = th.Palette.Fg
|
|
||||||
ret.Inset = layout.UniformInset(unit.Dp(6))
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clickable represents a clickable area.
|
// Clickable represents a clickable area.
|
||||||
@ -277,22 +253,21 @@ func (b *Clickable) update(t event.Tag, gtx layout.Context) (widget.Click, bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ButtonStyle struct {
|
type ButtonStyle struct {
|
||||||
Text string
|
|
||||||
// Color is the text color.
|
// Color is the text color.
|
||||||
Color color.NRGBA
|
Color color.NRGBA
|
||||||
Font font.Font
|
Font font.Font
|
||||||
TextSize unit.Sp
|
TextSize unit.Sp
|
||||||
Background color.NRGBA
|
Background color.NRGBA
|
||||||
CornerRadius unit.Dp
|
CornerRadius unit.Dp
|
||||||
|
Height unit.Dp
|
||||||
Inset layout.Inset
|
Inset layout.Inset
|
||||||
Button *Clickable
|
|
||||||
shaper *text.Shaper
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ButtonLayoutStyle struct {
|
type Button struct {
|
||||||
Background color.NRGBA
|
Text string
|
||||||
CornerRadius unit.Dp
|
Button *Clickable
|
||||||
Button *Clickable
|
shaper *text.Shaper
|
||||||
|
ButtonStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
type IconButtonStyle struct {
|
type IconButtonStyle struct {
|
||||||
@ -307,36 +282,20 @@ type IconButtonStyle struct {
|
|||||||
Description string
|
Description string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Button(th *material.Theme, button *Clickable, txt string) ButtonStyle {
|
func Btn(th *Theme, style *ButtonStyle, button *Clickable, txt string) Button {
|
||||||
b := ButtonStyle{
|
b := Button{
|
||||||
Text: txt,
|
Text: txt,
|
||||||
Color: th.Palette.ContrastFg,
|
ButtonStyle: *style,
|
||||||
CornerRadius: 4,
|
Button: button,
|
||||||
Background: th.Palette.ContrastBg,
|
shaper: th.Material.Shaper,
|
||||||
TextSize: th.TextSize * 14.0 / 16.0,
|
|
||||||
Inset: layout.Inset{
|
|
||||||
Top: 10, Bottom: 10,
|
|
||||||
Left: 12, Right: 12,
|
|
||||||
},
|
|
||||||
Button: button,
|
|
||||||
shaper: th.Shaper,
|
|
||||||
}
|
}
|
||||||
b.Font.Typeface = th.Face
|
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func ButtonLayout(th *material.Theme, button *Clickable) ButtonLayoutStyle {
|
func IconButton(th *Theme, button *Clickable, icon *widget.Icon, description string) IconButtonStyle {
|
||||||
return ButtonLayoutStyle{
|
|
||||||
Button: button,
|
|
||||||
Background: th.Palette.ContrastBg,
|
|
||||||
CornerRadius: 4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func IconButton(th *material.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),
|
||||||
@ -345,22 +304,9 @@ func IconButton(th *material.Theme, button *Clickable, icon *widget.Icon, descri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b ButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
|
func (b *Button) Layout(gtx layout.Context) layout.Dimensions {
|
||||||
return ButtonLayoutStyle{
|
|
||||||
Background: b.Background,
|
|
||||||
CornerRadius: b.CornerRadius,
|
|
||||||
Button: b.Button,
|
|
||||||
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
|
||||||
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 ButtonLayoutStyle) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
|
|
||||||
min := gtx.Constraints.Min
|
min := gtx.Constraints.Min
|
||||||
|
min.Y = gtx.Dp(b.Height)
|
||||||
return b.Button.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
return b.Button.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
semantic.Button.Add(gtx.Ops)
|
semantic.Button.Add(gtx.Ops)
|
||||||
return layout.Background{}.Layout(gtx,
|
return layout.Background{}.Layout(gtx,
|
||||||
@ -380,7 +326,13 @@ func (b ButtonLayoutStyle) Layout(gtx layout.Context, w layout.Widget) layout.Di
|
|||||||
},
|
},
|
||||||
func(gtx layout.Context) layout.Dimensions {
|
func(gtx layout.Context) layout.Dimensions {
|
||||||
gtx.Constraints.Min = min
|
gtx.Constraints.Min = min
|
||||||
return layout.Center.Layout(gtx, w)
|
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())
|
||||||
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
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"
|
||||||
"gioui.org/text"
|
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
"gioui.org/widget/material"
|
"gioui.org/widget/material"
|
||||||
@ -28,7 +29,7 @@ type DialogStyle struct {
|
|||||||
AltStyle material.ButtonStyle
|
AltStyle material.ButtonStyle
|
||||||
OkStyle material.ButtonStyle
|
OkStyle material.ButtonStyle
|
||||||
CancelStyle material.ButtonStyle
|
CancelStyle material.ButtonStyle
|
||||||
Shaper *text.Shaper
|
Theme *Theme
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDialog(ok, alt, cancel tracker.Action) *Dialog {
|
func NewDialog(ok, alt, cancel tracker.Action) *Dialog {
|
||||||
@ -37,22 +38,22 @@ func NewDialog(ok, alt, cancel tracker.Action) *Dialog {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfirmDialog(gtx C, th *material.Theme, dialog *Dialog, title, text string) DialogStyle {
|
func ConfirmDialog(gtx C, th *Theme, dialog *Dialog, title, text string) DialogStyle {
|
||||||
ret := DialogStyle{
|
ret := DialogStyle{
|
||||||
dialog: dialog,
|
dialog: dialog,
|
||||||
Title: title,
|
Title: title,
|
||||||
Text: text,
|
Text: text,
|
||||||
Inset: layout.Inset{Top: unit.Dp(12), Bottom: unit.Dp(12), Left: unit.Dp(20), Right: unit.Dp(20)},
|
Inset: layout.Inset{Top: unit.Dp(12), Bottom: unit.Dp(12), Left: unit.Dp(20), Right: unit.Dp(20)},
|
||||||
TextInset: layout.Inset{Top: unit.Dp(12), Bottom: unit.Dp(12)},
|
TextInset: layout.Inset{Top: unit.Dp(12), Bottom: unit.Dp(12)},
|
||||||
AltStyle: material.Button(th, &dialog.BtnAlt, "Alt"),
|
AltStyle: material.Button(&th.Material, &dialog.BtnAlt, "Alt"),
|
||||||
OkStyle: material.Button(th, &dialog.BtnOk, "Ok"),
|
OkStyle: material.Button(&th.Material, &dialog.BtnOk, "Ok"),
|
||||||
CancelStyle: material.Button(th, &dialog.BtnCancel, "Cancel"),
|
CancelStyle: material.Button(&th.Material, &dialog.BtnCancel, "Cancel"),
|
||||||
Shaper: th.Shaper,
|
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.Palette.Fg
|
b.Color = th.Material.Palette.Fg
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@ -106,17 +107,14 @@ func (d *DialogStyle) Layout(gtx C) D {
|
|||||||
gtx.Execute(key.FocusCmd{Tag: &d.dialog.BtnCancel})
|
gtx.Execute(key.FocusCmd{Tag: &d.dialog.BtnCancel})
|
||||||
}
|
}
|
||||||
d.dialog.handleKeys(gtx)
|
d.dialog.handleKeys(gtx)
|
||||||
paint.Fill(gtx.Ops, dialogBgColor)
|
paint.Fill(gtx.Ops, d.Theme.Dialog.Bg)
|
||||||
text := func(gtx C) D {
|
|
||||||
return d.TextInset.Layout(gtx, LabelStyle{Text: d.Text, Color: highEmphasisTextColor, Font: labelDefaultFont, FontSize: unit.Sp(14), Shaper: d.Shaper}.Layout)
|
|
||||||
}
|
|
||||||
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.Title, highEmphasisTextColor, d.Shaper)),
|
layout.Rigid(Label(d.Theme, &d.Theme.Dialog.Title, d.Title).Layout),
|
||||||
layout.Rigid(text),
|
layout.Rigid(Label(d.Theme, &d.Theme.Dialog.Text, d.Text).Layout),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
return layout.E.Layout(gtx, func(gtx C) D {
|
return layout.E.Layout(gtx, func(gtx C) D {
|
||||||
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(120))
|
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(120))
|
||||||
|
@ -15,8 +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"
|
|
||||||
"gioui.org/widget/material"
|
|
||||||
"github.com/vsariola/sointu/tracker"
|
"github.com/vsariola/sointu/tracker"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,27 +32,27 @@ type DragList struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FilledDragListStyle struct {
|
type FilledDragListStyle struct {
|
||||||
dragList *DragList
|
dragList *DragList
|
||||||
HoverColor color.NRGBA
|
HoverColor color.NRGBA
|
||||||
SelectedColor color.NRGBA
|
Cursor CursorStyle
|
||||||
CursorColor color.NRGBA
|
Selection CursorStyle
|
||||||
ScrollBarWidth unit.Dp
|
ScrollBar ScrollBarStyle
|
||||||
element, bg func(gtx C, i int) D
|
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 {
|
||||||
return &DragList{TrackerList: model, List: &layout.List{Axis: axis}, HoverItem: -1, ScrollBar: &ScrollBar{Axis: axis}}
|
return &DragList{TrackerList: model, List: &layout.List{Axis: axis}, HoverItem: -1, ScrollBar: &ScrollBar{Axis: axis}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func FilledDragList(th *material.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: dragListHoverColor,
|
HoverColor: hoveredColor(th.Selection.Active),
|
||||||
SelectedColor: dragListSelectedColor,
|
Cursor: th.Cursor,
|
||||||
CursorColor: cursorColor,
|
Selection: th.Selection,
|
||||||
ScrollBarWidth: unit.Dp(10),
|
ScrollBar: th.ScrollBar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,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 {
|
||||||
@ -147,12 +145,16 @@ func (s FilledDragListStyle) Layout(gtx C) D {
|
|||||||
var color color.NRGBA
|
var color color.NRGBA
|
||||||
if s.dragList.TrackerList.Selected() == index {
|
if s.dragList.TrackerList.Selected() == index {
|
||||||
if s.dragList.focused {
|
if s.dragList.focused {
|
||||||
color = s.CursorColor
|
color = s.Cursor.Active
|
||||||
} else {
|
} else {
|
||||||
color = s.SelectedColor
|
color = s.Cursor.Inactive
|
||||||
}
|
}
|
||||||
} else if between(s.dragList.TrackerList.Selected(), index, s.dragList.TrackerList.Selected2()) {
|
} else if between(s.dragList.TrackerList.Selected(), index, s.dragList.TrackerList.Selected2()) {
|
||||||
color = s.SelectedColor
|
if s.dragList.focused {
|
||||||
|
color = s.Selection.Active
|
||||||
|
} else {
|
||||||
|
color = s.Selection.Inactive
|
||||||
|
}
|
||||||
} else if s.dragList.HoverItem == index {
|
} else if s.dragList.HoverItem == index {
|
||||||
color = s.HoverColor
|
color = s.HoverColor
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
package gioui
|
package gioui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"gioui.org/font"
|
||||||
"gioui.org/io/event"
|
"gioui.org/io/event"
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
|
"gioui.org/unit"
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
"gioui.org/widget/material"
|
"gioui.org/widget/material"
|
||||||
)
|
)
|
||||||
@ -18,7 +22,12 @@ type (
|
|||||||
requestFocus bool
|
requestFocus bool
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorStyle material.EditorStyle
|
EditorStyle struct {
|
||||||
|
Color color.NRGBA
|
||||||
|
HintColor color.NRGBA
|
||||||
|
Font font.Font
|
||||||
|
TextSize unit.Sp
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewEditor(e widget.Editor) *Editor {
|
func NewEditor(e widget.Editor) *Editor {
|
||||||
@ -36,8 +45,21 @@ func NewEditor(e widget.Editor) *Editor {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func MaterialEditor(th *material.Theme, e *Editor, hint string) EditorStyle {
|
func (s *EditorStyle) AsLabelStyle() LabelStyle {
|
||||||
return EditorStyle(material.Editor(th, &e.Editor, hint))
|
return LabelStyle{
|
||||||
|
Color: s.Color,
|
||||||
|
Font: s.Font,
|
||||||
|
TextSize: s.TextSize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaterialEditor(th *Theme, style *EditorStyle, editor *Editor, hint string) material.EditorStyle {
|
||||||
|
ret := material.Editor(&th.Material, &editor.Editor, hint)
|
||||||
|
ret.Font = style.Font
|
||||||
|
ret.TextSize = style.TextSize
|
||||||
|
ret.Color = style.Color
|
||||||
|
ret.HintColor = style.HintColor
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Editor) SetText(s string) {
|
func (e *Editor) SetText(s string) {
|
||||||
@ -80,7 +102,3 @@ func (e *Editor) Cancelled(gtx C) bool {
|
|||||||
func (e *Editor) Focus() {
|
func (e *Editor) Focus() {
|
||||||
e.requestFocus = true
|
e.requestFocus = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EditorStyle) Layout(gtx C) D {
|
|
||||||
return material.EditorStyle(*e).Layout(gtx)
|
|
||||||
}
|
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gioui.org/font"
|
|
||||||
"gioui.org/io/clipboard"
|
"gioui.org/io/clipboard"
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
@ -130,7 +129,7 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
|
|||||||
|
|
||||||
octave := func(gtx C) D {
|
octave := func(gtx C) D {
|
||||||
in := layout.UniformInset(unit.Dp(1))
|
in := layout.UniformInset(unit.Dp(1))
|
||||||
numStyle := NumericUpDown(t.Theme, t.OctaveNumberInput, ie.octaveHint)
|
numStyle := NumUpDown(t.Theme, t.OctaveNumberInput, ie.octaveHint)
|
||||||
dims := in.Layout(gtx, numStyle.Layout)
|
dims := in.Layout(gtx, numStyle.Layout)
|
||||||
return dims
|
return dims
|
||||||
}
|
}
|
||||||
@ -144,7 +143,7 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
|
|||||||
return ie.layoutInstrumentList(gtx, t)
|
return ie.layoutInstrumentList(gtx, t)
|
||||||
}),
|
}),
|
||||||
layout.Rigid(layout.Spacer{Width: 10}.Layout),
|
layout.Rigid(layout.Spacer{Width: 10}.Layout),
|
||||||
layout.Rigid(LabelStyle{Text: "Octave", Color: disabledTextColor, Alignment: layout.W, FontSize: t.Theme.TextSize * 14.0 / 16.0, Shaper: t.Theme.Shaper}.Layout),
|
layout.Rigid(Label(t.Theme, &t.Theme.InstrumentEditor.Octave, "Octave").Layout),
|
||||||
layout.Rigid(layout.Spacer{Width: 4}.Layout),
|
layout.Rigid(layout.Spacer{Width: 4}.Layout),
|
||||||
layout.Rigid(octave),
|
layout.Rigid(octave),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
@ -186,7 +185,7 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
|||||||
soloBtnStyle := ToggleIcon(gtx, t.Theme, 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, 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(&ie.presetMenu, t.Theme.Shaper)
|
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 {
|
||||||
@ -214,10 +213,10 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
|||||||
header := func(gtx C) D {
|
header := func(gtx C) D {
|
||||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Rigid(layout.Spacer{Width: 6}.Layout),
|
layout.Rigid(layout.Spacer{Width: 6}.Layout),
|
||||||
layout.Rigid(LabelStyle{Text: "Voices", Color: disabledTextColor, Alignment: layout.W, FontSize: t.Theme.TextSize * 14.0 / 16.0, Shaper: t.Theme.Shaper}.Layout),
|
layout.Rigid(Label(t.Theme, &t.Theme.InstrumentEditor.Voices, "Voices").Layout),
|
||||||
layout.Rigid(layout.Spacer{Width: 4}.Layout),
|
layout.Rigid(layout.Spacer{Width: 4}.Layout),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
numStyle := NumericUpDown(t.Theme, t.InstrumentVoices, "Number of voices for this instrument")
|
numStyle := NumUpDown(t.Theme, t.InstrumentVoices, "Number of voices for this instrument")
|
||||||
dims := numStyle.Layout(gtx)
|
dims := numStyle.Layout(gtx)
|
||||||
return dims
|
return dims
|
||||||
}),
|
}),
|
||||||
@ -254,8 +253,7 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
|||||||
for ie.commentEditor.Submitted(gtx) || ie.commentEditor.Cancelled(gtx) {
|
for ie.commentEditor.Submitted(gtx) || ie.commentEditor.Cancelled(gtx) {
|
||||||
ie.instrumentDragList.Focus()
|
ie.instrumentDragList.Focus()
|
||||||
}
|
}
|
||||||
style := MaterialEditor(t.Theme, ie.commentEditor, "Comment")
|
style := MaterialEditor(t.Theme, &t.Theme.InstrumentEditor.InstrumentComment, ie.commentEditor, "Comment")
|
||||||
style.Color = highEmphasisTextColor
|
|
||||||
ret := layout.UniformInset(unit.Dp(6)).Layout(gtx, style.Layout)
|
ret := layout.UniformInset(unit.Dp(6)).Layout(gtx, style.Layout)
|
||||||
ie.commentString.Set(ie.commentEditor.Text())
|
ie.commentString.Set(ie.commentEditor.Text())
|
||||||
return ret
|
return ret
|
||||||
@ -270,33 +268,29 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D {
|
func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D {
|
||||||
gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(36))
|
gtx.Constraints.Max.Y = gtx.Dp(36)
|
||||||
|
gtx.Constraints.Min.Y = gtx.Dp(36)
|
||||||
element := func(gtx C, i int) D {
|
element := func(gtx C, i int) D {
|
||||||
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(36))
|
grabhandle := Label(t.Theme, &t.Theme.InstrumentEditor.InstrumentList.Number, strconv.Itoa(i+1))
|
||||||
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(30))
|
|
||||||
grabhandle := LabelStyle{Text: strconv.Itoa(i + 1), ShadeColor: black, Color: mediumEmphasisTextColor, FontSize: unit.Sp(10), Alignment: layout.Center, Shaper: t.Theme.Shaper}
|
|
||||||
label := func(gtx C) D {
|
label := func(gtx C) D {
|
||||||
name, level, mute, ok := (*tracker.Instruments)(t.Model).Item(i)
|
name, level, mute, ok := (*tracker.Instruments)(t.Model).Item(i)
|
||||||
if !ok {
|
if !ok {
|
||||||
labelStyle := LabelStyle{Text: "", ShadeColor: black, Color: white, FontSize: unit.Sp(12), Shaper: t.Theme.Shaper}
|
labelStyle := Label(t.Theme, &t.Theme.InstrumentEditor.InstrumentList.Number, "")
|
||||||
return layout.Center.Layout(gtx, labelStyle.Layout)
|
return layout.Center.Layout(gtx, labelStyle.Layout)
|
||||||
}
|
}
|
||||||
k := byte(255 - level*127)
|
k := byte(255 - level*127)
|
||||||
color := color.NRGBA{R: 255, G: k, B: 255, A: 255}
|
color := color.NRGBA{R: 255, G: k, B: 255, A: 255}
|
||||||
|
s := t.Theme.InstrumentEditor.InstrumentList.Name
|
||||||
|
if mute {
|
||||||
|
s = t.Theme.InstrumentEditor.InstrumentList.NameMuted
|
||||||
|
}
|
||||||
|
s.Color = color
|
||||||
if i == ie.instrumentDragList.TrackerList.Selected() {
|
if i == ie.instrumentDragList.TrackerList.Selected() {
|
||||||
ie.nameEditor.SetText(name)
|
ie.nameEditor.SetText(name)
|
||||||
for ie.nameEditor.Submitted(gtx) || ie.nameEditor.Cancelled(gtx) {
|
for ie.nameEditor.Submitted(gtx) || ie.nameEditor.Cancelled(gtx) {
|
||||||
ie.instrumentDragList.Focus()
|
ie.instrumentDragList.Focus()
|
||||||
}
|
}
|
||||||
style := MaterialEditor(t.Theme, ie.nameEditor, "Instr")
|
style := MaterialEditor(t.Theme, &s, ie.nameEditor, "Instr")
|
||||||
style.Color = color
|
|
||||||
style.HintColor = instrumentNameHintColor
|
|
||||||
style.TextSize = unit.Sp(12)
|
|
||||||
style.Font = labelDefaultFont
|
|
||||||
if mute {
|
|
||||||
style.Color = disabledTextColor
|
|
||||||
style.Font.Style = font.Italic
|
|
||||||
}
|
|
||||||
dims := layout.Center.Layout(gtx, func(gtx C) D {
|
dims := layout.Center.Layout(gtx, func(gtx C) D {
|
||||||
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()
|
||||||
return style.Layout(gtx)
|
return style.Layout(gtx)
|
||||||
@ -307,29 +301,21 @@ func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D {
|
|||||||
if name == "" {
|
if name == "" {
|
||||||
name = "Instr"
|
name = "Instr"
|
||||||
}
|
}
|
||||||
labelStyle := LabelStyle{Text: name, ShadeColor: black, Color: color, Font: labelDefaultFont, FontSize: unit.Sp(12), Shaper: t.Theme.Shaper}
|
l := s.AsLabelStyle()
|
||||||
if mute {
|
return layout.Center.Layout(gtx, Label(t.Theme, &l, name).Layout)
|
||||||
labelStyle.Color = disabledTextColor
|
|
||||||
labelStyle.Font.Style = font.Italic
|
|
||||||
}
|
|
||||||
return layout.Center.Layout(gtx, labelStyle.Layout)
|
|
||||||
}
|
}
|
||||||
return layout.Inset{Left: unit.Dp(6), Right: unit.Dp(6), Top: unit.Dp(4)}.Layout(gtx, func(gtx C) D {
|
return layout.Center.Layout(gtx, func(gtx C) D {
|
||||||
return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx,
|
return layout.Inset{Left: unit.Dp(6), Right: unit.Dp(6)}.Layout(gtx, func(gtx C) D {
|
||||||
layout.Rigid(grabhandle.Layout),
|
return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Rigid(label),
|
layout.Rigid(grabhandle.Layout),
|
||||||
)
|
layout.Rigid(label),
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
color := inactiveLightSurfaceColor
|
|
||||||
if ie.wasFocused {
|
|
||||||
color = activeLightSurfaceColor
|
|
||||||
}
|
|
||||||
instrumentList := FilledDragList(t.Theme, ie.instrumentDragList, element, nil)
|
instrumentList := FilledDragList(t.Theme, ie.instrumentDragList, element, nil)
|
||||||
instrumentList.SelectedColor = color
|
instrumentList.ScrollBar = t.Theme.InstrumentEditor.InstrumentList.ScrollBar
|
||||||
instrumentList.HoverColor = instrumentHoverColor
|
|
||||||
instrumentList.ScrollBarWidth = unit.Dp(6)
|
|
||||||
|
|
||||||
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()
|
||||||
@ -367,8 +353,8 @@ 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, ie.addUnitBtn, icons.ContentAdd, "Add unit (Enter)")
|
addUnitBtnStyle := ActionIcon(gtx, t.Theme, ie.addUnitBtn, icons.ContentAdd, "Add unit (Enter)")
|
||||||
addUnitBtnStyle.IconButtonStyle.Color = t.Theme.ContrastFg
|
addUnitBtnStyle.IconButtonStyle.Color = t.Theme.Material.ContrastFg
|
||||||
addUnitBtnStyle.IconButtonStyle.Background = t.Theme.Fg
|
addUnitBtnStyle.IconButtonStyle.Background = t.Theme.Material.ContrastBg
|
||||||
addUnitBtnStyle.IconButtonStyle.Inset = layout.UniformInset(unit.Dp(4))
|
addUnitBtnStyle.IconButtonStyle.Inset = layout.UniformInset(unit.Dp(4))
|
||||||
|
|
||||||
var units [256]tracker.UnitListItem
|
var units [256]tracker.UnitListItem
|
||||||
@ -393,23 +379,23 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
|
|||||||
return layout.Dimensions{Size: gtx.Constraints.Min}
|
return layout.Dimensions{Size: gtx.Constraints.Min}
|
||||||
}
|
}
|
||||||
u := units[i]
|
u := units[i]
|
||||||
var color color.NRGBA = white
|
|
||||||
f := labelDefaultFont
|
editorStyle := t.Theme.InstrumentEditor.UnitList.Name
|
||||||
|
if u.Disabled {
|
||||||
|
editorStyle = t.Theme.InstrumentEditor.UnitList.NameDisabled
|
||||||
|
}
|
||||||
|
|
||||||
stackText := strconv.FormatInt(int64(u.StackAfter), 10)
|
stackText := strconv.FormatInt(int64(u.StackAfter), 10)
|
||||||
if u.StackNeed > u.StackBefore {
|
if u.StackNeed > u.StackBefore {
|
||||||
color = errorColor
|
editorStyle.Color = t.Theme.InstrumentEditor.UnitList.Error
|
||||||
(*tracker.Alerts)(t.Model).AddNamed("UnitNeedsInputs", fmt.Sprintf("%v needs at least %v input signals, got %v", u.Type, u.StackNeed, u.StackBefore), tracker.Error)
|
(*tracker.Alerts)(t.Model).AddNamed("UnitNeedsInputs", fmt.Sprintf("%v needs at least %v input signals, got %v", u.Type, u.StackNeed, u.StackBefore), tracker.Error)
|
||||||
} else if i == count-1 && u.StackAfter != 0 {
|
} else if i == count-1 && u.StackAfter != 0 {
|
||||||
color = warningColor
|
editorStyle.Color = t.Theme.InstrumentEditor.UnitList.Warning
|
||||||
(*tracker.Alerts)(t.Model).AddNamed("InstrumentLeavesSignals", fmt.Sprintf("Instrument leaves %v signal(s) on the stack", u.StackAfter), tracker.Warning)
|
(*tracker.Alerts)(t.Model).AddNamed("InstrumentLeavesSignals", fmt.Sprintf("Instrument leaves %v signal(s) on the stack", u.StackAfter), tracker.Warning)
|
||||||
}
|
}
|
||||||
if u.Disabled {
|
|
||||||
color = disabledTextColor
|
|
||||||
f.Style = font.Italic
|
|
||||||
}
|
|
||||||
|
|
||||||
stackLabel := LabelStyle{Text: stackText, ShadeColor: black, Color: mediumEmphasisTextColor, Font: labelDefaultFont, FontSize: unit.Sp(12), Shaper: t.Theme.Shaper}
|
stackLabel := Label(t.Theme, &t.Theme.InstrumentEditor.UnitList.Stack, stackText)
|
||||||
|
|
||||||
rightMargin := layout.Inset{Right: unit.Dp(10)}
|
rightMargin := layout.Inset{Right: unit.Dp(10)}
|
||||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
@ -435,24 +421,21 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
|
|||||||
ie.searchEditor.SetText(str.Value())
|
ie.searchEditor.SetText(str.Value())
|
||||||
ie.unitDragList.Focus()
|
ie.unitDragList.Focus()
|
||||||
}
|
}
|
||||||
style := MaterialEditor(t.Theme, ie.searchEditor, "---")
|
style := MaterialEditor(t.Theme, &editorStyle, ie.searchEditor, "---")
|
||||||
style.Color = color
|
|
||||||
style.HintColor = instrumentNameHintColor
|
|
||||||
style.TextSize = unit.Sp(12)
|
|
||||||
style.Font = f
|
|
||||||
ret := style.Layout(gtx)
|
ret := style.Layout(gtx)
|
||||||
str.Set(ie.searchEditor.Text())
|
str.Set(ie.searchEditor.Text())
|
||||||
return ret
|
return ret
|
||||||
} else {
|
} else {
|
||||||
unitNameLabel := LabelStyle{Text: u.Type, ShadeColor: black, Color: color, Font: f, FontSize: unit.Sp(12), Shaper: t.Theme.Shaper}
|
text := u.Type
|
||||||
if unitNameLabel.Text == "" {
|
if text == "" {
|
||||||
unitNameLabel.Text = "---"
|
text = "---"
|
||||||
}
|
}
|
||||||
return unitNameLabel.Layout(gtx)
|
l := editorStyle.AsLabelStyle()
|
||||||
|
return Label(t.Theme, &l, text).Layout(gtx)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
layout.Flexed(1, func(gtx C) D {
|
layout.Flexed(1, func(gtx C) D {
|
||||||
unitNameLabel := LabelStyle{Text: u.Comment, ShadeColor: black, Color: mediumEmphasisTextColor, Font: f, FontSize: unit.Sp(12), Shaper: t.Theme.Shaper}
|
unitNameLabel := Label(t.Theme, &t.Theme.InstrumentEditor.UnitList.Comment, u.Comment)
|
||||||
inset := layout.Inset{Left: unit.Dp(5)}
|
inset := layout.Inset{Left: unit.Dp(5)}
|
||||||
return inset.Layout(gtx, unitNameLabel.Layout)
|
return inset.Layout(gtx, unitNameLabel.Layout)
|
||||||
}),
|
}),
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"gioui.org/font"
|
"gioui.org/font"
|
||||||
"gioui.org/layout"
|
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
"gioui.org/op/paint"
|
"gioui.org/op/paint"
|
||||||
"gioui.org/text"
|
"gioui.org/text"
|
||||||
@ -14,37 +13,38 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LabelStyle struct {
|
type LabelStyle struct {
|
||||||
Text string
|
Color color.NRGBA
|
||||||
Color color.NRGBA
|
ShadowColor color.NRGBA
|
||||||
ShadeColor color.NRGBA
|
Alignment text.Alignment
|
||||||
Alignment layout.Direction
|
Font font.Font
|
||||||
Font font.Font
|
TextSize unit.Sp
|
||||||
FontSize unit.Sp
|
|
||||||
Shaper *text.Shaper
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l LabelStyle) Layout(gtx layout.Context) layout.Dimensions {
|
type LabelWidget struct {
|
||||||
return l.Alignment.Layout(gtx, func(gtx C) D {
|
Text string
|
||||||
gtx.Constraints.Min = image.Point{}
|
Shaper *text.Shaper
|
||||||
paint.ColorOp{Color: l.ShadeColor}.Add(gtx.Ops)
|
LabelStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LabelWidget) Layout(gtx C) D {
|
||||||
|
textColorMacro := op.Record(gtx.Ops)
|
||||||
|
paint.ColorOp{Color: l.Color}.Add(gtx.Ops)
|
||||||
|
textColor := textColorMacro.Stop()
|
||||||
|
t := widget.Label{
|
||||||
|
Alignment: l.Alignment,
|
||||||
|
MaxLines: 1,
|
||||||
|
}
|
||||||
|
if l.ShadowColor.A > 0 {
|
||||||
|
shadowColorMacro := op.Record(gtx.Ops)
|
||||||
|
paint.ColorOp{Color: l.ShadowColor}.Add(gtx.Ops)
|
||||||
|
shadowColor := shadowColorMacro.Stop()
|
||||||
offs := op.Offset(image.Pt(2, 2)).Push(gtx.Ops)
|
offs := op.Offset(image.Pt(2, 2)).Push(gtx.Ops)
|
||||||
widget.Label{
|
t.Layout(gtx, l.Shaper, l.Font, l.TextSize, l.Text, shadowColor)
|
||||||
Alignment: text.Start,
|
|
||||||
MaxLines: 1,
|
|
||||||
}.Layout(gtx, l.Shaper, l.Font, l.FontSize, l.Text, op.CallOp{})
|
|
||||||
offs.Pop()
|
offs.Pop()
|
||||||
paint.ColorOp{Color: l.Color}.Add(gtx.Ops)
|
}
|
||||||
dims := widget.Label{
|
return t.Layout(gtx, l.Shaper, l.Font, l.TextSize, l.Text, textColor)
|
||||||
Alignment: text.Start,
|
|
||||||
MaxLines: 1,
|
|
||||||
}.Layout(gtx, l.Shaper, l.Font, l.FontSize, l.Text, op.CallOp{})
|
|
||||||
return layout.Dimensions{
|
|
||||||
Size: dims.Size,
|
|
||||||
Baseline: dims.Baseline,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Label(str string, color color.NRGBA, shaper *text.Shaper) layout.Widget {
|
func Label(th *Theme, style *LabelStyle, txt string) LabelWidget {
|
||||||
return LabelStyle{Text: str, Color: color, ShadeColor: black, Font: labelDefaultFont, FontSize: labelDefaultFontSize, Alignment: layout.W, Shaper: shaper}.Layout
|
return LabelWidget{Text: txt, Shaper: th.Material.Shaper, LabelStyle: *style}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,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/text"
|
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
"github.com/vsariola/sointu/tracker"
|
"github.com/vsariola/sointu/tracker"
|
||||||
)
|
)
|
||||||
@ -27,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
|
||||||
Shaper *text.Shaper
|
Theme *Theme
|
||||||
|
LabelStyle LabelStyle
|
||||||
|
Disabled color.NRGBA
|
||||||
}
|
}
|
||||||
|
|
||||||
type MenuItem struct {
|
type MenuItem struct {
|
||||||
@ -100,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 := LabelStyle{Text: item.Text, FontSize: m.FontSize, Color: m.TextColor, Shaper: m.Shaper}
|
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 := LabelStyle{Text: item.ShortcutText, FontSize: m.FontSize, Color: m.ShortCutColor, Shaper: m.Shaper}
|
|
||||||
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)
|
||||||
})
|
})
|
||||||
@ -142,27 +139,25 @@ 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(menu *Menu, shaper *text.Shaper) 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),
|
Theme: th,
|
||||||
HoverColor: menuHoverColor,
|
|
||||||
Shaper: shaper,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,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(menu, tr.Theme.Shaper)
|
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 := Button(tr.Theme, 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)
|
||||||
|
@ -150,17 +150,17 @@ 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, te.AddSemitoneBtn, "+1")
|
addSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.AddSemitoneBtn, "+1")
|
||||||
subtractSemitoneBtnStyle := ActionButton(gtx, t.Theme, te.SubtractSemitoneBtn, "-1")
|
subtractSemitoneBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.SubtractSemitoneBtn, "-1")
|
||||||
addOctaveBtnStyle := ActionButton(gtx, t.Theme, te.AddOctaveBtn, "+12")
|
addOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.AddOctaveBtn, "+12")
|
||||||
subtractOctaveBtnStyle := ActionButton(gtx, t.Theme, te.SubtractOctaveBtn, "-12")
|
subtractOctaveBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.SubtractOctaveBtn, "-12")
|
||||||
noteOffBtnStyle := ActionButton(gtx, t.Theme, te.NoteOffBtn, "Note Off")
|
noteOffBtnStyle := ActionButton(gtx, t.Theme, &t.Theme.Button.Text, te.NoteOffBtn, "Note Off")
|
||||||
deleteTrackBtnStyle := ActionIcon(gtx, t.Theme, te.DeleteTrackBtn, icons.ActionDelete, te.deleteTrackHint)
|
deleteTrackBtnStyle := ActionIcon(gtx, t.Theme, te.DeleteTrackBtn, icons.ActionDelete, te.deleteTrackHint)
|
||||||
splitTrackBtnStyle := ActionIcon(gtx, t.Theme, te.SplitTrackBtn, icons.CommunicationCallSplit, te.splitTrackHint)
|
splitTrackBtnStyle := ActionIcon(gtx, t.Theme, te.SplitTrackBtn, icons.CommunicationCallSplit, te.splitTrackHint)
|
||||||
newTrackBtnStyle := ActionIcon(gtx, t.Theme, 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 := NumericUpDown(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")
|
||||||
@ -176,7 +176,7 @@ func (te *NoteEditor) layoutButtons(gtx C, t *Tracker) D {
|
|||||||
layout.Rigid(effectBtnStyle.Layout),
|
layout.Rigid(effectBtnStyle.Layout),
|
||||||
layout.Rigid(uniqueBtnStyle.Layout),
|
layout.Rigid(uniqueBtnStyle.Layout),
|
||||||
layout.Rigid(layout.Spacer{Width: 10}.Layout),
|
layout.Rigid(layout.Spacer{Width: 10}.Layout),
|
||||||
layout.Rigid(LabelStyle{Text: "Voices", Color: disabledTextColor, Alignment: layout.W, FontSize: t.Theme.TextSize * 14.0 / 16.0, Shaper: t.Theme.Shaper}.Layout),
|
layout.Rigid(Label(t.Theme, &t.Theme.NoteEditor.Header, "Voices").Layout),
|
||||||
layout.Rigid(layout.Spacer{Width: 4}.Layout),
|
layout.Rigid(layout.Spacer{Width: 4}.Layout),
|
||||||
layout.Rigid(voiceUpDown),
|
layout.Rigid(voiceUpDown),
|
||||||
layout.Rigid(splitTrackBtnStyle.Layout),
|
layout.Rigid(splitTrackBtnStyle.Layout),
|
||||||
@ -224,28 +224,26 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D {
|
|||||||
colTitle := func(gtx C, i int) D {
|
colTitle := func(gtx C, i int) D {
|
||||||
h := gtx.Dp(trackColTitleHeight)
|
h := gtx.Dp(trackColTitleHeight)
|
||||||
gtx.Constraints = layout.Exact(image.Pt(pxWidth, h))
|
gtx.Constraints = layout.Exact(image.Pt(pxWidth, h))
|
||||||
LabelStyle{
|
Label(t.Theme, &t.Theme.NoteEditor.TrackTitle, t.Model.TrackTitle(i)).Layout(gtx)
|
||||||
Alignment: layout.N,
|
|
||||||
Text: t.Model.TrackTitle(i),
|
|
||||||
FontSize: unit.Sp(12),
|
|
||||||
Color: mediumEmphasisTextColor,
|
|
||||||
Shaper: t.Theme.Shaper,
|
|
||||||
}.Layout(gtx)
|
|
||||||
return D{Size: image.Pt(pxWidth, h)}
|
return D{Size: image.Pt(pxWidth, h)}
|
||||||
}
|
}
|
||||||
|
|
||||||
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{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orderRowOp := colorOp(gtx, t.Theme.NoteEditor.OrderRow.Color)
|
||||||
|
loopColorOp := colorOp(gtx, t.Theme.OrderEditor.Loop)
|
||||||
|
patternRowOp := colorOp(gtx, t.Theme.NoteEditor.PatternRow.Color)
|
||||||
|
|
||||||
rowTitle := func(gtx C, j int) D {
|
rowTitle := func(gtx C, j int) D {
|
||||||
rpp := max(t.RowsPerPattern().Value(), 1)
|
rpp := max(t.RowsPerPattern().Value(), 1)
|
||||||
pat := j / rpp
|
pat := j / rpp
|
||||||
@ -253,16 +251,14 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D {
|
|||||||
w := pxPatMarkWidth + pxRowMarkWidth
|
w := pxPatMarkWidth + pxRowMarkWidth
|
||||||
defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
||||||
if row == 0 {
|
if row == 0 {
|
||||||
color := rowMarkerPatternTextColor
|
op := orderRowOp
|
||||||
if l := t.Loop(); pat >= l.Start && pat < l.Start+l.Length {
|
if l := t.Loop(); pat >= l.Start && pat < l.Start+l.Length {
|
||||||
color = loopMarkerColor
|
op = loopColorOp
|
||||||
}
|
}
|
||||||
paint.ColorOp{Color: color}.Add(gtx.Ops)
|
widget.Label{}.Layout(gtx, t.Theme.Material.Shaper, t.Theme.NoteEditor.OrderRow.Font, t.Theme.NoteEditor.OrderRow.TextSize, strings.ToUpper(fmt.Sprintf("%02x", pat)), op)
|
||||||
widget.Label{}.Layout(gtx, t.Theme.Shaper, trackerFont, trackerFontSize, strings.ToUpper(fmt.Sprintf("%02x", pat)), op.CallOp{})
|
|
||||||
}
|
}
|
||||||
defer op.Offset(image.Pt(pxPatMarkWidth, 0)).Push(gtx.Ops).Pop()
|
defer op.Offset(image.Pt(pxPatMarkWidth, 0)).Push(gtx.Ops).Pop()
|
||||||
paint.ColorOp{Color: rowMarkerRowTextColor}.Add(gtx.Ops)
|
widget.Label{}.Layout(gtx, t.Theme.Material.Shaper, t.Theme.NoteEditor.PatternRow.Font, t.Theme.NoteEditor.PatternRow.TextSize, strings.ToUpper(fmt.Sprintf("%02x", row)), patternRowOp)
|
||||||
widget.Label{}.Layout(gtx, t.Theme.Shaper, trackerFont, trackerFontSize, strings.ToUpper(fmt.Sprintf("%02x", row)), op.CallOp{})
|
|
||||||
return D{Size: image.Pt(w, pxHeight)}
|
return D{Size: image.Pt(w, pxHeight)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,25 +267,28 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D {
|
|||||||
selection := te.scrollTable.Table.Range()
|
selection := te.scrollTable.Table.Range()
|
||||||
hasTrackMidiIn := te.TrackMidiInBtn.Bool.Value()
|
hasTrackMidiIn := te.TrackMidiInBtn.Bool.Value()
|
||||||
|
|
||||||
|
patternNoOp := colorOp(gtx, t.Theme.NoteEditor.PatternNo.Color)
|
||||||
|
uniqueOp := colorOp(gtx, t.Theme.NoteEditor.Unique.Color)
|
||||||
|
noteOp := colorOp(gtx, t.Theme.NoteEditor.Note.Color)
|
||||||
|
|
||||||
cell := func(gtx C, x, y int) D {
|
cell := func(gtx C, x, y int) D {
|
||||||
// draw the background, to indicate selection
|
// draw the background, to indicate selection
|
||||||
color := transparent
|
|
||||||
point := tracker.Point{X: x, Y: y}
|
point := tracker.Point{X: x, Y: y}
|
||||||
if drawSelection && selection.Contains(point) {
|
if drawSelection && selection.Contains(point) {
|
||||||
color = inactiveSelectionColor
|
color := t.Theme.Selection.Inactive
|
||||||
if te.scrollTable.Focused() {
|
if te.scrollTable.Focused() {
|
||||||
color = selectionColor
|
color = t.Theme.Selection.Active
|
||||||
}
|
}
|
||||||
|
paint.FillShape(gtx.Ops, color, clip.Rect{Min: image.Pt(0, 0), Max: image.Pt(gtx.Constraints.Min.X, gtx.Constraints.Min.Y)}.Op())
|
||||||
}
|
}
|
||||||
paint.FillShape(gtx.Ops, color, clip.Rect{Min: image.Pt(0, 0), Max: image.Pt(gtx.Constraints.Min.X, gtx.Constraints.Min.Y)}.Op())
|
|
||||||
// draw the cursor
|
// draw the cursor
|
||||||
if point == cursor {
|
if point == cursor {
|
||||||
c := inactiveSelectionColor
|
c := t.Theme.Cursor.Inactive
|
||||||
if te.scrollTable.Focused() {
|
if te.scrollTable.Focused() {
|
||||||
c = cursorColor
|
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)
|
||||||
}
|
}
|
||||||
@ -297,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -309,23 +308,17 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D {
|
|||||||
defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
||||||
s := t.Model.Order().Value(tracker.Point{X: x, Y: pat})
|
s := t.Model.Order().Value(tracker.Point{X: x, Y: pat})
|
||||||
if row == 0 { // draw the pattern marker
|
if row == 0 { // draw the pattern marker
|
||||||
paint.ColorOp{Color: trackerPatMarker}.Add(gtx.Ops)
|
widget.Label{}.Layout(gtx, t.Theme.Material.Shaper, t.Theme.NoteEditor.PatternNo.Font, t.Theme.NoteEditor.PatternNo.TextSize, patternIndexToString(s), patternNoOp)
|
||||||
widget.Label{}.Layout(gtx, t.Theme.Shaper, trackerFont, trackerFontSize, patternIndexToString(s), op.CallOp{})
|
|
||||||
}
|
}
|
||||||
if row == 1 && t.Model.PatternUnique(x, s) { // draw a * if the pattern is unique
|
if row == 1 && t.Model.PatternUnique(x, s) { // draw a * if the pattern is unique
|
||||||
paint.ColorOp{Color: mediumEmphasisTextColor}.Add(gtx.Ops)
|
widget.Label{}.Layout(gtx, t.Theme.Material.Shaper, t.Theme.NoteEditor.Unique.Font, t.Theme.NoteEditor.Unique.TextSize, "*", uniqueOp)
|
||||||
widget.Label{}.Layout(gtx, t.Theme.Shaper, trackerFont, trackerFontSize, "*", op.CallOp{})
|
|
||||||
}
|
|
||||||
if te.scrollTable.Table.Cursor() == point && te.scrollTable.Focused() {
|
|
||||||
paint.ColorOp{Color: trackerActiveTextColor}.Add(gtx.Ops)
|
|
||||||
} else {
|
|
||||||
paint.ColorOp{Color: trackerInactiveTextColor}.Add(gtx.Ops)
|
|
||||||
}
|
}
|
||||||
|
op := noteOp
|
||||||
val := noteStr[byte(t.Model.Notes().Value(tracker.Point{X: x, Y: y}))]
|
val := noteStr[byte(t.Model.Notes().Value(tracker.Point{X: x, Y: y}))]
|
||||||
if t.Model.Notes().Effect(x) {
|
if t.Model.Notes().Effect(x) {
|
||||||
val = hexStr[byte(t.Model.Notes().Value(tracker.Point{X: x, Y: y}))]
|
val = hexStr[byte(t.Model.Notes().Value(tracker.Point{X: x, Y: y}))]
|
||||||
}
|
}
|
||||||
widget.Label{Alignment: text.Middle}.Layout(gtx, t.Theme.Shaper, trackerFont, trackerFontSize, val, op.CallOp{})
|
widget.Label{Alignment: text.Middle}.Layout(gtx, t.Theme.Material.Shaper, t.Theme.NoteEditor.Note.Font, t.Theme.NoteEditor.Note.TextSize, val, op)
|
||||||
return D{Size: image.Pt(pxWidth, pxHeight)}
|
return D{Size: image.Pt(pxWidth, pxHeight)}
|
||||||
}
|
}
|
||||||
table := FilledScrollTable(t.Theme, te.scrollTable, cell, colTitle, rowTitle, nil, rowTitleBg)
|
table := FilledScrollTable(t.Theme, te.scrollTable, cell, colTitle, rowTitle, nil, rowTitleBg)
|
||||||
@ -336,6 +329,12 @@ func (te *NoteEditor) layoutTracks(gtx C, t *Tracker) D {
|
|||||||
return table.Layout(gtx)
|
return table.Layout(gtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func colorOp(gtx C, c color.NRGBA) op.CallOp {
|
||||||
|
macro := op.Record(gtx.Ops)
|
||||||
|
paint.ColorOp{Color: c}.Add(gtx.Ops)
|
||||||
|
return macro.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
func (te *NoteEditor) paintColumnCell(gtx C, x int, t *Tracker, c color.NRGBA) {
|
func (te *NoteEditor) paintColumnCell(gtx C, x int, t *Tracker, c color.NRGBA) {
|
||||||
cw := gtx.Constraints.Min.X
|
cw := gtx.Constraints.Min.X
|
||||||
cx := 0
|
cx := 0
|
||||||
|
@ -12,6 +12,7 @@ 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"
|
||||||
"gioui.org/widget"
|
"gioui.org/widget"
|
||||||
"gioui.org/x/component"
|
"gioui.org/x/component"
|
||||||
|
|
||||||
@ -20,8 +21,6 @@ import (
|
|||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/text"
|
"gioui.org/text"
|
||||||
"gioui.org/unit"
|
|
||||||
"gioui.org/widget/material"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type NumberInput struct {
|
type NumberInput struct {
|
||||||
@ -34,48 +33,41 @@ type NumberInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NumericUpDownStyle struct {
|
type NumericUpDownStyle struct {
|
||||||
NumberInput *NumberInput
|
TextColor color.NRGBA `yaml:",flow"`
|
||||||
Color color.NRGBA
|
IconColor color.NRGBA `yaml:",flow"`
|
||||||
Font font.Font
|
BgColor color.NRGBA `yaml:",flow"`
|
||||||
TextSize unit.Sp
|
CornerRadius unit.Dp
|
||||||
BorderColor color.NRGBA
|
ButtonWidth unit.Dp
|
||||||
IconColor color.NRGBA
|
Width unit.Dp
|
||||||
BackgroundColor color.NRGBA
|
Height unit.Dp
|
||||||
CornerRadius unit.Dp
|
TextSize unit.Sp
|
||||||
Border unit.Dp
|
DpPerStep unit.Dp
|
||||||
ButtonWidth unit.Dp
|
}
|
||||||
UnitsPerStep unit.Dp
|
|
||||||
Tooltip component.Tooltip
|
type NumericUpDown struct {
|
||||||
Width unit.Dp
|
NumberInput *NumberInput
|
||||||
Height unit.Dp
|
Tooltip component.Tooltip
|
||||||
shaper text.Shaper
|
Shaper *text.Shaper
|
||||||
|
Font font.Font
|
||||||
|
NumericUpDownStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNumberInput(v tracker.Int) *NumberInput {
|
func NewNumberInput(v tracker.Int) *NumberInput {
|
||||||
return &NumberInput{Int: v}
|
return &NumberInput{Int: v}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NumericUpDown(th *material.Theme, number *NumberInput, tooltip string) NumericUpDownStyle {
|
func NumUpDown(th *Theme, number *NumberInput, tooltip string) NumericUpDown {
|
||||||
return NumericUpDownStyle{
|
return NumericUpDown{
|
||||||
NumberInput: number,
|
NumberInput: number,
|
||||||
Color: white,
|
Shaper: th.Material.Shaper,
|
||||||
IconColor: th.Palette.Fg,
|
Tooltip: Tooltip(th, tooltip),
|
||||||
BackgroundColor: numberInputBgColor,
|
NumericUpDownStyle: th.NumericUpDown,
|
||||||
CornerRadius: unit.Dp(4),
|
|
||||||
ButtonWidth: unit.Dp(16),
|
|
||||||
Border: unit.Dp(1),
|
|
||||||
UnitsPerStep: unit.Dp(8),
|
|
||||||
TextSize: th.TextSize * 14 / 16,
|
|
||||||
Tooltip: Tooltip(th, tooltip),
|
|
||||||
Width: unit.Dp(70),
|
|
||||||
Height: unit.Dp(20),
|
|
||||||
shaper: *th.Shaper,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NumericUpDownStyle) Update(gtx layout.Context) {
|
func (s *NumericUpDown) Update(gtx layout.Context) {
|
||||||
// handle dragging
|
// handle dragging
|
||||||
pxPerStep := float32(gtx.Dp(s.UnitsPerStep))
|
pxPerStep := float32(gtx.Dp(s.DpPerStep))
|
||||||
for {
|
for {
|
||||||
ev, ok := gtx.Event(pointer.Filter{
|
ev, ok := gtx.Event(pointer.Filter{
|
||||||
Target: s.NumberInput,
|
Target: s.NumberInput,
|
||||||
@ -110,14 +102,14 @@ func (s *NumericUpDownStyle) Update(gtx layout.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s NumericUpDownStyle) Layout(gtx C) D {
|
func (s NumericUpDown) Layout(gtx C) D {
|
||||||
if s.Tooltip.Text.Text != "" {
|
if s.Tooltip.Text.Text != "" {
|
||||||
return s.NumberInput.tipArea.Layout(gtx, s.Tooltip, s.actualLayout)
|
return s.NumberInput.tipArea.Layout(gtx, s.Tooltip, s.actualLayout)
|
||||||
}
|
}
|
||||||
return s.actualLayout(gtx)
|
return s.actualLayout(gtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NumericUpDownStyle) actualLayout(gtx C) D {
|
func (s *NumericUpDown) actualLayout(gtx C) D {
|
||||||
s.Update(gtx)
|
s.Update(gtx)
|
||||||
gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(s.Width), gtx.Dp(s.Height)))
|
gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(s.Width), gtx.Dp(s.Height)))
|
||||||
width := gtx.Dp(s.ButtonWidth)
|
width := gtx.Dp(s.ButtonWidth)
|
||||||
@ -125,7 +117,7 @@ func (s *NumericUpDownStyle) actualLayout(gtx C) D {
|
|||||||
return layout.Background{}.Layout(gtx,
|
return layout.Background{}.Layout(gtx,
|
||||||
func(gtx C) D {
|
func(gtx C) D {
|
||||||
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, gtx.Dp(s.CornerRadius)).Push(gtx.Ops).Pop()
|
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, gtx.Dp(s.CornerRadius)).Push(gtx.Ops).Pop()
|
||||||
paint.Fill(gtx.Ops, s.BackgroundColor)
|
paint.Fill(gtx.Ops, s.BgColor)
|
||||||
event.Op(gtx.Ops, s.NumberInput) // register drag inputs, if not hitting the clicks
|
event.Op(gtx.Ops, s.NumberInput) // register drag inputs, if not hitting the clicks
|
||||||
return D{Size: gtx.Constraints.Min}
|
return D{Size: gtx.Constraints.Min}
|
||||||
},
|
},
|
||||||
@ -143,8 +135,8 @@ func (s *NumericUpDownStyle) actualLayout(gtx C) D {
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
layout.Flexed(1, func(gtx C) D {
|
layout.Flexed(1, func(gtx C) D {
|
||||||
paint.ColorOp{Color: s.Color}.Add(gtx.Ops)
|
paint.ColorOp{Color: s.TextColor}.Add(gtx.Ops)
|
||||||
return widget.Label{Alignment: text.Middle}.Layout(gtx, &s.shaper, s.Font, s.TextSize, strconv.Itoa(s.NumberInput.Int.Value()), op.CallOp{})
|
return widget.Label{Alignment: text.Middle}.Layout(gtx, s.Shaper, s.Font, s.TextSize, strconv.Itoa(s.NumberInput.Int.Value()), op.CallOp{})
|
||||||
}),
|
}),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
gtx.Constraints = layout.Exact(image.Pt(width, height))
|
gtx.Constraints = layout.Exact(image.Pt(width, height))
|
||||||
|
@ -68,54 +68,53 @@ func (oe *OrderEditor) Layout(gtx C, t *Tracker) D {
|
|||||||
defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
||||||
defer op.Affine(f32.Affine2D{}.Rotate(f32.Pt(0, 0), -90*math.Pi/180).Offset(f32.Point{X: 0, Y: float32(h)})).Push(gtx.Ops).Pop()
|
defer op.Affine(f32.Affine2D{}.Rotate(f32.Pt(0, 0), -90*math.Pi/180).Offset(f32.Point{X: 0, Y: float32(h)})).Push(gtx.Ops).Pop()
|
||||||
gtx.Constraints = layout.Exact(image.Pt(1e6, 1e6))
|
gtx.Constraints = layout.Exact(image.Pt(1e6, 1e6))
|
||||||
LabelStyle{
|
Label(t.Theme, &t.Theme.OrderEditor.TrackTitle, t.Model.TrackTitle(i)).Layout(gtx)
|
||||||
Alignment: layout.NW,
|
|
||||||
Text: t.Model.TrackTitle(i),
|
|
||||||
FontSize: unit.Sp(12),
|
|
||||||
Color: mediumEmphasisTextColor,
|
|
||||||
Shaper: t.Theme.Shaper,
|
|
||||||
}.Layout(gtx)
|
|
||||||
return D{Size: image.Pt(gtx.Dp(patternCellWidth), h)}
|
return D{Size: image.Pt(gtx.Dp(patternCellWidth), h)}
|
||||||
}
|
}
|
||||||
|
|
||||||
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{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rowMarkerPatternTextColorOp := colorOp(gtx, t.Theme.OrderEditor.RowTitle.Color)
|
||||||
|
loopMarkerColorOp := colorOp(gtx, t.Theme.OrderEditor.Loop)
|
||||||
|
|
||||||
rowTitle := func(gtx C, j int) D {
|
rowTitle := func(gtx C, j int) D {
|
||||||
w := gtx.Dp(unit.Dp(30))
|
w := gtx.Dp(unit.Dp(30))
|
||||||
color := rowMarkerPatternTextColor
|
callOp := rowMarkerPatternTextColorOp
|
||||||
if l := t.Loop(); j >= l.Start && j < l.Start+l.Length {
|
if l := t.Loop(); j >= l.Start && j < l.Start+l.Length {
|
||||||
color = loopMarkerColor
|
callOp = loopMarkerColorOp
|
||||||
}
|
}
|
||||||
paint.ColorOp{Color: color}.Add(gtx.Ops)
|
|
||||||
defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
||||||
widget.Label{}.Layout(gtx, t.Theme.Shaper, trackerFont, trackerFontSize, strings.ToUpper(fmt.Sprintf("%02x", j)), op.CallOp{})
|
widget.Label{}.Layout(gtx, t.Theme.Material.Shaper, t.Theme.OrderEditor.RowTitle.Font, t.Theme.OrderEditor.RowTitle.TextSize, strings.ToUpper(fmt.Sprintf("%02x", j)), callOp)
|
||||||
return D{Size: image.Pt(w, gtx.Dp(patternCellHeight))}
|
return D{Size: image.Pt(w, gtx.Dp(patternCellHeight))}
|
||||||
}
|
}
|
||||||
|
|
||||||
selection := oe.scrollTable.Table.Range()
|
selection := oe.scrollTable.Table.Range()
|
||||||
|
cellColorOp := colorOp(gtx, t.Theme.OrderEditor.Cell.Color)
|
||||||
|
|
||||||
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 = inactiveSelectionColor
|
color = t.Theme.Selection.Inactive
|
||||||
if oe.scrollTable.Focused() {
|
if oe.scrollTable.Focused() {
|
||||||
color = selectionColor
|
color = t.Theme.Selection.Active
|
||||||
if point == oe.scrollTable.Table.Cursor() {
|
}
|
||||||
color = cursorColor
|
if point == oe.scrollTable.Table.Cursor() {
|
||||||
|
color = t.Theme.Cursor.Inactive
|
||||||
|
if oe.scrollTable.Focused() {
|
||||||
|
color = t.Theme.Cursor.Active
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paint.FillShape(gtx.Ops, color, clip.Rect{Min: image.Pt(1, 1), Max: image.Pt(gtx.Constraints.Min.X-1, gtx.Constraints.Min.X-1)}.Op())
|
paint.FillShape(gtx.Ops, color, clip.Rect{Min: image.Pt(1, 1), Max: image.Pt(gtx.Constraints.Min.X-1, gtx.Constraints.Min.X-1)}.Op())
|
||||||
paint.ColorOp{Color: patternTextColor}.Add(gtx.Ops)
|
|
||||||
defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
defer op.Offset(image.Pt(0, -2)).Push(gtx.Ops).Pop()
|
||||||
widget.Label{Alignment: text.Middle}.Layout(gtx, t.Theme.Shaper, trackerFont, trackerFontSize, val, op.CallOp{})
|
widget.Label{Alignment: text.Middle}.Layout(gtx, t.Theme.Material.Shaper, t.Theme.OrderEditor.Cell.Font, t.Theme.OrderEditor.Cell.TextSize, val, cellColorOp)
|
||||||
return D{Size: image.Pt(gtx.Dp(patternCellWidth), gtx.Dp(patternCellHeight))}
|
return D{Size: image.Pt(gtx.Dp(patternCellWidth), gtx.Dp(patternCellHeight))}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,12 +12,11 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Oscilloscope struct {
|
OscilloscopeState struct {
|
||||||
onceBtn *BoolClickable
|
onceBtn *BoolClickable
|
||||||
wrapBtn *BoolClickable
|
wrapBtn *BoolClickable
|
||||||
lengthInBeatsNumber *NumberInput
|
lengthInBeatsNumber *NumberInput
|
||||||
@ -31,16 +30,21 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
OscilloscopeStyle struct {
|
OscilloscopeStyle struct {
|
||||||
Oscilloscope *Oscilloscope
|
CurveColors [2]color.NRGBA `yaml:",flow"`
|
||||||
Wave tracker.RingBuffer[[2]float32]
|
LimitColor color.NRGBA `yaml:",flow"`
|
||||||
Colors [2]color.NRGBA
|
CursorColor color.NRGBA `yaml:",flow"`
|
||||||
ClippedColor color.NRGBA
|
}
|
||||||
Theme *material.Theme
|
|
||||||
|
Oscilloscope struct {
|
||||||
|
State *OscilloscopeState
|
||||||
|
Wave tracker.RingBuffer[[2]float32]
|
||||||
|
Theme *Theme
|
||||||
|
OscilloscopeStyle
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewOscilloscope(model *tracker.Model) *Oscilloscope {
|
func NewOscilloscope(model *tracker.Model) *OscilloscopeState {
|
||||||
return &Oscilloscope{
|
return &OscilloscopeState{
|
||||||
onceBtn: NewBoolClickable(model.SignalAnalyzer().Once().Bool()),
|
onceBtn: NewBoolClickable(model.SignalAnalyzer().Once().Bool()),
|
||||||
wrapBtn: NewBoolClickable(model.SignalAnalyzer().Wrap().Bool()),
|
wrapBtn: NewBoolClickable(model.SignalAnalyzer().Wrap().Bool()),
|
||||||
lengthInBeatsNumber: NewNumberInput(model.SignalAnalyzer().LengthInBeats().Int()),
|
lengthInBeatsNumber: NewNumberInput(model.SignalAnalyzer().LengthInBeats().Int()),
|
||||||
@ -48,15 +52,15 @@ func NewOscilloscope(model *tracker.Model) *Oscilloscope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LineOscilloscope(s *Oscilloscope, wave tracker.RingBuffer[[2]float32], th *material.Theme) *OscilloscopeStyle {
|
func Scope(s *OscilloscopeState, wave tracker.RingBuffer[[2]float32], th *Theme) *Oscilloscope {
|
||||||
return &OscilloscopeStyle{Oscilloscope: s, Wave: wave, Colors: [2]color.NRGBA{primaryColor, secondaryColor}, Theme: th, ClippedColor: errorColor}
|
return &Oscilloscope{State: s, Wave: wave, Theme: th}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OscilloscopeStyle) Layout(gtx C) D {
|
func (s *Oscilloscope) Layout(gtx C) D {
|
||||||
wrapBtnStyle := ToggleButton(gtx, s.Theme, s.Oscilloscope.wrapBtn, "Wrap")
|
wrapBtnStyle := ToggleButton(gtx, s.Theme, s.State.wrapBtn, "Wrap")
|
||||||
onceBtnStyle := ToggleButton(gtx, s.Theme, s.Oscilloscope.onceBtn, "Once")
|
onceBtnStyle := ToggleButton(gtx, s.Theme, s.State.onceBtn, "Once")
|
||||||
triggerChannelStyle := NumericUpDown(s.Theme, s.Oscilloscope.triggerChannelNumber, "Trigger channel")
|
triggerChannelStyle := NumUpDown(s.Theme, s.State.triggerChannelNumber, "Trigger channel")
|
||||||
lengthNumberStyle := NumericUpDown(s.Theme, s.Oscilloscope.lengthInBeatsNumber, "Buffer length in beats")
|
lengthNumberStyle := NumUpDown(s.Theme, s.State.lengthInBeatsNumber, "Buffer length in beats")
|
||||||
|
|
||||||
leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout
|
leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout
|
||||||
rightSpacer := layout.Spacer{Width: unit.Dp(6)}.Layout
|
rightSpacer := layout.Spacer{Width: unit.Dp(6)}.Layout
|
||||||
@ -66,7 +70,7 @@ func (s *OscilloscopeStyle) Layout(gtx C) D {
|
|||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Rigid(leftSpacer),
|
layout.Rigid(leftSpacer),
|
||||||
layout.Rigid(LabelStyle{Text: "Trigger", Color: disabledTextColor, Alignment: layout.W, FontSize: s.Theme.TextSize * 14.0 / 16.0, Shaper: s.Theme.Shaper}.Layout),
|
layout.Rigid(Label(s.Theme, &s.Theme.SongPanel.RowHeader, "Trigger").Layout),
|
||||||
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
|
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
|
||||||
layout.Rigid(onceBtnStyle.Layout),
|
layout.Rigid(onceBtnStyle.Layout),
|
||||||
layout.Rigid(triggerChannelStyle.Layout),
|
layout.Rigid(triggerChannelStyle.Layout),
|
||||||
@ -76,7 +80,7 @@ func (s *OscilloscopeStyle) Layout(gtx C) D {
|
|||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Rigid(leftSpacer),
|
layout.Rigid(leftSpacer),
|
||||||
layout.Rigid(LabelStyle{Text: "Buffer", Color: disabledTextColor, Alignment: layout.W, FontSize: s.Theme.TextSize * 14.0 / 16.0, Shaper: s.Theme.Shaper}.Layout),
|
layout.Rigid(Label(s.Theme, &s.Theme.SongPanel.RowHeader, "Buffer").Layout),
|
||||||
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
|
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
|
||||||
layout.Rigid(wrapBtnStyle.Layout),
|
layout.Rigid(wrapBtnStyle.Layout),
|
||||||
layout.Rigid(lengthNumberStyle.Layout),
|
layout.Rigid(lengthNumberStyle.Layout),
|
||||||
@ -86,17 +90,17 @@ func (s *OscilloscopeStyle) Layout(gtx C) D {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OscilloscopeStyle) layoutWave(gtx C) D {
|
func (s *Oscilloscope) layoutWave(gtx C) D {
|
||||||
s.update(gtx)
|
s.update(gtx)
|
||||||
if gtx.Constraints.Max.X == 0 || gtx.Constraints.Max.Y == 0 {
|
if gtx.Constraints.Max.X == 0 || gtx.Constraints.Max.Y == 0 {
|
||||||
return D{}
|
return D{}
|
||||||
}
|
}
|
||||||
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Push(gtx.Ops).Pop()
|
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Push(gtx.Ops).Pop()
|
||||||
event.Op(gtx.Ops, s.Oscilloscope)
|
event.Op(gtx.Ops, s.State)
|
||||||
paint.ColorOp{Color: oscilloscopeCursorColor}.Add(gtx.Ops)
|
paint.ColorOp{Color: s.Theme.Oscilloscope.CursorColor}.Add(gtx.Ops)
|
||||||
cursorX := int(s.sampleToPx(gtx, float32(s.Wave.Cursor)))
|
cursorX := int(s.sampleToPx(gtx, float32(s.Wave.Cursor)))
|
||||||
fillRect(gtx, clip.Rect{Min: image.Pt(cursorX, 0), Max: image.Pt(cursorX+1, gtx.Constraints.Max.Y)})
|
fillRect(gtx, clip.Rect{Min: image.Pt(cursorX, 0), Max: image.Pt(cursorX+1, gtx.Constraints.Max.Y)})
|
||||||
paint.ColorOp{Color: oscilloscopeLimitColor}.Add(gtx.Ops)
|
paint.ColorOp{Color: s.Theme.Oscilloscope.LimitColor}.Add(gtx.Ops)
|
||||||
minusOneY := int(s.ampToY(gtx, -1))
|
minusOneY := int(s.ampToY(gtx, -1))
|
||||||
fillRect(gtx, clip.Rect{Min: image.Pt(0, minusOneY), Max: image.Pt(gtx.Constraints.Max.X, minusOneY+1)})
|
fillRect(gtx, clip.Rect{Min: image.Pt(0, minusOneY), Max: image.Pt(gtx.Constraints.Max.X, minusOneY+1)})
|
||||||
plusOneY := int(s.ampToY(gtx, 1))
|
plusOneY := int(s.ampToY(gtx, 1))
|
||||||
@ -106,7 +110,7 @@ func (s *OscilloscopeStyle) layoutWave(gtx C) D {
|
|||||||
rightX := int(s.sampleToPx(gtx, float32(len(s.Wave.Buffer)-1)))
|
rightX := int(s.sampleToPx(gtx, float32(len(s.Wave.Buffer)-1)))
|
||||||
fillRect(gtx, clip.Rect{Min: image.Pt(rightX, 0), Max: image.Pt(rightX+1, gtx.Constraints.Max.Y)})
|
fillRect(gtx, clip.Rect{Min: image.Pt(rightX, 0), Max: image.Pt(rightX+1, gtx.Constraints.Max.Y)})
|
||||||
for chn := range 2 {
|
for chn := range 2 {
|
||||||
paint.ColorOp{Color: s.Colors[chn]}.Add(gtx.Ops)
|
paint.ColorOp{Color: s.Theme.Oscilloscope.CurveColors[chn]}.Add(gtx.Ops)
|
||||||
for px := range gtx.Constraints.Max.X {
|
for px := range gtx.Constraints.Max.X {
|
||||||
// left and right is the sample range covered by the pixel
|
// left and right is the sample range covered by the pixel
|
||||||
left := int(s.pxToSample(gtx, float32(px)-0.5))
|
left := int(s.pxToSample(gtx, float32(px)-0.5))
|
||||||
@ -138,10 +142,10 @@ func fillRect(gtx C, rect clip.Rect) {
|
|||||||
stack.Pop()
|
stack.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OscilloscopeStyle) update(gtx C) {
|
func (o *Oscilloscope) update(gtx C) {
|
||||||
for {
|
for {
|
||||||
ev, ok := gtx.Event(pointer.Filter{
|
ev, ok := gtx.Event(pointer.Filter{
|
||||||
Target: o.Oscilloscope,
|
Target: o.State,
|
||||||
Kinds: pointer.Scroll | pointer.Press | pointer.Drag | pointer.Release | pointer.Cancel,
|
Kinds: pointer.Scroll | pointer.Press | pointer.Drag | pointer.Release | pointer.Cancel,
|
||||||
ScrollY: pointer.ScrollRange{Min: -1e6, Max: 1e6},
|
ScrollY: pointer.ScrollRange{Min: -1e6, Max: 1e6},
|
||||||
})
|
})
|
||||||
@ -152,58 +156,58 @@ func (o *OscilloscopeStyle) update(gtx C) {
|
|||||||
switch e.Kind {
|
switch e.Kind {
|
||||||
case pointer.Scroll:
|
case pointer.Scroll:
|
||||||
s1 := o.pxToSample(gtx, e.Position.X)
|
s1 := o.pxToSample(gtx, e.Position.X)
|
||||||
o.Oscilloscope.xScale += min(max(-1, int(e.Scroll.Y)), 1)
|
o.State.xScale += min(max(-1, int(e.Scroll.Y)), 1)
|
||||||
s2 := o.pxToSample(gtx, e.Position.X)
|
s2 := o.pxToSample(gtx, e.Position.X)
|
||||||
o.Oscilloscope.xOffset -= s1 - s2
|
o.State.xOffset -= s1 - s2
|
||||||
case pointer.Press:
|
case pointer.Press:
|
||||||
if e.Buttons&pointer.ButtonSecondary != 0 {
|
if e.Buttons&pointer.ButtonSecondary != 0 {
|
||||||
o.Oscilloscope.xOffset = 0
|
o.State.xOffset = 0
|
||||||
o.Oscilloscope.xScale = 0
|
o.State.xScale = 0
|
||||||
o.Oscilloscope.yScale = 0
|
o.State.yScale = 0
|
||||||
}
|
}
|
||||||
if e.Buttons&pointer.ButtonPrimary != 0 {
|
if e.Buttons&pointer.ButtonPrimary != 0 {
|
||||||
o.Oscilloscope.dragging = true
|
o.State.dragging = true
|
||||||
o.Oscilloscope.dragId = e.PointerID
|
o.State.dragId = e.PointerID
|
||||||
o.Oscilloscope.dragStartPoint = e.Position
|
o.State.dragStartPoint = e.Position
|
||||||
}
|
}
|
||||||
case pointer.Drag:
|
case pointer.Drag:
|
||||||
if e.Buttons&pointer.ButtonPrimary != 0 && o.Oscilloscope.dragging && e.PointerID == o.Oscilloscope.dragId {
|
if e.Buttons&pointer.ButtonPrimary != 0 && o.State.dragging && e.PointerID == o.State.dragId {
|
||||||
deltaX := o.pxToSample(gtx, e.Position.X) - o.pxToSample(gtx, o.Oscilloscope.dragStartPoint.X)
|
deltaX := o.pxToSample(gtx, e.Position.X) - o.pxToSample(gtx, o.State.dragStartPoint.X)
|
||||||
o.Oscilloscope.xOffset += deltaX
|
o.State.xOffset += deltaX
|
||||||
num := o.yToAmp(gtx, e.Position.Y)
|
num := o.yToAmp(gtx, e.Position.Y)
|
||||||
den := o.yToAmp(gtx, o.Oscilloscope.dragStartPoint.Y)
|
den := o.yToAmp(gtx, o.State.dragStartPoint.Y)
|
||||||
if l := math.Abs(float64(num / den)); l > 1e-3 && l < 1e3 {
|
if l := math.Abs(float64(num / den)); l > 1e-3 && l < 1e3 {
|
||||||
o.Oscilloscope.yScale += math.Log(l)
|
o.State.yScale += math.Log(l)
|
||||||
o.Oscilloscope.yScale = min(max(o.Oscilloscope.yScale, -1e3), 1e3)
|
o.State.yScale = min(max(o.State.yScale, -1e3), 1e3)
|
||||||
}
|
}
|
||||||
o.Oscilloscope.dragStartPoint = e.Position
|
o.State.dragStartPoint = e.Position
|
||||||
|
|
||||||
}
|
}
|
||||||
case pointer.Release | pointer.Cancel:
|
case pointer.Release | pointer.Cancel:
|
||||||
o.Oscilloscope.dragging = false
|
o.State.dragging = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OscilloscopeStyle) scaleFactor() float32 {
|
func (o *Oscilloscope) scaleFactor() float32 {
|
||||||
return float32(math.Pow(1.1, float64(o.Oscilloscope.xScale)))
|
return float32(math.Pow(1.1, float64(o.State.xScale)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OscilloscopeStyle) pxToSample(gtx C, px float32) float32 {
|
func (s *Oscilloscope) pxToSample(gtx C, px float32) float32 {
|
||||||
return px*s.scaleFactor()*float32(len(s.Wave.Buffer))/float32(gtx.Constraints.Max.X) - s.Oscilloscope.xOffset
|
return px*s.scaleFactor()*float32(len(s.Wave.Buffer))/float32(gtx.Constraints.Max.X) - s.State.xOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OscilloscopeStyle) sampleToPx(gtx C, sample float32) float32 {
|
func (s *Oscilloscope) sampleToPx(gtx C, sample float32) float32 {
|
||||||
return (sample + s.Oscilloscope.xOffset) * float32(gtx.Constraints.Max.X) / float32(len(s.Wave.Buffer)) / s.scaleFactor()
|
return (sample + s.State.xOffset) * float32(gtx.Constraints.Max.X) / float32(len(s.Wave.Buffer)) / s.scaleFactor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OscilloscopeStyle) ampToY(gtx C, amp float32) float32 {
|
func (s *Oscilloscope) ampToY(gtx C, amp float32) float32 {
|
||||||
scale := float32(math.Exp(s.Oscilloscope.yScale))
|
scale := float32(math.Exp(s.State.yScale))
|
||||||
return (1 - amp*scale) / 2 * float32(gtx.Constraints.Max.Y-1)
|
return (1 - amp*scale) / 2 * float32(gtx.Constraints.Max.Y-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OscilloscopeStyle) yToAmp(gtx C, y float32) float32 {
|
func (s *Oscilloscope) yToAmp(gtx C, y float32) float32 {
|
||||||
scale := float32(math.Exp(s.Oscilloscope.yScale))
|
scale := float32(math.Exp(s.State.yScale))
|
||||||
return (1 - y/float32(gtx.Constraints.Max.Y-1)*2) / scale
|
return (1 - y/float32(gtx.Constraints.Max.Y-1)*2) / scale
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -9,7 +9,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/text"
|
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
"github.com/vsariola/sointu/tracker"
|
"github.com/vsariola/sointu/tracker"
|
||||||
)
|
)
|
||||||
@ -17,17 +16,21 @@ import (
|
|||||||
type PopupAlert struct {
|
type PopupAlert struct {
|
||||||
alerts *tracker.Alerts
|
alerts *tracker.Alerts
|
||||||
prevUpdate time.Time
|
prevUpdate time.Time
|
||||||
shaper *text.Shaper
|
}
|
||||||
|
|
||||||
|
type PopupAlertStyle struct {
|
||||||
|
Bg color.NRGBA
|
||||||
|
Text LabelStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
var alertMargin = layout.UniformInset(unit.Dp(6))
|
var alertMargin = layout.UniformInset(unit.Dp(6))
|
||||||
var alertInset = layout.UniformInset(unit.Dp(6))
|
var alertInset = layout.UniformInset(unit.Dp(6))
|
||||||
|
|
||||||
func NewPopupAlert(alerts *tracker.Alerts, shaper *text.Shaper) *PopupAlert {
|
func NewPopupAlert(alerts *tracker.Alerts) *PopupAlert {
|
||||||
return &PopupAlert{alerts: alerts, shaper: shaper, prevUpdate: time.Now()}
|
return &PopupAlert{alerts: alerts, prevUpdate: time.Now()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *PopupAlert) Layout(gtx C) D {
|
func (a *PopupAlert) Layout(gtx C, th *Theme) D {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if a.alerts.Update(now.Sub(a.prevUpdate)) {
|
if a.alerts.Update(now.Sub(a.prevUpdate)) {
|
||||||
gtx.Execute(op.InvalidateCmd{At: now.Add(50 * time.Millisecond)})
|
gtx.Execute(op.InvalidateCmd{At: now.Add(50 * time.Millisecond)})
|
||||||
@ -36,26 +39,22 @@ func (a *PopupAlert) Layout(gtx C) D {
|
|||||||
|
|
||||||
var totalY float64 = float64(gtx.Dp(38))
|
var totalY float64 = float64(gtx.Dp(38))
|
||||||
for _, alert := range a.alerts.Iterate {
|
for _, alert := range a.alerts.Iterate {
|
||||||
var color, textColor, shadeColor color.NRGBA
|
var alertStyle *PopupAlertStyle
|
||||||
switch alert.Priority {
|
switch alert.Priority {
|
||||||
case tracker.Warning:
|
case tracker.Warning:
|
||||||
color = warningColor
|
alertStyle = &th.Alert.Warning
|
||||||
textColor = black
|
|
||||||
case tracker.Error:
|
case tracker.Error:
|
||||||
color = errorColor
|
alertStyle = &th.Alert.Error
|
||||||
textColor = black
|
|
||||||
default:
|
default:
|
||||||
color = popupSurfaceColor
|
alertStyle = &th.Alert.Info
|
||||||
textColor = white
|
|
||||||
shadeColor = black
|
|
||||||
}
|
}
|
||||||
bgWidget := func(gtx C) D {
|
bgWidget := func(gtx C) D {
|
||||||
paint.FillShape(gtx.Ops, color, clip.Rect{
|
paint.FillShape(gtx.Ops, alertStyle.Bg, clip.Rect{
|
||||||
Max: gtx.Constraints.Min,
|
Max: gtx.Constraints.Min,
|
||||||
}.Op())
|
}.Op())
|
||||||
return D{Size: gtx.Constraints.Min}
|
return D{Size: gtx.Constraints.Min}
|
||||||
}
|
}
|
||||||
labelStyle := LabelStyle{Text: alert.Message, Color: textColor, ShadeColor: shadeColor, Font: labelDefaultFont, Alignment: layout.Center, FontSize: unit.Sp(16), Shaper: a.shaper}
|
labelStyle := Label(th, &alertStyle.Text, alert.Message)
|
||||||
alertMargin.Layout(gtx, func(gtx C) D {
|
alertMargin.Layout(gtx, func(gtx C) D {
|
||||||
return layout.S.Layout(gtx, func(gtx C) D {
|
return layout.S.Layout(gtx, func(gtx C) D {
|
||||||
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
|
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
"gioui.org/op/clip"
|
"gioui.org/op/clip"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
"gioui.org/widget/material"
|
|
||||||
"github.com/vsariola/sointu/tracker"
|
"github.com/vsariola/sointu/tracker"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -72,7 +71,7 @@ func NewScrollTable(table tracker.Table, vertList, horizList tracker.List) *Scro
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func FilledScrollTable(th *material.Theme, scrollTable *ScrollTable, element func(gtx C, x, y int) D, colTitle, rowTitle, colTitleBg, rowTitleBg func(gtx C, i int) D) ScrollTableStyle {
|
func FilledScrollTable(th *Theme, scrollTable *ScrollTable, element func(gtx C, x, y int) D, colTitle, rowTitle, colTitleBg, rowTitleBg func(gtx C, i int) D) ScrollTableStyle {
|
||||||
return ScrollTableStyle{
|
return ScrollTableStyle{
|
||||||
RowTitleStyle: FilledDragList(th, scrollTable.RowTitleList, rowTitle, rowTitleBg),
|
RowTitleStyle: FilledDragList(th, scrollTable.RowTitleList, rowTitle, rowTitleBg),
|
||||||
ColTitleStyle: FilledDragList(th, scrollTable.ColTitleList, colTitle, colTitleBg),
|
ColTitleStyle: FilledDragList(th, scrollTable.ColTitleList, colTitle, colTitleBg),
|
||||||
|
@ -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)
|
||||||
|
@ -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"
|
||||||
@ -32,7 +31,7 @@ type SongPanel struct {
|
|||||||
Step *NumberInput
|
Step *NumberInput
|
||||||
SongLength *NumberInput
|
SongLength *NumberInput
|
||||||
|
|
||||||
Scope *Oscilloscope
|
Scope *OscilloscopeState
|
||||||
|
|
||||||
MenuBar *MenuBar
|
MenuBar *MenuBar
|
||||||
PlayBar *PlayBar
|
PlayBar *PlayBar
|
||||||
@ -76,17 +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 layout.Background{}.Layout(gtx,
|
return s.PlayBar.Layout(gtx, t.Theme)
|
||||||
func(gtx C) D {
|
|
||||||
// push defer clip op
|
|
||||||
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)).Push(gtx.Ops).Pop()
|
|
||||||
paint.FillShape(gtx.Ops, songSurfaceColor, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
|
|
||||||
return D{Size: image.Pt(gtx.Constraints.Min.X, gtx.Constraints.Min.Y)}
|
|
||||||
},
|
|
||||||
func(gtx C) D {
|
|
||||||
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)
|
||||||
@ -95,9 +84,7 @@ func (s *SongPanel) Layout(gtx C, t *Tracker) D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
||||||
paint.FillShape(gtx.Ops, songSurfaceColor, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
|
paint.FillShape(gtx.Ops, tr.Theme.SongPanel.Bg, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
|
||||||
|
|
||||||
scopeStyle := LineOscilloscope(t.Scope, tr.SignalAnalyzer().Waveform(), tr.Theme)
|
|
||||||
|
|
||||||
var weightingTxt string
|
var weightingTxt string
|
||||||
switch tracker.WeightingType(tr.Model.DetectorWeighting().Value()) {
|
switch tracker.WeightingType(tr.Model.DetectorWeighting().Value()) {
|
||||||
@ -111,38 +98,36 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
|||||||
weightingTxt = "No weight (RMS)"
|
weightingTxt = "No weight (RMS)"
|
||||||
}
|
}
|
||||||
|
|
||||||
weightingBtn := LowEmphasisButton(tr.Theme, t.WeightingTypeBtn, weightingTxt)
|
weightingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.WeightingTypeBtn, weightingTxt)
|
||||||
weightingBtn.Color = mediumEmphasisTextColor
|
|
||||||
|
|
||||||
oversamplingTxt := "Sample peak"
|
oversamplingTxt := "Sample peak"
|
||||||
if tr.Model.Oversampling().Value() {
|
if tr.Model.Oversampling().Value() {
|
||||||
oversamplingTxt = "True peak"
|
oversamplingTxt = "True peak"
|
||||||
}
|
}
|
||||||
oversamplingBtn := LowEmphasisButton(tr.Theme, t.OversamplingBtn, oversamplingTxt)
|
oversamplingBtn := Btn(tr.Theme, &tr.Theme.Button.Text, t.OversamplingBtn, oversamplingTxt)
|
||||||
oversamplingBtn.Color = mediumEmphasisTextColor
|
|
||||||
|
|
||||||
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 {
|
||||||
return t.SongSettingsExpander.Layout(gtx, tr.Theme, "Song",
|
return t.SongSettingsExpander.Layout(gtx, tr.Theme, "Song",
|
||||||
func(gtx C) D {
|
func(gtx C) D {
|
||||||
return LabelStyle{Text: strconv.Itoa(tr.BPM().Value()) + " BPM", Color: mediumEmphasisTextColor, Alignment: layout.W, FontSize: tr.Theme.TextSize * 14.0 / 16.0, Shaper: tr.Theme.Shaper}.Layout(gtx)
|
return Label(tr.Theme, &tr.Theme.SongPanel.RowHeader, strconv.Itoa(tr.BPM().Value())+" BPM").Layout(gtx)
|
||||||
},
|
},
|
||||||
func(gtx C) D {
|
func(gtx C) D {
|
||||||
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 {
|
||||||
return layoutSongOptionRow(gtx, tr.Theme, "BPM", NumericUpDown(tr.Theme, t.BPM, "Song Length").Layout)
|
return layoutSongOptionRow(gtx, tr.Theme, "BPM", NumUpDown(tr.Theme, t.BPM, "Song Length").Layout)
|
||||||
}),
|
}),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
return layoutSongOptionRow(gtx, tr.Theme, "Song length", NumericUpDown(tr.Theme, t.SongLength, "Song Length").Layout)
|
return layoutSongOptionRow(gtx, tr.Theme, "Song length", NumUpDown(tr.Theme, t.SongLength, "Song Length").Layout)
|
||||||
}),
|
}),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
return layoutSongOptionRow(gtx, tr.Theme, "Rows per pat", NumericUpDown(tr.Theme, t.RowsPerPattern, "Rows per pattern").Layout)
|
return layoutSongOptionRow(gtx, tr.Theme, "Rows per pat", NumUpDown(tr.Theme, t.RowsPerPattern, "Rows per pattern").Layout)
|
||||||
}),
|
}),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
return layoutSongOptionRow(gtx, tr.Theme, "Rows per beat", NumericUpDown(tr.Theme, t.RowsPerBeat, "Rows per beat").Layout)
|
return layoutSongOptionRow(gtx, tr.Theme, "Rows per beat", NumUpDown(tr.Theme, t.RowsPerBeat, "Rows per beat").Layout)
|
||||||
}),
|
}),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
return layoutSongOptionRow(gtx, tr.Theme, "Cursor step", NumericUpDown(tr.Theme, t.Step, "Cursor step").Layout)
|
return layoutSongOptionRow(gtx, tr.Theme, "Cursor step", NumUpDown(tr.Theme, t.Step, "Cursor step").Layout)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -150,7 +135,7 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
|||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
return t.LoudnessExpander.Layout(gtx, tr.Theme, "Loudness",
|
return t.LoudnessExpander.Layout(gtx, tr.Theme, "Loudness",
|
||||||
func(gtx C) D {
|
func(gtx C) D {
|
||||||
return LabelStyle{Text: fmt.Sprintf("%.1f dB", tr.Model.DetectorResult().Loudness[tracker.LoudnessShortTerm]), Color: mediumEmphasisTextColor, Alignment: layout.W, FontSize: tr.Theme.TextSize * 14.0 / 16.0, Shaper: tr.Theme.Shaper}.Layout(gtx)
|
return Label(tr.Theme, &tr.Theme.SongPanel.RowHeader, fmt.Sprintf("%.1f dB", tr.Model.DetectorResult().Loudness[tracker.LoudnessShortTerm])).Layout(gtx)
|
||||||
},
|
},
|
||||||
func(gtx C) D {
|
func(gtx C) D {
|
||||||
return layout.Flex{Axis: layout.Vertical, Alignment: layout.End}.Layout(gtx,
|
return layout.Flex{Axis: layout.Vertical, Alignment: layout.End}.Layout(gtx,
|
||||||
@ -207,36 +192,27 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
layout.Flexed(1, func(gtx C) D {
|
layout.Flexed(1, func(gtx C) D {
|
||||||
return t.ScopeExpander.Layout(gtx, tr.Theme, "Oscilloscope", func(gtx C) D { return D{} }, scopeStyle.Layout)
|
return t.ScopeExpander.Layout(gtx, tr.Theme, "Oscilloscope", func(gtx C) D { return D{} }, Scope(t.Scope, tr.SignalAnalyzer().Waveform(), tr.Theme).Layout)
|
||||||
}),
|
|
||||||
layout.Rigid(func(gtx C) D {
|
|
||||||
labelStyle := LabelStyle{Text: version.VersionOrHash, FontSize: unit.Sp(12), Color: mediumEmphasisTextColor, Shaper: tr.Theme.Shaper}
|
|
||||||
return labelStyle.Layout(gtx)
|
|
||||||
}),
|
}),
|
||||||
|
layout.Rigid(Label(tr.Theme, &tr.Theme.SongPanel.Version, version.VersionOrHash).Layout),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func dbLabel(th *material.Theme, value tracker.Decibel) LabelStyle {
|
func dbLabel(th *Theme, value tracker.Decibel) LabelWidget {
|
||||||
color := mediumEmphasisTextColor
|
ret := Label(th, &th.SongPanel.RowValue, fmt.Sprintf("%.1f dB", value))
|
||||||
if value >= 0 {
|
if value >= 0 {
|
||||||
color = errorColor
|
ret.Color = th.SongPanel.ErrorColor
|
||||||
}
|
|
||||||
return LabelStyle{
|
|
||||||
Text: fmt.Sprintf("%.1f dB", value),
|
|
||||||
Color: color,
|
|
||||||
Alignment: layout.W,
|
|
||||||
FontSize: th.TextSize * 14.0 / 16.0,
|
|
||||||
Shaper: th.Shaper,
|
|
||||||
}
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func layoutSongOptionRow(gtx C, th *material.Theme, label string, widget layout.Widget) D {
|
func layoutSongOptionRow(gtx C, th *Theme, label string, widget layout.Widget) D {
|
||||||
leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout
|
leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout
|
||||||
rightSpacer := layout.Spacer{Width: unit.Dp(6)}.Layout
|
rightSpacer := layout.Spacer{Width: unit.Dp(6)}.Layout
|
||||||
|
|
||||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Rigid(leftSpacer),
|
layout.Rigid(leftSpacer),
|
||||||
layout.Rigid(LabelStyle{Text: label, Color: mediumEmphasisTextColor, Alignment: layout.W, FontSize: th.TextSize * 14.0 / 16.0, Shaper: th.Shaper}.Layout),
|
layout.Rigid(Label(th, &th.SongPanel.RowHeader, label).Layout),
|
||||||
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
|
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
|
||||||
layout.Rigid(widget),
|
layout.Rigid(widget),
|
||||||
layout.Rigid(rightSpacer),
|
layout.Rigid(rightSpacer),
|
||||||
@ -257,7 +233,7 @@ func (e *Expander) Update(gtx C) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Expander) Layout(gtx C, th *material.Theme, title string, smallWidget, largeWidget layout.Widget) D {
|
func (e *Expander) Layout(gtx C, th *Theme, title string, smallWidget, largeWidget layout.Widget) D {
|
||||||
e.Update(gtx)
|
e.Update(gtx)
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||||
layout.Rigid(func(gtx C) D { return e.layoutHeader(gtx, th, title, smallWidget) }),
|
layout.Rigid(func(gtx C) D { return e.layoutHeader(gtx, th, title, smallWidget) }),
|
||||||
@ -275,7 +251,7 @@ func (e *Expander) Layout(gtx C, th *material.Theme, title string, smallWidget,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Expander) layoutHeader(gtx C, th *material.Theme, title string, smallWidget layout.Widget) D {
|
func (e *Expander) layoutHeader(gtx C, th *Theme, title string, smallWidget layout.Widget) D {
|
||||||
return layout.Background{}.Layout(gtx,
|
return layout.Background{}.Layout(gtx,
|
||||||
func(gtx C) D {
|
func(gtx C) D {
|
||||||
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)).Push(gtx.Ops).Pop()
|
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)).Push(gtx.Ops).Pop()
|
||||||
@ -287,7 +263,7 @@ func (e *Expander) layoutHeader(gtx C, th *material.Theme, title string, smallWi
|
|||||||
leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout
|
leftSpacer := layout.Spacer{Width: unit.Dp(6), Height: unit.Dp(24)}.Layout
|
||||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Rigid(leftSpacer),
|
layout.Rigid(leftSpacer),
|
||||||
layout.Rigid(LabelStyle{Text: title, Color: disabledTextColor, Alignment: layout.W, FontSize: th.TextSize * 14.0 / 16.0, Shaper: th.Shaper}.Layout),
|
layout.Rigid(Label(th, &th.SongPanel.Expander, title).Layout),
|
||||||
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
|
layout.Flexed(1, func(gtx C) D { return D{Size: gtx.Constraints.Min} }),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
if !e.Expanded {
|
if !e.Expanded {
|
||||||
@ -302,7 +278,7 @@ func (e *Expander) layoutHeader(gtx C, th *material.Theme, title string, smallWi
|
|||||||
icon = icons.NavigationExpandLess
|
icon = icons.NavigationExpandLess
|
||||||
}
|
}
|
||||||
gtx.Constraints.Min = image.Pt(gtx.Dp(unit.Dp(24)), gtx.Dp(unit.Dp(24)))
|
gtx.Constraints.Min = image.Pt(gtx.Dp(unit.Dp(24)), gtx.Dp(unit.Dp(24)))
|
||||||
return widgetForIcon(icon).Layout(gtx, th.Palette.Fg)
|
return widgetForIcon(icon).Layout(gtx, th.SongPanel.Expander.Color)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -359,7 +335,7 @@ func (t *MenuBar) Layout(gtx C, tr *Tracker) D {
|
|||||||
|
|
||||||
panicBtnStyle := ToggleIcon(gtx, tr.Theme, 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 = errorColor
|
panicBtnStyle.IconButtonStyle.Color = tr.Theme.SongPanel.ErrorColor
|
||||||
}
|
}
|
||||||
menuLayouts := []layout.FlexChild{
|
menuLayouts := []layout.FlexChild{
|
||||||
layout.Rigid(tr.layoutMenu(gtx, "File", &t.Clickables[0], &t.Menus[0], unit.Dp(200), t.fileMenuItems...)),
|
layout.Rigid(tr.layoutMenu(gtx, "File", &t.Clickables[0], &t.Menus[0], unit.Dp(200), t.fileMenuItems...)),
|
||||||
@ -411,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)
|
||||||
|
@ -1,82 +1,143 @@
|
|||||||
package gioui
|
package gioui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"gioui.org/font/gofont"
|
|
||||||
"gioui.org/text"
|
"gioui.org/text"
|
||||||
"gioui.org/unit"
|
"gioui.org/widget"
|
||||||
|
"gioui.org/widget/material"
|
||||||
|
"golang.org/x/exp/shiny/materialdesign/icons"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fontCollection []text.FontFace = gofont.Collection()
|
type Theme struct {
|
||||||
|
Define any // this is just needed for yaml.UnmarshalStrict, so we can have "defines" in the yaml
|
||||||
|
Material material.Theme
|
||||||
|
Button struct {
|
||||||
|
Filled ButtonStyle
|
||||||
|
Text ButtonStyle
|
||||||
|
Disabled ButtonStyle
|
||||||
|
Menu ButtonStyle
|
||||||
|
}
|
||||||
|
Oscilloscope OscilloscopeStyle
|
||||||
|
NumericUpDown NumericUpDownStyle
|
||||||
|
DialogTitle LabelStyle
|
||||||
|
DialogText LabelStyle
|
||||||
|
SongPanel struct {
|
||||||
|
RowHeader LabelStyle
|
||||||
|
RowValue LabelStyle
|
||||||
|
Expander LabelStyle
|
||||||
|
Version LabelStyle
|
||||||
|
ErrorColor color.NRGBA
|
||||||
|
Bg color.NRGBA
|
||||||
|
}
|
||||||
|
Alert struct {
|
||||||
|
Warning PopupAlertStyle
|
||||||
|
Error PopupAlertStyle
|
||||||
|
Info PopupAlertStyle
|
||||||
|
}
|
||||||
|
NoteEditor struct {
|
||||||
|
TrackTitle LabelStyle
|
||||||
|
OrderRow LabelStyle
|
||||||
|
PatternRow LabelStyle
|
||||||
|
Note LabelStyle
|
||||||
|
PatternNo LabelStyle
|
||||||
|
Unique LabelStyle
|
||||||
|
Loop color.NRGBA
|
||||||
|
Header LabelStyle
|
||||||
|
Play color.NRGBA
|
||||||
|
OneBeat color.NRGBA
|
||||||
|
TwoBeat color.NRGBA
|
||||||
|
}
|
||||||
|
Dialog struct {
|
||||||
|
Bg color.NRGBA
|
||||||
|
Title LabelStyle
|
||||||
|
Text LabelStyle
|
||||||
|
}
|
||||||
|
OrderEditor struct {
|
||||||
|
TrackTitle LabelStyle
|
||||||
|
RowTitle LabelStyle
|
||||||
|
Cell LabelStyle
|
||||||
|
Loop color.NRGBA
|
||||||
|
CellBg color.NRGBA
|
||||||
|
Play color.NRGBA
|
||||||
|
}
|
||||||
|
Menu struct {
|
||||||
|
Text LabelStyle
|
||||||
|
ShortCut color.NRGBA
|
||||||
|
Hover color.NRGBA
|
||||||
|
Disabled color.NRGBA
|
||||||
|
}
|
||||||
|
InstrumentEditor struct {
|
||||||
|
Octave LabelStyle
|
||||||
|
Voices LabelStyle
|
||||||
|
InstrumentComment EditorStyle
|
||||||
|
UnitComment EditorStyle
|
||||||
|
InstrumentList struct {
|
||||||
|
Number LabelStyle
|
||||||
|
Name EditorStyle
|
||||||
|
NameMuted EditorStyle
|
||||||
|
ScrollBar ScrollBarStyle
|
||||||
|
}
|
||||||
|
UnitList struct {
|
||||||
|
Name EditorStyle
|
||||||
|
NameDisabled EditorStyle
|
||||||
|
Comment LabelStyle
|
||||||
|
Stack LabelStyle
|
||||||
|
Disabled LabelStyle
|
||||||
|
Warning color.NRGBA
|
||||||
|
Error color.NRGBA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnitEditor struct {
|
||||||
|
Hint LabelStyle
|
||||||
|
Chooser LabelStyle
|
||||||
|
ParameterName LabelStyle
|
||||||
|
InvalidParam color.NRGBA
|
||||||
|
SendTarget color.NRGBA
|
||||||
|
}
|
||||||
|
Cursor CursorStyle
|
||||||
|
Selection CursorStyle
|
||||||
|
Tooltip struct {
|
||||||
|
Color color.NRGBA
|
||||||
|
Bg color.NRGBA
|
||||||
|
}
|
||||||
|
Popup struct {
|
||||||
|
Bg color.NRGBA
|
||||||
|
Shadow color.NRGBA
|
||||||
|
}
|
||||||
|
ScrollBar ScrollBarStyle
|
||||||
|
}
|
||||||
|
|
||||||
var white = color.NRGBA{R: 255, G: 255, B: 255, A: 255}
|
type CursorStyle struct {
|
||||||
var black = color.NRGBA{R: 0, G: 0, B: 0, A: 255}
|
Active color.NRGBA
|
||||||
var transparent = color.NRGBA{A: 0}
|
ActiveAlt color.NRGBA // alternative color for the cursor, used e.g. when the midi input is active
|
||||||
|
Inactive color.NRGBA
|
||||||
|
}
|
||||||
|
|
||||||
var primaryColor = color.NRGBA{R: 206, G: 147, B: 216, A: 255}
|
//go:embed theme.yml
|
||||||
var secondaryColor = color.NRGBA{R: 128, G: 222, B: 234, A: 255}
|
var defaultTheme []byte
|
||||||
|
|
||||||
var highEmphasisTextColor = color.NRGBA{R: 222, G: 222, B: 222, A: 222}
|
func NewTheme() *Theme {
|
||||||
var mediumEmphasisTextColor = color.NRGBA{R: 153, G: 153, B: 153, A: 153}
|
var theme Theme
|
||||||
var disabledTextColor = color.NRGBA{R: 255, G: 255, B: 255, A: 97}
|
err := yaml.UnmarshalStrict(defaultTheme, &theme)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("failed to default theme: %w", err))
|
||||||
|
}
|
||||||
|
ReadCustomConfigYml("theme.yml", &theme)
|
||||||
|
theme.Material.Shaper = &text.Shaper{}
|
||||||
|
theme.Material.Icon.CheckBoxChecked = must(widget.NewIcon(icons.ToggleCheckBox))
|
||||||
|
theme.Material.Icon.CheckBoxUnchecked = must(widget.NewIcon(icons.ToggleCheckBoxOutlineBlank))
|
||||||
|
theme.Material.Icon.RadioChecked = must(widget.NewIcon(icons.ToggleRadioButtonChecked))
|
||||||
|
theme.Material.Icon.RadioUnchecked = must(widget.NewIcon(icons.ToggleRadioButtonUnchecked))
|
||||||
|
return &theme
|
||||||
|
}
|
||||||
|
|
||||||
var backgroundColor = color.NRGBA{R: 18, G: 18, B: 18, A: 255}
|
func must[T any](ic T, err error) T {
|
||||||
|
if err != nil {
|
||||||
var labelDefaultFont = fontCollection[6].Font
|
panic(err)
|
||||||
var labelDefaultFontSize = unit.Sp(18)
|
}
|
||||||
|
return ic
|
||||||
var rowMarkerPatternTextColor = secondaryColor
|
}
|
||||||
var rowMarkerRowTextColor = mediumEmphasisTextColor
|
|
||||||
|
|
||||||
var trackerFont = fontCollection[6].Font
|
|
||||||
var trackerFontSize = unit.Sp(16)
|
|
||||||
var trackerInactiveTextColor = highEmphasisTextColor
|
|
||||||
var trackerActiveTextColor = color.NRGBA{R: 255, G: 255, B: 130, A: 255}
|
|
||||||
var trackerPlayColor = color.NRGBA{R: 55, G: 55, B: 61, A: 255}
|
|
||||||
var trackerPatMarker = primaryColor
|
|
||||||
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 patternTextColor = primaryColor
|
|
||||||
var patternCellColor = color.NRGBA{R: 255, G: 255, B: 255, A: 3}
|
|
||||||
var loopMarkerColor = color.NRGBA{R: 252, G: 186, B: 3, A: 255}
|
|
||||||
|
|
||||||
var instrumentHoverColor = color.NRGBA{R: 30, G: 31, B: 38, A: 255}
|
|
||||||
var instrumentNameHintColor = color.NRGBA{R: 200, G: 200, B: 200, A: 255}
|
|
||||||
|
|
||||||
var songSurfaceColor = color.NRGBA{R: 24, G: 24, B: 24, A: 255}
|
|
||||||
|
|
||||||
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 dragListSelectedColor = color.NRGBA{R: 55, G: 55, B: 61, A: 255}
|
|
||||||
var dragListHoverColor = color.NRGBA{R: 42, G: 45, B: 61, A: 255}
|
|
||||||
|
|
||||||
var inactiveLightSurfaceColor = color.NRGBA{R: 37, G: 37, B: 38, A: 255}
|
|
||||||
var activeLightSurfaceColor = color.NRGBA{R: 45, G: 45, B: 45, A: 255}
|
|
||||||
|
|
||||||
var numberInputBgColor = color.NRGBA{R: 255, G: 255, B: 255, A: 3}
|
|
||||||
|
|
||||||
var cursorColor = color.NRGBA{R: 100, G: 140, B: 255, A: 48}
|
|
||||||
var selectionColor = color.NRGBA{R: 100, G: 140, B: 255, A: 12}
|
|
||||||
var inactiveSelectionColor = color.NRGBA{R: 140, G: 140, B: 140, A: 16}
|
|
||||||
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 errorColor = color.NRGBA{R: 207, G: 102, B: 121, A: 255}
|
|
||||||
|
|
||||||
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 warningColor = color.NRGBA{R: 251, G: 192, B: 45, A: 255}
|
|
||||||
|
|
||||||
var dialogBgColor = color.NRGBA{R: 0, G: 0, B: 0, A: 224}
|
|
||||||
|
|
||||||
var paramIsSendTargetColor = color.NRGBA{R: 120, G: 120, B: 210, A: 255}
|
|
||||||
var paramValueInvalidColor = color.NRGBA{R: 120, G: 120, B: 120, A: 190}
|
|
||||||
|
|
||||||
var oscilloscopeLimitColor = color.NRGBA{R: 255, G: 255, B: 255, A: 8}
|
|
||||||
var oscilloscopeCursorColor = color.NRGBA{R: 252, G: 186, B: 3, A: 255}
|
|
||||||
|
170
tracker/gioui/theme.yml
Normal file
170
tracker/gioui/theme.yml
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
# Because we use yaml.UnmarshalStrict, we needed to have "Define any" field for
|
||||||
|
# all the defines; UnmarshalStrict thrwows an error if a field is not defined
|
||||||
|
define:
|
||||||
|
[
|
||||||
|
&primarycolor { r: 206, g: 147, b: 216, a: 255 },
|
||||||
|
&secondarycolor { r: 128, g: 222, b: 234, a: 255 },
|
||||||
|
&transparentcolor { r: 0, g: 0, b: 0, a: 0 },
|
||||||
|
&mediumemphasis { r: 153, g: 153, b: 153, a: 255 },
|
||||||
|
&highemphasis { r: 222, g: 222, b: 222, a: 255 },
|
||||||
|
&disabled { r: 255, g: 255, b: 255, a: 97 },
|
||||||
|
&errorcolor { r: 207, g: 102, b: 121, a: 255 },
|
||||||
|
&warningcolor { r: 251, g: 192, b: 45, 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
|
||||||
|
material:
|
||||||
|
textsize: 16
|
||||||
|
fingersize: 38
|
||||||
|
palette:
|
||||||
|
bg: &bg { r: 18, g: 18, b: 18, a: 255 }
|
||||||
|
fg: &fg { r: 255, g: 255, b: 255, a: 255 }
|
||||||
|
contrastbg: *primarycolor
|
||||||
|
contrastfg: &contrastfg { r: 0, g: 0, b: 0, a: 255 }
|
||||||
|
button:
|
||||||
|
filled:
|
||||||
|
background: *primarycolor
|
||||||
|
color: *contrastfg
|
||||||
|
textsize: &buttontextsize 14
|
||||||
|
cornerradius: &buttoncornerradius 18
|
||||||
|
height: &buttonheight 36
|
||||||
|
inset: &buttoninset { top: 0, bottom: 0, left: 6, right: 6 }
|
||||||
|
text:
|
||||||
|
background: *transparentcolor
|
||||||
|
color: *primarycolor
|
||||||
|
textsize: *buttontextsize
|
||||||
|
cornerradius: *buttoncornerradius
|
||||||
|
height: *buttonheight
|
||||||
|
inset: *buttoninset
|
||||||
|
disabled:
|
||||||
|
background: { r: 53, g: 51, b: 55, a: 255 }
|
||||||
|
color: { r: 120, g: 116, b: 121, a: 255 }
|
||||||
|
textsize: *buttontextsize
|
||||||
|
cornerradius: *buttoncornerradius
|
||||||
|
height: *buttonheight
|
||||||
|
inset: *buttoninset
|
||||||
|
menu:
|
||||||
|
background: *transparentcolor
|
||||||
|
color: { r: 255, g: 255, b: 255, a: 255 }
|
||||||
|
textsize: *buttontextsize
|
||||||
|
cornerradius: 0
|
||||||
|
height: *buttonheight
|
||||||
|
inset: *buttoninset
|
||||||
|
oscilloscope:
|
||||||
|
curvecolors: [*primarycolor, *secondarycolor]
|
||||||
|
limitcolor: { r: 255, g: 255, b: 255, a: 8 }
|
||||||
|
cursorcolor: { r: 252, g: 186, b: 3, a: 255 }
|
||||||
|
numericupdown:
|
||||||
|
bgcolor: { r: 255, g: 255, b: 255, a: 3 }
|
||||||
|
textcolor: *fg
|
||||||
|
iconcolor: *primarycolor
|
||||||
|
cornerradius: 4
|
||||||
|
buttonwidth: 16
|
||||||
|
dpperstep: 8
|
||||||
|
textsize: 14
|
||||||
|
width: 70
|
||||||
|
height: 20
|
||||||
|
songpanel:
|
||||||
|
bg: { r: 24, g: 24, b: 24, a: 255 }
|
||||||
|
rowheader:
|
||||||
|
textsize: 14
|
||||||
|
color: *mediumemphasis
|
||||||
|
rowvalue:
|
||||||
|
textsize: 14
|
||||||
|
color: *mediumemphasis
|
||||||
|
expander:
|
||||||
|
textsize: 14
|
||||||
|
color: *highemphasis
|
||||||
|
errorcolor: *errorcolor
|
||||||
|
version:
|
||||||
|
textsize: 12
|
||||||
|
color: *mediumemphasis
|
||||||
|
alert:
|
||||||
|
error:
|
||||||
|
bg: *errorcolor
|
||||||
|
text: { textsize: 16, color: *black }
|
||||||
|
warning:
|
||||||
|
bg: *warningcolor
|
||||||
|
text: { textsize: 16, color: *black }
|
||||||
|
info:
|
||||||
|
bg: { r: 50, g: 50, b: 51, a: 255 }
|
||||||
|
text: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
||||||
|
dialog:
|
||||||
|
bg: { r: 0, g: 0, b: 0, a: 224 }
|
||||||
|
title: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
||||||
|
text: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
||||||
|
ordereditor:
|
||||||
|
tracktitle: { textsize: 12, color: *mediumemphasis }
|
||||||
|
rowtitle:
|
||||||
|
{ textsize: 16, color: *secondarycolor, font: { typeface: "Go Mono" } }
|
||||||
|
cell: { textsize: 16, color: *primarycolor, font: { typeface: "Go Mono" } }
|
||||||
|
loop: *loopcolor
|
||||||
|
cellbg: { r: 255, g: 255, b: 255, a: 3 }
|
||||||
|
play: { r: 55, g: 55, b: 61, a: 255 }
|
||||||
|
noteeditor:
|
||||||
|
tracktitle: { textsize: 12, color: *mediumemphasis, alignment: 2 }
|
||||||
|
orderrow:
|
||||||
|
{ textsize: 16, color: *secondarycolor, font: { typeface: "Go Mono" } }
|
||||||
|
patternrow:
|
||||||
|
{ textsize: 16, color: *mediumemphasis, font: { typeface: "Go Mono" } }
|
||||||
|
note: { textsize: 16, color: *highemphasis, font: { typeface: "Go Mono" } }
|
||||||
|
patternno:
|
||||||
|
{ textsize: 16, color: *primarycolor, font: { typeface: "Go Mono" } }
|
||||||
|
unique:
|
||||||
|
{ textsize: 16, color: *secondarycolor, font: { typeface: "Go Mono" } }
|
||||||
|
loop: *loopcolor
|
||||||
|
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:
|
||||||
|
text: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
||||||
|
shortcut: *mediumemphasis
|
||||||
|
hover: { r: 100, g: 140, b: 255, a: 48 }
|
||||||
|
disabled: *disabled
|
||||||
|
instrumenteditor:
|
||||||
|
octave: { textsize: 14, color: *disabled }
|
||||||
|
voices: { textsize: 14, color: *disabled }
|
||||||
|
instrumentcomment:
|
||||||
|
{ textsize: 14, color: *highemphasis, hintcolor: *disabled }
|
||||||
|
unitcomment: { textsize: 14, color: *highemphasis, hintcolor: *disabled }
|
||||||
|
instrumentlist:
|
||||||
|
number: { textsize: 10, color: *mediumemphasis }
|
||||||
|
name: { textsize: 12, color: *white, hintcolor: *disabled }
|
||||||
|
namemuted: { textsize: 12, color: *disabled, hintcolor: *disabled }
|
||||||
|
scrollbar: { width: 6, color: *scrollbarcolor }
|
||||||
|
unitlist:
|
||||||
|
name: { textsize: 12, color: *white, hintcolor: *disabled }
|
||||||
|
namedisabled:
|
||||||
|
textsize: 12
|
||||||
|
color: *disabled
|
||||||
|
hintcolor: *disabled
|
||||||
|
font: { style: 1 }
|
||||||
|
comment: { textsize: 12, color: *disabled }
|
||||||
|
stack: { textsize: 12, color: *mediumemphasis, shadowcolor: *black }
|
||||||
|
disabled: { textsize: 12, color: *disabled }
|
||||||
|
warning: *warningcolor
|
||||||
|
error: *errorcolor
|
||||||
|
uniteditor:
|
||||||
|
hint: { textsize: 16, color: *highemphasis, shadowcolor: *black }
|
||||||
|
chooser: { textsize: 12, 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:
|
||||||
|
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 }
|
||||||
|
selection:
|
||||||
|
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 }
|
||||||
|
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 }
|
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gioui.org/app"
|
"gioui.org/app"
|
||||||
|
"gioui.org/font/gofont"
|
||||||
"gioui.org/io/event"
|
"gioui.org/io/event"
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
@ -18,7 +19,6 @@ import (
|
|||||||
"gioui.org/op/clip"
|
"gioui.org/op/clip"
|
||||||
"gioui.org/op/paint"
|
"gioui.org/op/paint"
|
||||||
"gioui.org/text"
|
"gioui.org/text"
|
||||||
"gioui.org/widget/material"
|
|
||||||
"gioui.org/x/explorer"
|
"gioui.org/x/explorer"
|
||||||
"github.com/vsariola/sointu/tracker"
|
"github.com/vsariola/sointu/tracker"
|
||||||
)
|
)
|
||||||
@ -27,7 +27,7 @@ var canQuit = true // set to false in init() if plugin tag is enabled
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
Tracker struct {
|
Tracker struct {
|
||||||
Theme *material.Theme
|
Theme *Theme
|
||||||
OctaveNumberInput *NumberInput
|
OctaveNumberInput *NumberInput
|
||||||
InstrumentVoices *NumberInput
|
InstrumentVoices *NumberInput
|
||||||
TopHorizontalSplit *Split
|
TopHorizontalSplit *Split
|
||||||
@ -71,7 +71,7 @@ var ZoomFactors = []float32{.25, 1. / 3, .5, 2. / 3, .75, .8, 1, 1.1, 1.25, 1.5,
|
|||||||
|
|
||||||
func NewTracker(model *tracker.Model) *Tracker {
|
func NewTracker(model *tracker.Model) *Tracker {
|
||||||
t := &Tracker{
|
t := &Tracker{
|
||||||
Theme: material.NewTheme(),
|
Theme: NewTheme(),
|
||||||
OctaveNumberInput: NewNumberInput(model.Octave().Int()),
|
OctaveNumberInput: NewNumberInput(model.Octave().Int()),
|
||||||
InstrumentVoices: NewNumberInput(model.InstrumentVoices().Int()),
|
InstrumentVoices: NewNumberInput(model.InstrumentVoices().Int()),
|
||||||
|
|
||||||
@ -95,16 +95,14 @@ func NewTracker(model *tracker.Model) *Tracker {
|
|||||||
filePathString: model.FilePath().String(),
|
filePathString: model.FilePath().String(),
|
||||||
preferences: MakePreferences(),
|
preferences: MakePreferences(),
|
||||||
}
|
}
|
||||||
t.Theme.Shaper = text.NewShaper(text.WithCollection(fontCollection))
|
t.Theme.Material.Shaper = text.NewShaper(text.WithCollection(gofont.Collection()))
|
||||||
t.PopupAlert = NewPopupAlert(model.Alerts(), t.Theme.Shaper)
|
t.PopupAlert = NewPopupAlert(model.Alerts())
|
||||||
if t.preferences.YmlError != nil {
|
if t.preferences.YmlError != nil {
|
||||||
model.Alerts().Add(
|
model.Alerts().Add(
|
||||||
fmt.Sprintf("Preferences YML Error: %s", t.preferences.YmlError),
|
fmt.Sprintf("Preferences YML Error: %s", t.preferences.YmlError),
|
||||||
tracker.Warning,
|
tracker.Warning,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
t.Theme.Palette.Fg = primaryColor
|
|
||||||
t.Theme.Palette.ContrastFg = black
|
|
||||||
t.TrackEditor.scrollTable.Focus()
|
t.TrackEditor.scrollTable.Focus()
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
@ -194,7 +192,7 @@ func (t *Tracker) Layout(gtx layout.Context, w *app.Window) {
|
|||||||
gtx.Metric.PxPerDp *= zoomFactor
|
gtx.Metric.PxPerDp *= zoomFactor
|
||||||
gtx.Metric.PxPerSp *= zoomFactor
|
gtx.Metric.PxPerSp *= zoomFactor
|
||||||
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Push(gtx.Ops).Pop()
|
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Push(gtx.Ops).Pop()
|
||||||
paint.Fill(gtx.Ops, backgroundColor)
|
paint.Fill(gtx.Ops, t.Theme.Material.Bg)
|
||||||
event.Op(gtx.Ops, t) // area for capturing scroll events
|
event.Op(gtx.Ops, t) // area for capturing scroll events
|
||||||
|
|
||||||
if t.InstrumentEditor.enlargeBtn.Bool.Value() {
|
if t.InstrumentEditor.enlargeBtn.Bool.Value() {
|
||||||
@ -204,7 +202,7 @@ func (t *Tracker) Layout(gtx layout.Context, w *app.Window) {
|
|||||||
t.layoutTop,
|
t.layoutTop,
|
||||||
t.layoutBottom)
|
t.layoutBottom)
|
||||||
}
|
}
|
||||||
t.PopupAlert.Layout(gtx)
|
t.PopupAlert.Layout(gtx, t.Theme)
|
||||||
t.showDialog(gtx)
|
t.showDialog(gtx)
|
||||||
// this is the top level input handler for the whole app
|
// this is the top level input handler for the whole app
|
||||||
// it handles all the global key events and clipboard events
|
// it handles all the global key events and clipboard events
|
||||||
|
@ -141,7 +141,7 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
|
|||||||
} else {
|
} else {
|
||||||
text = pe.caser.String(text)
|
text = pe.caser.String(text)
|
||||||
}
|
}
|
||||||
hintText := Label(text, white, t.Theme.Shaper)
|
hintText := Label(t.Theme, &t.Theme.UnitEditor.Hint, text)
|
||||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Rigid(deleteUnitBtnStyle.Layout),
|
layout.Rigid(deleteUnitBtnStyle.Layout),
|
||||||
layout.Rigid(copyUnitBtnStyle.Layout),
|
layout.Rigid(copyUnitBtnStyle.Layout),
|
||||||
@ -156,7 +156,7 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
|
|||||||
}),
|
}),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
gtx.Constraints.Min.X = gtx.Dp(120)
|
gtx.Constraints.Min.X = gtx.Dp(120)
|
||||||
return hintText(gtx)
|
return hintText.Layout(gtx)
|
||||||
}),
|
}),
|
||||||
layout.Flexed(1, func(gtx C) D {
|
layout.Flexed(1, func(gtx C) D {
|
||||||
s := t.UnitComment().String()
|
s := t.UnitComment().String()
|
||||||
@ -164,11 +164,7 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
|
|||||||
for pe.commentEditor.Submitted(gtx) || pe.commentEditor.Cancelled(gtx) {
|
for pe.commentEditor.Submitted(gtx) || pe.commentEditor.Cancelled(gtx) {
|
||||||
t.InstrumentEditor.Focus()
|
t.InstrumentEditor.Focus()
|
||||||
}
|
}
|
||||||
commentStyle := MaterialEditor(t.Theme, pe.commentEditor, "---")
|
commentStyle := MaterialEditor(t.Theme, &t.Theme.InstrumentEditor.UnitComment, pe.commentEditor, "---")
|
||||||
commentStyle.Font = labelDefaultFont
|
|
||||||
commentStyle.TextSize = labelDefaultFontSize
|
|
||||||
commentStyle.Color = mediumEmphasisTextColor
|
|
||||||
commentStyle.HintColor = mediumEmphasisTextColor
|
|
||||||
ret := commentStyle.Layout(gtx)
|
ret := commentStyle.Layout(gtx)
|
||||||
s.Set(pe.commentEditor.Text())
|
s.Set(pe.commentEditor.Text())
|
||||||
return ret
|
return ret
|
||||||
@ -185,7 +181,8 @@ func (pe *UnitEditor) layoutUnitTypeChooser(gtx C, t *Tracker) D {
|
|||||||
names[i] = item
|
names[i] = item
|
||||||
}
|
}
|
||||||
element := func(gtx C, i int) D {
|
element := func(gtx C, i int) D {
|
||||||
w := LabelStyle{Text: names[i], ShadeColor: black, Color: white, Font: labelDefaultFont, FontSize: unit.Sp(12), Shaper: t.Theme.Shaper}
|
w := Label(t.Theme, &t.Theme.UnitEditor.Chooser, names[i])
|
||||||
|
|
||||||
if i == pe.searchList.TrackerList.Selected() {
|
if i == pe.searchList.TrackerList.Selected() {
|
||||||
for pe.SelectTypeBtn.Clicked(gtx) {
|
for pe.SelectTypeBtn.Clicked(gtx) {
|
||||||
t.Units().SetSelectedType(names[i])
|
t.Units().SetSelectedType(names[i])
|
||||||
@ -248,17 +245,17 @@ type ParameterWidget struct {
|
|||||||
type ParameterStyle struct {
|
type ParameterStyle struct {
|
||||||
tracker *Tracker
|
tracker *Tracker
|
||||||
w *ParameterWidget
|
w *ParameterWidget
|
||||||
Theme *material.Theme
|
Theme *Theme
|
||||||
SendTargetTheme *material.Theme
|
SendTargetTheme *material.Theme
|
||||||
Focus bool
|
Focus bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tracker) ParamStyle(th *material.Theme, paramWidget *ParameterWidget) ParameterStyle {
|
func (t *Tracker) ParamStyle(th *Theme, paramWidget *ParameterWidget) ParameterStyle {
|
||||||
sendTargetTheme := th.WithPalette(material.Palette{
|
sendTargetTheme := th.Material.WithPalette(material.Palette{
|
||||||
Bg: th.Bg,
|
Bg: th.Material.Bg,
|
||||||
Fg: paramIsSendTargetColor,
|
Fg: th.UnitEditor.SendTarget,
|
||||||
ContrastBg: th.ContrastBg,
|
ContrastBg: th.Material.ContrastBg,
|
||||||
ContrastFg: th.ContrastFg,
|
ContrastFg: th.Material.ContrastFg,
|
||||||
})
|
})
|
||||||
return ParameterStyle{
|
return ParameterStyle{
|
||||||
tracker: t, // TODO: we need this to pull the instrument names for ID style parameters, find out another way
|
tracker: t, // TODO: we need this to pull the instrument names for ID style parameters, find out another way
|
||||||
@ -273,7 +270,7 @@ func (p ParameterStyle) Layout(gtx C) D {
|
|||||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(110))
|
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(110))
|
||||||
return layout.E.Layout(gtx, Label(p.w.Parameter.Name(), white, p.tracker.Theme.Shaper))
|
return layout.E.Layout(gtx, Label(p.Theme, &p.Theme.UnitEditor.ParameterName, p.w.Parameter.Name()).Layout)
|
||||||
}),
|
}),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
switch p.w.Parameter.Type() {
|
switch p.w.Parameter.Type() {
|
||||||
@ -298,10 +295,9 @@ func (p ParameterStyle) Layout(gtx C) D {
|
|||||||
if !p.w.floatWidget.Dragging() {
|
if !p.w.floatWidget.Dragging() {
|
||||||
p.w.floatWidget.Value = (float32(p.w.Parameter.Value()) - float32(ra.Min)) / float32(ra.Max-ra.Min)
|
p.w.floatWidget.Value = (float32(p.w.Parameter.Value()) - float32(ra.Min)) / float32(ra.Max-ra.Min)
|
||||||
}
|
}
|
||||||
sliderStyle := material.Slider(p.Theme, &p.w.floatWidget)
|
sliderStyle := material.Slider(&p.Theme.Material, &p.w.floatWidget)
|
||||||
sliderStyle.Color = p.Theme.Fg
|
|
||||||
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()
|
||||||
@ -317,9 +313,8 @@ func (p ParameterStyle) Layout(gtx C) D {
|
|||||||
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(40))
|
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(40))
|
||||||
ra := p.w.Parameter.Range()
|
ra := p.w.Parameter.Range()
|
||||||
p.w.boolWidget.Value = p.w.Parameter.Value() > ra.Min
|
p.w.boolWidget.Value = p.w.Parameter.Value() > ra.Min
|
||||||
boolStyle := material.Switch(p.Theme, &p.w.boolWidget, "Toggle boolean parameter")
|
boolStyle := material.Switch(&p.Theme.Material, &p.w.boolWidget, "Toggle boolean parameter")
|
||||||
boolStyle.Color.Disabled = p.Theme.Fg
|
boolStyle.Color.Disabled = p.Theme.Material.Fg
|
||||||
boolStyle.Color.Enabled = white
|
|
||||||
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
|
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
|
||||||
dims := layout.Center.Layout(gtx, boolStyle.Layout)
|
dims := layout.Center.Layout(gtx, boolStyle.Layout)
|
||||||
if p.w.boolWidget.Value {
|
if p.w.boolWidget.Value {
|
||||||
@ -374,17 +369,16 @@ func (p ParameterStyle) Layout(gtx C) D {
|
|||||||
}),
|
}),
|
||||||
layout.Rigid(func(gtx C) D {
|
layout.Rigid(func(gtx C) D {
|
||||||
if p.w.Parameter.Type() != tracker.IDParameter {
|
if p.w.Parameter.Type() != tracker.IDParameter {
|
||||||
color := white
|
|
||||||
hint := p.w.Parameter.Hint()
|
hint := p.w.Parameter.Hint()
|
||||||
|
label := Label(p.tracker.Theme, &p.tracker.Theme.UnitEditor.Hint, hint.Label)
|
||||||
if !hint.Valid {
|
if !hint.Valid {
|
||||||
color = paramValueInvalidColor
|
label.Color = p.tracker.Theme.UnitEditor.InvalidParam
|
||||||
}
|
}
|
||||||
label := Label(hint.Label, color, p.tracker.Theme.Shaper)
|
|
||||||
if info == "" {
|
if info == "" {
|
||||||
return label(gtx)
|
return label.Layout(gtx)
|
||||||
}
|
}
|
||||||
tooltip := component.PlatformTooltip(p.SendTargetTheme, info)
|
tooltip := component.PlatformTooltip(p.SendTargetTheme, info)
|
||||||
return p.w.tipArea.Layout(gtx, tooltip, label)
|
return p.w.tipArea.Layout(gtx, tooltip, label.Layout)
|
||||||
}
|
}
|
||||||
return D{}
|
return D{}
|
||||||
}),
|
}),
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
package gioui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
|
|
||||||
"gioui.org/op"
|
|
||||||
"gioui.org/op/clip"
|
|
||||||
"gioui.org/op/paint"
|
|
||||||
"gioui.org/unit"
|
|
||||||
"github.com/vsariola/sointu/tracker"
|
|
||||||
)
|
|
||||||
|
|
||||||
type VuMeter struct {
|
|
||||||
Loudness tracker.Decibel
|
|
||||||
Peak [2]tracker.Decibel
|
|
||||||
Range float32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v VuMeter) Layout(gtx C) D {
|
|
||||||
defer op.Offset(image.Point{}).Push(gtx.Ops).Pop()
|
|
||||||
gtx.Constraints.Max.Y = gtx.Dp(unit.Dp(12))
|
|
||||||
height := gtx.Dp(unit.Dp(6))
|
|
||||||
for j := 0; j < 2; j++ {
|
|
||||||
value := float32(v.Loudness) + v.Range
|
|
||||||
if value > 0 {
|
|
||||||
x := int(value/v.Range*float32(gtx.Constraints.Max.X) + 0.5)
|
|
||||||
if x > gtx.Constraints.Max.X {
|
|
||||||
x = gtx.Constraints.Max.X
|
|
||||||
}
|
|
||||||
paint.FillShape(gtx.Ops, mediumEmphasisTextColor, clip.Rect(image.Rect(0, 0, x, height)).Op())
|
|
||||||
}
|
|
||||||
valueMax := float32(v.Peak[j]) + v.Range
|
|
||||||
if valueMax > 0 {
|
|
||||||
color := white
|
|
||||||
if valueMax >= v.Range {
|
|
||||||
color = errorColor
|
|
||||||
}
|
|
||||||
x := int(valueMax/v.Range*float32(gtx.Constraints.Max.X) + 0.5)
|
|
||||||
if x > gtx.Constraints.Max.X {
|
|
||||||
x = gtx.Constraints.Max.X
|
|
||||||
}
|
|
||||||
paint.FillShape(gtx.Ops, color, clip.Rect(image.Rect(x-1, 0, x, height)).Op())
|
|
||||||
}
|
|
||||||
op.Offset(image.Point{0, height}).Add(gtx.Ops)
|
|
||||||
}
|
|
||||||
return D{Size: gtx.Constraints.Max}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user