mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
refactor(gioui): update gioui to v0.5.0
This commit is contained in:
parent
267973e061
commit
1c020fffa3
10
go.mod
10
go.mod
@ -3,12 +3,12 @@ module github.com/vsariola/sointu
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
gioui.org v0.3.1
|
||||
gioui.org/x v0.1.0
|
||||
gioui.org v0.5.0
|
||||
gioui.org/x v0.5.0
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible
|
||||
github.com/hajimehoshi/oto v0.6.6
|
||||
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95
|
||||
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91
|
||||
golang.org/x/text v0.9.0
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20240223162706-41e9b65fb5c2
|
||||
@ -29,10 +29,10 @@ require (
|
||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.6.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/image v0.7.0 // indirect
|
||||
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
pipelined.dev/pipe v0.11.0 // indirect
|
||||
pipelined.dev/signal v0.10.0 // indirect
|
||||
)
|
||||
|
20
go.sum
20
go.sum
@ -1,13 +1,14 @@
|
||||
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY=
|
||||
gioui.org v0.3.1 h1:hslYkrkIWvx28Mxe3A87opl+8s9mnWsnWmPDh11+zco=
|
||||
gioui.org v0.3.1/go.mod h1:2atiYR4upH71/6ehnh6XsUELa7JZOrOHHNMDxGBZF0Q=
|
||||
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA=
|
||||
gioui.org v0.5.0 h1:07g7/LY1MFuTncfO4A5DIKMMsQV6PkPHyx0MhDqgmYY=
|
||||
gioui.org v0.5.0/go.mod h1:2atiYR4upH71/6ehnh6XsUELa7JZOrOHHNMDxGBZF0Q=
|
||||
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc=
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
|
||||
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
||||
gioui.org/x v0.1.0 h1:CvphvaQSroRaNEZ+JbXBkV3J3klA76U3JpieyEwHFX4=
|
||||
gioui.org/x v0.1.0/go.mod h1:5qZxjtK/TVznMlcEOyn8OheiCZlArxF3IKnLqSehKXQ=
|
||||
gioui.org/x v0.5.0 h1:NVKTn5AZuYhkAnF7MYcy1dIes36+U1N4gUTsgBhfr4A=
|
||||
gioui.org/x v0.5.0/go.mod h1:X4UBhvanAN+8S16L3K6jDMrVo7Dii7NptgBpOLBD7E4=
|
||||
git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0 h1:bGG/g4ypjrCJoSvFrP5hafr9PPB5aw8SjcOWWila7ZI=
|
||||
git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
@ -22,6 +23,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 h1:FQivqchis6bE2/9uF70M2gmmLpe82esEm2QadL0TEJo=
|
||||
github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
@ -49,8 +51,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5U
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg=
|
||||
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 h1:ryT6Nf0R83ZgD8WnFFdfI8wCeyqgdXWN4+CkFVNPAT0=
|
||||
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
@ -84,8 +86,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@ -110,8 +112,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20231016195025-8c5c6a64c826 h1:4c7O6PJ/Zl677O2VhXHUZK7LJyVBhUI7Q39+ri+gKUs=
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20231016195025-8c5c6a64c826/go.mod h1:wETLxsbBPftj6t4iVBCXvH/Xgd27ZgIC4hNnHDYNuz8=
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20240223162706-41e9b65fb5c2 h1:qrI7YY5ZH4pJflMfzum2TKvA1NaX+H4feaA6jweX2R8=
|
||||
pipelined.dev/audio/vst2 v0.10.1-0.20240223162706-41e9b65fb5c2/go.mod h1:wETLxsbBPftj6t4iVBCXvH/Xgd27ZgIC4hNnHDYNuz8=
|
||||
pipelined.dev/pipe v0.10.0/go.mod h1:aIt+NPlW0QLYByqYniG77lTxSvl7OtCNLws/m+Xz5ww=
|
||||
|
@ -51,9 +51,9 @@ func Tooltip(th *material.Theme, tip string) component.Tooltip {
|
||||
return tooltip
|
||||
}
|
||||
|
||||
func ActionIcon(th *material.Theme, w *ActionClickable, icon []byte, tip string) TipIconButtonStyle {
|
||||
func ActionIcon(gtx C, th *material.Theme, w *ActionClickable, icon []byte, tip string) TipIconButtonStyle {
|
||||
ret := TipIcon(th, &w.TipClickable, icon, tip)
|
||||
for w.Clickable.Clicked() {
|
||||
for w.Clickable.Clicked(gtx) {
|
||||
w.Action.Do()
|
||||
}
|
||||
if !w.Action.Allowed() {
|
||||
@ -74,14 +74,14 @@ func TipIcon(th *material.Theme, w *TipClickable, icon []byte, tip string) TipIc
|
||||
}
|
||||
}
|
||||
|
||||
func ToggleIcon(th *material.Theme, w *BoolClickable, offIcon, onIcon []byte, offTip, onTip string) TipIconButtonStyle {
|
||||
func ToggleIcon(gtx C, th *material.Theme, w *BoolClickable, offIcon, onIcon []byte, offTip, onTip string) TipIconButtonStyle {
|
||||
icon := offIcon
|
||||
tip := offTip
|
||||
if w.Bool.Value() {
|
||||
icon = onIcon
|
||||
tip = onTip
|
||||
}
|
||||
for w.Clickable.Clicked() {
|
||||
for w.Clickable.Clicked(gtx) {
|
||||
w.Bool.Toggle()
|
||||
}
|
||||
ibStyle := material.IconButton(th, &w.Clickable, widgetForIcon(icon), "")
|
||||
@ -102,8 +102,8 @@ func (t *TipIconButtonStyle) Layout(gtx C) D {
|
||||
return t.TipArea.Layout(gtx, t.Tooltip, t.IconButtonStyle.Layout)
|
||||
}
|
||||
|
||||
func ActionButton(th *material.Theme, w *ActionClickable, text string) material.ButtonStyle {
|
||||
for w.Clickable.Clicked() {
|
||||
func ActionButton(gtx C, th *material.Theme, w *ActionClickable, text string) material.ButtonStyle {
|
||||
for w.Clickable.Clicked(gtx) {
|
||||
w.Action.Do()
|
||||
}
|
||||
ret := material.Button(th, &w.Clickable, text)
|
||||
@ -116,8 +116,8 @@ func ActionButton(th *material.Theme, w *ActionClickable, text string) material.
|
||||
return ret
|
||||
}
|
||||
|
||||
func ToggleButton(th *material.Theme, b *BoolClickable, text string) material.ButtonStyle {
|
||||
for b.Clickable.Clicked() {
|
||||
func ToggleButton(gtx C, th *material.Theme, b *BoolClickable, text string) material.ButtonStyle {
|
||||
for b.Clickable.Clicked(gtx) {
|
||||
b.Bool.Toggle()
|
||||
}
|
||||
ret := material.Button(th, &b.Clickable, text)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package gioui
|
||||
|
||||
import (
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op/paint"
|
||||
@ -11,10 +12,11 @@ import (
|
||||
)
|
||||
|
||||
type Dialog struct {
|
||||
BtnAlt *ActionClickable
|
||||
BtnOk *ActionClickable
|
||||
BtnCancel *ActionClickable
|
||||
tag bool
|
||||
BtnAlt *ActionClickable
|
||||
BtnOk *ActionClickable
|
||||
BtnCancel *ActionClickable
|
||||
tag bool
|
||||
keyFilters []event.Filter
|
||||
}
|
||||
|
||||
type DialogStyle struct {
|
||||
@ -30,45 +32,77 @@ type DialogStyle struct {
|
||||
}
|
||||
|
||||
func NewDialog(ok, alt, cancel tracker.Action) *Dialog {
|
||||
return &Dialog{
|
||||
ret := &Dialog{
|
||||
BtnOk: NewActionClickable(ok),
|
||||
BtnAlt: NewActionClickable(alt),
|
||||
BtnCancel: NewActionClickable(cancel),
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func ConfirmDialog(th *material.Theme, dialog *Dialog, title, text string) DialogStyle {
|
||||
func ConfirmDialog(gtx C, th *material.Theme, dialog *Dialog, title, text string) DialogStyle {
|
||||
ret := DialogStyle{
|
||||
dialog: dialog,
|
||||
Title: title,
|
||||
Text: text,
|
||||
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)},
|
||||
AltStyle: ActionButton(th, dialog.BtnAlt, "Alt"),
|
||||
OkStyle: ActionButton(th, dialog.BtnOk, "Ok"),
|
||||
CancelStyle: ActionButton(th, dialog.BtnCancel, "Cancel"),
|
||||
AltStyle: ActionButton(gtx, th, dialog.BtnAlt, "Alt"),
|
||||
OkStyle: ActionButton(gtx, th, dialog.BtnOk, "Ok"),
|
||||
CancelStyle: ActionButton(gtx, th, dialog.BtnCancel, "Cancel"),
|
||||
Shaper: th.Shaper,
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (d *DialogStyle) Layout(gtx C) D {
|
||||
if !d.dialog.BtnOk.Clickable.Focused() && !d.dialog.BtnCancel.Clickable.Focused() && !d.dialog.BtnAlt.Clickable.Focused() {
|
||||
d.dialog.BtnCancel.Clickable.Focus()
|
||||
func (d *Dialog) handleKeysForButton(gtx C, btn, next, prev *ActionClickable) {
|
||||
for {
|
||||
e, ok := gtx.Event(
|
||||
key.Filter{Focus: &btn.Clickable, Name: key.NameLeftArrow},
|
||||
key.Filter{Focus: &btn.Clickable, Name: key.NameRightArrow},
|
||||
key.Filter{Focus: &btn.Clickable, Name: key.NameEscape},
|
||||
key.Filter{Focus: &btn.Clickable, Name: key.NameTab, Optional: key.ModShift},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if e, ok := e.(key.Event); ok && e.State == key.Press {
|
||||
switch {
|
||||
case e.Name == key.NameLeftArrow || (e.Name == key.NameTab && e.Modifiers.Contain(key.ModShift)):
|
||||
gtx.Execute(key.FocusCmd{Tag: &prev.Clickable})
|
||||
case e.Name == key.NameRightArrow || (e.Name == key.NameTab && !e.Modifiers.Contain(key.ModShift)):
|
||||
gtx.Execute(key.FocusCmd{Tag: &next.Clickable})
|
||||
case e.Name == key.NameEscape:
|
||||
d.BtnCancel.Action.Do()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dialog) handleKeys(gtx C) {
|
||||
if d.BtnAlt.Action.Allowed() {
|
||||
d.handleKeysForButton(gtx, d.BtnAlt, d.BtnCancel, d.BtnOk)
|
||||
d.handleKeysForButton(gtx, d.BtnCancel, d.BtnOk, d.BtnAlt)
|
||||
d.handleKeysForButton(gtx, d.BtnOk, d.BtnAlt, d.BtnCancel)
|
||||
} else {
|
||||
d.handleKeysForButton(gtx, d.BtnOk, d.BtnCancel, d.BtnCancel)
|
||||
d.handleKeysForButton(gtx, d.BtnCancel, d.BtnOk, d.BtnOk)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DialogStyle) Layout(gtx C) D {
|
||||
if !gtx.Source.Focused(&d.dialog.BtnOk.Clickable) && !gtx.Source.Focused(&d.dialog.BtnCancel.Clickable) && !gtx.Source.Focused(&d.dialog.BtnAlt.Clickable) {
|
||||
gtx.Execute(key.FocusCmd{Tag: &d.dialog.BtnCancel.Clickable})
|
||||
}
|
||||
d.dialog.handleKeys(gtx)
|
||||
paint.Fill(gtx.Ops, dialogBgColor)
|
||||
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)
|
||||
}
|
||||
for _, e := range gtx.Events(&d.dialog.tag) {
|
||||
if e, ok := e.(key.Event); ok && e.State == key.Press {
|
||||
d.command(e)
|
||||
}
|
||||
}
|
||||
visible := true
|
||||
return layout.Center.Layout(gtx, func(gtx C) D {
|
||||
return Popup(&visible).Layout(gtx, func(gtx C) D {
|
||||
key.InputOp{Tag: &d.dialog.tag, Keys: "⎋|←|→|Tab"}.Add(gtx.Ops)
|
||||
return d.Inset.Layout(gtx, func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(Label(d.Title, highEmphasisTextColor, d.Shaper)),
|
||||
@ -94,36 +128,3 @@ func (d *DialogStyle) Layout(gtx C) D {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DialogStyle) command(e key.Event) {
|
||||
switch e.Name {
|
||||
case key.NameEscape:
|
||||
d.dialog.BtnCancel.Action.Do()
|
||||
case key.NameLeftArrow:
|
||||
switch {
|
||||
case d.dialog.BtnOk.Clickable.Focused():
|
||||
d.dialog.BtnCancel.Clickable.Focus()
|
||||
case d.dialog.BtnCancel.Clickable.Focused():
|
||||
if d.dialog.BtnAlt.Action.Allowed() {
|
||||
d.dialog.BtnAlt.Clickable.Focus()
|
||||
} else {
|
||||
d.dialog.BtnOk.Clickable.Focus()
|
||||
}
|
||||
case d.dialog.BtnAlt.Clickable.Focused():
|
||||
d.dialog.BtnOk.Clickable.Focus()
|
||||
}
|
||||
case key.NameRightArrow, key.NameTab:
|
||||
switch {
|
||||
case d.dialog.BtnOk.Clickable.Focused():
|
||||
if d.dialog.BtnAlt.Action.Allowed() {
|
||||
d.dialog.BtnAlt.Clickable.Focus()
|
||||
} else {
|
||||
d.dialog.BtnCancel.Clickable.Focus()
|
||||
}
|
||||
case d.dialog.BtnCancel.Clickable.Focused():
|
||||
d.dialog.BtnOk.Clickable.Focus()
|
||||
case d.dialog.BtnAlt.Clickable.Focused():
|
||||
d.dialog.BtnCancel.Clickable.Focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,16 @@
|
||||
package gioui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
"io"
|
||||
|
||||
"gioui.org/io/clipboard"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/io/transfer"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/clip"
|
||||
@ -27,7 +31,6 @@ type DragList struct {
|
||||
swapped bool
|
||||
focused bool
|
||||
requestFocus bool
|
||||
mainTag bool
|
||||
}
|
||||
|
||||
type FilledDragListStyle struct {
|
||||
@ -72,11 +75,7 @@ func (s FilledDragListStyle) Layout(gtx C) D {
|
||||
|
||||
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()
|
||||
keys := key.Set("↑|↓|Ctrl-↑|Ctrl-↓|Shift-↑|Shift-↓|⇞|⇟|Ctrl-⇞|Ctrl-⇟|Ctrl-A|Ctrl-C|Ctrl-X|Ctrl-V|⌦|Ctrl-⌫")
|
||||
if s.dragList.List.Axis == layout.Horizontal {
|
||||
keys = key.Set("←|→|Ctrl-←|Ctrl-→|Shift-←|Shift-→|Home|End|Ctrl-Home|Ctrl-End|Ctrl-A|Ctrl-C|Ctrl-X|Ctrl-V|⌦|Ctrl-⌫")
|
||||
}
|
||||
key.InputOp{Tag: &s.dragList.mainTag, Keys: keys}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, s.dragList)
|
||||
|
||||
if s.dragList.List.Axis == layout.Horizontal {
|
||||
gtx.Constraints.Min.X = gtx.Constraints.Max.X
|
||||
@ -86,11 +85,39 @@ func (s FilledDragListStyle) Layout(gtx C) D {
|
||||
|
||||
if s.dragList.requestFocus {
|
||||
s.dragList.requestFocus = false
|
||||
key.FocusOp{Tag: &s.dragList.mainTag}.Add(gtx.Ops)
|
||||
gtx.Execute(key.FocusCmd{Tag: s.dragList})
|
||||
}
|
||||
|
||||
for _, ke := range gtx.Events(&s.dragList.mainTag) {
|
||||
switch ke := ke.(type) {
|
||||
prevKey := key.NameUpArrow
|
||||
nextKey := key.NameDownArrow
|
||||
firstKey := key.NamePageUp
|
||||
lastKey := key.NamePageDown
|
||||
if s.dragList.List.Axis == layout.Horizontal {
|
||||
prevKey = key.NameLeftArrow
|
||||
nextKey = key.NameRightArrow
|
||||
firstKey = key.NameHome
|
||||
lastKey = key.NameEnd
|
||||
}
|
||||
|
||||
for {
|
||||
event, ok := gtx.Event(
|
||||
key.FocusFilter{Target: s.dragList},
|
||||
transfer.TargetFilter{Target: s.dragList, Type: "application/text"},
|
||||
key.Filter{Focus: s.dragList, Name: prevKey, Optional: key.ModShift | key.ModShortcut},
|
||||
key.Filter{Focus: s.dragList, Name: nextKey, Optional: key.ModShift | key.ModShortcut},
|
||||
key.Filter{Focus: s.dragList, Name: firstKey, Optional: key.ModShift | key.ModShortcut},
|
||||
key.Filter{Focus: s.dragList, Name: lastKey, Optional: key.ModShift | key.ModShortcut},
|
||||
key.Filter{Focus: s.dragList, Name: "A", Required: key.ModShortcut},
|
||||
key.Filter{Focus: s.dragList, Name: "C", Required: key.ModShortcut},
|
||||
key.Filter{Focus: s.dragList, Name: "X", Required: key.ModShortcut},
|
||||
key.Filter{Focus: s.dragList, Name: "V", Required: key.ModShortcut},
|
||||
key.Filter{Focus: s.dragList, Name: key.NameDeleteBackward, Required: key.ModShortcut},
|
||||
key.Filter{Focus: s.dragList, Name: key.NameDeleteForward},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch ke := event.(type) {
|
||||
case key.FocusEvent:
|
||||
s.dragList.focused = ke.Focus
|
||||
if !s.dragList.focused {
|
||||
@ -101,10 +128,13 @@ func (s FilledDragListStyle) Layout(gtx C) D {
|
||||
break
|
||||
}
|
||||
s.dragList.command(gtx, ke)
|
||||
case clipboard.Event:
|
||||
s.dragList.TrackerList.PasteElements([]byte(ke.Text))
|
||||
case transfer.DataEvent:
|
||||
if b, err := io.ReadAll(ke.Open()); err == nil {
|
||||
s.dragList.TrackerList.PasteElements([]byte(b))
|
||||
}
|
||||
|
||||
}
|
||||
op.InvalidateOp{}.Add(gtx.Ops)
|
||||
gtx.Execute(op.InvalidateCmd{})
|
||||
}
|
||||
|
||||
_, isMutable := s.dragList.TrackerList.ListData.(tracker.MutableListData)
|
||||
@ -128,12 +158,19 @@ func (s FilledDragListStyle) Layout(gtx C) D {
|
||||
}
|
||||
paint.FillShape(gtx.Ops, color, clip.Rect{Max: image.Pt(gtx.Constraints.Min.X, gtx.Constraints.Min.Y)}.Op())
|
||||
|
||||
for _, ev := range gtx.Events(&s.dragList.tags[index]) {
|
||||
for {
|
||||
ev, ok := gtx.Event(pointer.Filter{
|
||||
Target: &s.dragList.tags[index],
|
||||
Kinds: pointer.Press | pointer.Enter | pointer.Leave,
|
||||
})
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
e, ok := ev.(pointer.Event)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch e.Type {
|
||||
switch e.Kind {
|
||||
case pointer.Enter:
|
||||
s.dragList.HoverItem = index
|
||||
case pointer.Leave:
|
||||
@ -148,22 +185,28 @@ func (s FilledDragListStyle) Layout(gtx C) D {
|
||||
if !e.Modifiers.Contain(key.ModShift) {
|
||||
s.dragList.TrackerList.SetSelected2(index)
|
||||
}
|
||||
key.FocusOp{Tag: &s.dragList.mainTag}.Add(gtx.Ops)
|
||||
gtx.Execute(key.FocusCmd{Tag: s.dragList})
|
||||
}
|
||||
}
|
||||
rect := image.Rect(0, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)
|
||||
area := clip.Rect(rect).Push(gtx.Ops)
|
||||
pointer.InputOp{Tag: &s.dragList.tags[index],
|
||||
Types: pointer.Press | pointer.Enter | pointer.Leave,
|
||||
}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, &s.dragList.tags[index])
|
||||
area.Pop()
|
||||
if index == s.dragList.TrackerList.Selected() && isMutable {
|
||||
for _, ev := range gtx.Events(&s.dragList.focused) {
|
||||
for {
|
||||
target := &s.dragList.focused
|
||||
if s.dragList.drag {
|
||||
target = nil
|
||||
}
|
||||
ev, ok := gtx.Event(pointer.Filter{Target: target, Kinds: pointer.Drag | pointer.Press | pointer.Release | pointer.Cancel})
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
e, ok := ev.(pointer.Event)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch e.Type {
|
||||
switch e.Kind {
|
||||
case pointer.Press:
|
||||
s.dragList.dragID = e.PointerID
|
||||
s.dragList.drag = true
|
||||
@ -186,17 +229,12 @@ func (s FilledDragListStyle) Layout(gtx C) D {
|
||||
swap = 1
|
||||
}
|
||||
}
|
||||
case pointer.Release:
|
||||
fallthrough
|
||||
case pointer.Cancel:
|
||||
case pointer.Release, pointer.Cancel:
|
||||
s.dragList.drag = false
|
||||
}
|
||||
}
|
||||
area := clip.Rect(rect).Push(gtx.Ops)
|
||||
pointer.InputOp{Tag: &s.dragList.focused,
|
||||
Types: pointer.Drag | pointer.Press | pointer.Release,
|
||||
Grab: s.dragList.drag,
|
||||
}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, &s.dragList.focused)
|
||||
pointer.CursorGrab.Add(gtx.Ops)
|
||||
area.Pop()
|
||||
}
|
||||
@ -225,7 +263,7 @@ func (s FilledDragListStyle) Layout(gtx C) D {
|
||||
dims := s.dragList.List.Layout(gtx, count, listElem)
|
||||
if !s.dragList.swapped && swap != 0 {
|
||||
if s.dragList.TrackerList.MoveElements(swap) {
|
||||
op.InvalidateOp{}.Add(gtx.Ops)
|
||||
gtx.Execute(op.InvalidateCmd{})
|
||||
}
|
||||
s.dragList.swapped = true
|
||||
} else {
|
||||
@ -238,12 +276,12 @@ func (e *DragList) command(gtx layout.Context, k key.Event) {
|
||||
if k.Modifiers.Contain(key.ModShortcut) {
|
||||
switch k.Name {
|
||||
case "V":
|
||||
clipboard.ReadOp{Tag: &e.mainTag}.Add(gtx.Ops)
|
||||
gtx.Execute(clipboard.ReadCmd{Tag: e})
|
||||
return
|
||||
case "C", "X":
|
||||
data, ok := e.TrackerList.CopyElements()
|
||||
if ok && (k.Name == "C" || e.TrackerList.DeleteElements(false)) {
|
||||
clipboard.WriteOp{Text: string(data)}.Add(gtx.Ops)
|
||||
gtx.Execute(clipboard.WriteCmd{Type: "application/text", Data: io.NopCloser(bytes.NewReader(data))})
|
||||
}
|
||||
return
|
||||
case "A":
|
||||
|
@ -1,14 +1,17 @@
|
||||
package gioui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gioui.org/font"
|
||||
"gioui.org/io/clipboard"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
@ -45,6 +48,9 @@ type InstrumentEditor struct {
|
||||
wasFocused bool
|
||||
presetMenuItems []MenuItem
|
||||
presetMenu Menu
|
||||
commentKeyFilters []event.Filter
|
||||
searchkeyFilters []event.Filter
|
||||
nameKeyFilters []event.Filter
|
||||
}
|
||||
|
||||
func NewInstrumentEditor(model *tracker.Model) *InstrumentEditor {
|
||||
@ -72,6 +78,17 @@ func NewInstrumentEditor(model *tracker.Model) *InstrumentEditor {
|
||||
ret.presetMenuItems = append(ret.presetMenuItems, MenuItem{Text: name, IconBytes: icons.ImageAudiotrack, Doer: model.LoadPreset(index)})
|
||||
return true
|
||||
})
|
||||
for k := range noteMap {
|
||||
ret.commentKeyFilters = append(ret.commentKeyFilters, key.Filter{Name: k, Focus: ret.commentEditor})
|
||||
ret.searchkeyFilters = append(ret.searchkeyFilters, key.Filter{Name: k, Focus: ret.searchEditor})
|
||||
ret.nameKeyFilters = append(ret.nameKeyFilters, key.Filter{Name: k, Focus: ret.nameEditor})
|
||||
}
|
||||
ret.commentKeyFilters = append(ret.commentKeyFilters, key.Filter{Name: key.NameEscape, Focus: ret.commentEditor})
|
||||
ret.searchkeyFilters = append(ret.searchkeyFilters, key.Filter{Name: key.NameEscape, Focus: ret.searchEditor})
|
||||
ret.nameKeyFilters = append(ret.nameKeyFilters, key.Filter{Name: key.NameEscape, Focus: ret.nameEditor})
|
||||
ret.commentKeyFilters = append(ret.commentKeyFilters, key.Filter{Name: key.NameSpace, Focus: ret.commentEditor})
|
||||
ret.searchkeyFilters = append(ret.searchkeyFilters, key.Filter{Name: key.NameSpace, Focus: ret.searchEditor})
|
||||
ret.nameKeyFilters = append(ret.nameKeyFilters, key.Filter{Name: key.NameSpace, Focus: ret.nameEditor})
|
||||
return ret
|
||||
}
|
||||
|
||||
@ -83,14 +100,16 @@ func (ie *InstrumentEditor) Focused() bool {
|
||||
return ie.unitDragList.focused
|
||||
}
|
||||
|
||||
func (ie *InstrumentEditor) ChildFocused() bool {
|
||||
return ie.unitEditor.sliderList.Focused() || ie.instrumentDragList.Focused() || ie.commentEditor.Focused() || ie.nameEditor.Focused() || ie.searchEditor.Focused() ||
|
||||
ie.addUnitBtn.Clickable.Focused() || ie.commentExpandBtn.Clickable.Focused() || ie.presetMenuBtn.Clickable.Focused() || ie.deleteInstrumentBtn.Clickable.Focused() || ie.copyInstrumentBtn.Clickable.Focused()
|
||||
func (ie *InstrumentEditor) childFocused(gtx C) bool {
|
||||
return ie.unitEditor.sliderList.Focused() ||
|
||||
ie.instrumentDragList.Focused() || gtx.Source.Focused(ie.commentEditor) || gtx.Source.Focused(ie.nameEditor) || gtx.Source.Focused(ie.searchEditor) ||
|
||||
gtx.Source.Focused(ie.addUnitBtn.Clickable) || gtx.Source.Focused(ie.commentExpandBtn.Clickable) || gtx.Source.Focused(ie.presetMenuBtn.Clickable) ||
|
||||
gtx.Source.Focused(ie.deleteInstrumentBtn.Clickable) || gtx.Source.Focused(ie.copyInstrumentBtn.Clickable)
|
||||
}
|
||||
|
||||
func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
|
||||
ie.wasFocused = ie.Focused() || ie.ChildFocused()
|
||||
fullscreenBtnStyle := ToggleIcon(t.Theme, ie.enlargeBtn, icons.NavigationFullscreen, icons.NavigationFullscreenExit, "Enlarge (Ctrl+E)", "Shrink (Ctrl+E)")
|
||||
ie.wasFocused = ie.Focused() || ie.childFocused(gtx)
|
||||
fullscreenBtnStyle := ToggleIcon(gtx, t.Theme, ie.enlargeBtn, icons.NavigationFullscreen, icons.NavigationFullscreenExit, "Enlarge (Ctrl+E)", "Shrink (Ctrl+E)")
|
||||
|
||||
octave := func(gtx C) D {
|
||||
in := layout.UniformInset(unit.Dp(1))
|
||||
@ -99,7 +118,7 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
|
||||
return dims
|
||||
}
|
||||
|
||||
newBtnStyle := ActionIcon(t.Theme, ie.newInstrumentBtn, icons.ContentAdd, "Add\ninstrument\n(Ctrl+I)")
|
||||
newBtnStyle := ActionIcon(gtx, t.Theme, ie.newInstrumentBtn, icons.ContentAdd, "Add\ninstrument\n(Ctrl+I)")
|
||||
ret := layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx C) D {
|
||||
return layout.Flex{}.Layout(
|
||||
@ -142,23 +161,23 @@ func (ie *InstrumentEditor) Layout(gtx C, t *Tracker) D {
|
||||
|
||||
func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
||||
header := func(gtx C) D {
|
||||
commentExpandBtnStyle := ToggleIcon(t.Theme, ie.commentExpandBtn, icons.NavigationExpandMore, icons.NavigationExpandLess, "Expand comment", "Collapse comment")
|
||||
commentExpandBtnStyle := ToggleIcon(gtx, t.Theme, ie.commentExpandBtn, icons.NavigationExpandMore, icons.NavigationExpandLess, "Expand comment", "Collapse comment")
|
||||
presetMenuBtnStyle := TipIcon(t.Theme, ie.presetMenuBtn, icons.NavigationMenu, "Load preset")
|
||||
copyInstrumentBtnStyle := TipIcon(t.Theme, ie.copyInstrumentBtn, icons.ContentContentCopy, "Copy instrument")
|
||||
saveInstrumentBtnStyle := TipIcon(t.Theme, ie.saveInstrumentBtn, icons.ContentSave, "Save instrument")
|
||||
loadInstrumentBtnStyle := TipIcon(t.Theme, ie.loadInstrumentBtn, icons.FileFolderOpen, "Load instrument")
|
||||
deleteInstrumentBtnStyle := ActionIcon(t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, "Delete\ninstrument")
|
||||
deleteInstrumentBtnStyle := ActionIcon(gtx, t.Theme, ie.deleteInstrumentBtn, icons.ActionDelete, "Delete\ninstrument")
|
||||
|
||||
m := PopupMenu(&ie.presetMenu, t.Theme.Shaper)
|
||||
|
||||
for ie.copyInstrumentBtn.Clickable.Clicked() {
|
||||
for ie.copyInstrumentBtn.Clickable.Clicked(gtx) {
|
||||
if contents, ok := t.Instruments().List().CopyElements(); ok {
|
||||
clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops)
|
||||
gtx.Execute(clipboard.WriteCmd{Type: "application/text", Data: io.NopCloser(bytes.NewReader(contents))})
|
||||
t.Alerts().Add("Instrument copied to clipboard", tracker.Info)
|
||||
}
|
||||
}
|
||||
|
||||
for ie.saveInstrumentBtn.Clickable.Clicked() {
|
||||
for ie.saveInstrumentBtn.Clickable.Clicked(gtx) {
|
||||
writer, err := t.Explorer.CreateFile(t.InstrumentName().Value() + ".yml")
|
||||
if err != nil {
|
||||
continue
|
||||
@ -166,7 +185,7 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
||||
t.SaveInstrument(writer)
|
||||
}
|
||||
|
||||
for ie.loadInstrumentBtn.Clickable.Clicked() {
|
||||
for ie.loadInstrumentBtn.Clickable.Clicked(gtx) {
|
||||
reader, err := t.Explorer.ChooseFile(".yml", ".json", ".4ki", ".4kp")
|
||||
if err != nil {
|
||||
continue
|
||||
@ -199,11 +218,11 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
||||
layout.Rigid(deleteInstrumentBtnStyle.Layout))
|
||||
}
|
||||
|
||||
for ie.presetMenuBtn.Clickable.Clicked() {
|
||||
for ie.presetMenuBtn.Clickable.Clicked(gtx) {
|
||||
ie.presetMenu.Visible = true
|
||||
}
|
||||
|
||||
if ie.commentExpandBtn.Bool.Value() || ie.commentEditor.Focused() { // we draw once the widget after it manages to lose focus
|
||||
if ie.commentExpandBtn.Bool.Value() || gtx.Source.Focused(ie.commentEditor) { // we draw once the widget after it manages to lose focus
|
||||
if ie.commentEditor.Text() != ie.commentString.Value() {
|
||||
ie.commentEditor.SetText(ie.commentString.Value())
|
||||
}
|
||||
@ -211,8 +230,11 @@ func (ie *InstrumentEditor) layoutInstrumentHeader(gtx C, t *Tracker) D {
|
||||
layout.Rigid(header),
|
||||
layout.Rigid(func(gtx C) D {
|
||||
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops).Pop()
|
||||
key.InputOp{Tag: &ie.unitDragList, Keys: globalKeys + "|⎋"}.Add(gtx.Ops)
|
||||
for _, event := range gtx.Events(&ie.unitDragList) {
|
||||
for {
|
||||
event, ok := gtx.Event(ie.commentKeyFilters...)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if e, ok := event.(key.Event); ok && e.State == key.Press && e.Name == key.NameEscape {
|
||||
ie.instrumentDragList.Focus()
|
||||
}
|
||||
@ -249,13 +271,6 @@ func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D {
|
||||
k := byte(255 - level*127)
|
||||
color := color.NRGBA{R: 255, G: k, B: 255, A: 255}
|
||||
if i == ie.instrumentDragList.TrackerList.Selected() {
|
||||
for _, ev := range ie.nameEditor.Events() {
|
||||
_, ok := ev.(widget.SubmitEvent)
|
||||
if ok {
|
||||
ie.instrumentDragList.Focus()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if n := name; n != ie.nameEditor.Text() {
|
||||
ie.nameEditor.SetText(n)
|
||||
}
|
||||
@ -264,11 +279,30 @@ func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D {
|
||||
editor.HintColor = instrumentNameHintColor
|
||||
editor.TextSize = unit.Sp(12)
|
||||
editor.Font = labelDefaultFont
|
||||
for {
|
||||
ev, ok := ie.nameEditor.Update(gtx)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
_, ok = ev.(widget.SubmitEvent)
|
||||
if ok {
|
||||
ie.instrumentDragList.Focus()
|
||||
continue
|
||||
}
|
||||
}
|
||||
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()
|
||||
key.InputOp{Tag: &ie.nameEditor, Keys: globalKeys}.Add(gtx.Ops)
|
||||
return editor.Layout(gtx)
|
||||
})
|
||||
for { // don't let key presses flow through from the editor
|
||||
event, ok := gtx.Event(ie.nameKeyFilters...)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if e, ok := event.(key.Event); ok && e.State == key.Press && e.Name == key.NameEscape {
|
||||
ie.instrumentDragList.Focus()
|
||||
}
|
||||
}
|
||||
ie.nameString.Set(ie.nameEditor.Text())
|
||||
return dims
|
||||
}
|
||||
@ -297,9 +331,15 @@ func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D {
|
||||
|
||||
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()
|
||||
key.InputOp{Tag: ie.instrumentDragList, Keys: "↓|⏎|⌤"}.Add(gtx.Ops)
|
||||
|
||||
for _, event := range gtx.Events(ie.instrumentDragList) {
|
||||
for {
|
||||
event, ok := gtx.Event(
|
||||
key.Filter{Focus: ie.instrumentDragList, Name: key.NameDownArrow},
|
||||
key.Filter{Focus: ie.instrumentDragList, Name: key.NameReturn},
|
||||
key.Filter{Focus: ie.instrumentDragList, Name: key.NameEnter},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch e := event.(type) {
|
||||
case key.Event:
|
||||
switch e.State {
|
||||
@ -308,7 +348,7 @@ func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D {
|
||||
case key.NameDownArrow:
|
||||
ie.unitDragList.Focus()
|
||||
case key.NameReturn, key.NameEnter:
|
||||
ie.nameEditor.Focus()
|
||||
gtx.Execute(key.FocusCmd{Tag: ie.nameEditor})
|
||||
l := len(ie.nameEditor.Text())
|
||||
ie.nameEditor.SetCaret(l, l)
|
||||
}
|
||||
@ -321,9 +361,10 @@ func (ie *InstrumentEditor) layoutInstrumentList(gtx C, t *Tracker) D {
|
||||
instrumentList.LayoutScrollBar(gtx)
|
||||
return dims
|
||||
}
|
||||
|
||||
func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
|
||||
// TODO: how to ie.unitDragList.Focus()
|
||||
addUnitBtnStyle := ActionIcon(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.Background = t.Theme.Fg
|
||||
addUnitBtnStyle.IconButtonStyle.Inset = layout.UniformInset(unit.Dp(4))
|
||||
@ -365,8 +406,26 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
|
||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||
layout.Flexed(1, func(gtx C) D {
|
||||
if i == ie.unitDragList.TrackerList.Selected() {
|
||||
for _, ev := range ie.searchEditor.Events() {
|
||||
_, ok := ev.(widget.SubmitEvent)
|
||||
editor := material.Editor(t.Theme, ie.searchEditor, "---")
|
||||
editor.Color = color
|
||||
editor.HintColor = instrumentNameHintColor
|
||||
editor.TextSize = unit.Sp(12)
|
||||
editor.Font = f
|
||||
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops).Pop()
|
||||
txt := u.Type
|
||||
str := tracker.String{StringData: (*tracker.UnitSearch)(t.Model)}
|
||||
if t.UnitSearching().Value() {
|
||||
txt = str.Value()
|
||||
}
|
||||
if ie.searchEditor.Text() != txt {
|
||||
ie.searchEditor.SetText(txt)
|
||||
}
|
||||
for {
|
||||
ev, ok := ie.searchEditor.Update(gtx)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
_, ok = ev.(widget.SubmitEvent)
|
||||
if ok {
|
||||
txt := ""
|
||||
ie.unitDragList.Focus()
|
||||
@ -383,23 +442,16 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
|
||||
continue
|
||||
}
|
||||
}
|
||||
editor := material.Editor(t.Theme, ie.searchEditor, "---")
|
||||
editor.Color = color
|
||||
editor.HintColor = instrumentNameHintColor
|
||||
editor.TextSize = unit.Sp(12)
|
||||
editor.Font = f
|
||||
|
||||
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops).Pop()
|
||||
key.InputOp{Tag: &ie.searchEditor, Keys: globalKeys}.Add(gtx.Ops)
|
||||
txt := u.Type
|
||||
str := tracker.String{StringData: (*tracker.UnitSearch)(t.Model)}
|
||||
if t.UnitSearching().Value() {
|
||||
txt = str.Value()
|
||||
}
|
||||
if ie.searchEditor.Text() != txt {
|
||||
ie.searchEditor.SetText(txt)
|
||||
}
|
||||
ret := editor.Layout(gtx)
|
||||
for { // don't let key presses flow through from the editor
|
||||
event, ok := gtx.Event(ie.searchkeyFilters...)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if e, ok := event.(key.Event); ok && e.State == key.Press && e.Name == key.NameEscape {
|
||||
ie.instrumentDragList.Focus()
|
||||
}
|
||||
}
|
||||
if ie.searchEditor.Text() != txt {
|
||||
str.Set(ie.searchEditor.Text())
|
||||
}
|
||||
@ -424,8 +476,18 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
|
||||
return layout.Stack{Alignment: layout.SE}.Layout(gtx,
|
||||
layout.Expanded(func(gtx C) D {
|
||||
defer clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Push(gtx.Ops).Pop()
|
||||
key.InputOp{Tag: ie.unitDragList, Keys: "→|⏎|Ctrl-⏎|⌫|⎋"}.Add(gtx.Ops)
|
||||
for _, event := range gtx.Events(ie.unitDragList) {
|
||||
for {
|
||||
event, ok := gtx.Event(
|
||||
key.Filter{Focus: ie.unitDragList, Name: key.NameRightArrow},
|
||||
key.Filter{Focus: ie.unitDragList, Name: key.NameEnter, Optional: key.ModCtrl},
|
||||
key.Filter{Focus: ie.unitDragList, Name: key.NameReturn, Optional: key.ModCtrl},
|
||||
key.Filter{Focus: ie.unitDragList, Name: key.NameDeleteBackward},
|
||||
key.Filter{Focus: ie.unitDragList, Name: key.NameEscape},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
switch e := event.(type) {
|
||||
case key.Event:
|
||||
switch e.State {
|
||||
@ -437,13 +499,13 @@ func (ie *InstrumentEditor) layoutUnitList(gtx C, t *Tracker) D {
|
||||
ie.unitEditor.sliderList.Focus()
|
||||
case key.NameDeleteBackward:
|
||||
t.Units().SetSelectedType("")
|
||||
ie.searchEditor.Focus()
|
||||
gtx.Execute(key.FocusCmd{Tag: ie.searchEditor})
|
||||
l := len(ie.searchEditor.Text())
|
||||
ie.searchEditor.SetCaret(l, l)
|
||||
case key.NameReturn:
|
||||
case key.NameEnter, key.NameReturn:
|
||||
t.Model.AddUnit(e.Modifiers.Contain(key.ModCtrl)).Do()
|
||||
ie.searchEditor.SetText("")
|
||||
ie.searchEditor.Focus()
|
||||
gtx.Execute(key.FocusCmd{Tag: ie.searchEditor})
|
||||
l := len(ie.searchEditor.Text())
|
||||
ie.searchEditor.SetCaret(l, l)
|
||||
}
|
||||
|
@ -3,14 +3,9 @@ package gioui
|
||||
import (
|
||||
"gioui.org/io/clipboard"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/op"
|
||||
)
|
||||
|
||||
// globalKeys is a list of keys that are handled globally by the app.
|
||||
// All Editors should capture these keys to prevent them flowing to the global handler.
|
||||
var globalKeys = key.Set("Space|\\|<|>|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|1|2|3|4|5|6|7|8|9|0|,|.")
|
||||
|
||||
var noteMap = map[string]int{
|
||||
var noteMap = map[key.Name]int{
|
||||
"Z": -12,
|
||||
"S": -11,
|
||||
"X": -10,
|
||||
@ -46,12 +41,12 @@ var noteMap = map[string]int{
|
||||
}
|
||||
|
||||
// KeyEvent handles incoming key events and returns true if repaint is needed.
|
||||
func (t *Tracker) KeyEvent(e key.Event, o *op.Ops) {
|
||||
func (t *Tracker) KeyEvent(e key.Event, gtx C) {
|
||||
if e.State == key.Press {
|
||||
switch e.Name {
|
||||
case "V":
|
||||
if e.Modifiers.Contain(key.ModShortcut) {
|
||||
clipboard.ReadOp{Tag: t}.Add(o)
|
||||
gtx.Execute(clipboard.ReadCmd{Tag: t})
|
||||
return
|
||||
}
|
||||
case "Z":
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
@ -65,12 +66,19 @@ func (m *MenuStyle) Layout(gtx C, items ...MenuItem) D {
|
||||
m.Menu.tags = append(m.Menu.tags, false)
|
||||
}
|
||||
// handle pointer events for this item
|
||||
for _, ev := range gtx.Events(&m.Menu.tags[i]) {
|
||||
for {
|
||||
ev, ok := gtx.Event(pointer.Filter{
|
||||
Target: &m.Menu.tags[i],
|
||||
Kinds: pointer.Press | pointer.Enter | pointer.Leave,
|
||||
})
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
e, ok := ev.(pointer.Event)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch e.Type {
|
||||
switch e.Kind {
|
||||
case pointer.Press:
|
||||
item.Doer.Do()
|
||||
m.Menu.Visible = false
|
||||
@ -130,9 +138,7 @@ func (m *MenuStyle) Layout(gtx C, items ...MenuItem) D {
|
||||
if item.Doer.Allowed() {
|
||||
rect := image.Rect(0, 0, dims.Size.X, dims.Size.Y)
|
||||
area := clip.Rect(rect).Push(gtx.Ops)
|
||||
pointer.InputOp{Tag: &m.Menu.tags[i],
|
||||
Types: pointer.Press | pointer.Enter | pointer.Leave,
|
||||
}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, &m.Menu.tags[i])
|
||||
area.Pop()
|
||||
}
|
||||
return dims
|
||||
@ -163,8 +169,8 @@ func PopupMenu(menu *Menu, shaper *text.Shaper) MenuStyle {
|
||||
}
|
||||
}
|
||||
|
||||
func (tr *Tracker) layoutMenu(title string, clickable *widget.Clickable, menu *Menu, width unit.Dp, items ...MenuItem) layout.Widget {
|
||||
for clickable.Clicked() {
|
||||
func (tr *Tracker) layoutMenu(gtx C, title string, clickable *widget.Clickable, menu *Menu, width unit.Dp, items ...MenuItem) layout.Widget {
|
||||
for clickable.Clicked(gtx) {
|
||||
menu.Visible = true
|
||||
}
|
||||
m := PopupMenu(menu, tr.Theme.Shaper)
|
||||
|
@ -83,7 +83,50 @@ func NewNoteEditor(model *tracker.Model) *NoteEditor {
|
||||
}
|
||||
|
||||
func (te *NoteEditor) Layout(gtx layout.Context, t *Tracker) layout.Dimensions {
|
||||
for _, e := range gtx.Events(&te.tag) {
|
||||
for {
|
||||
e, ok := gtx.Event(
|
||||
key.Filter{Focus: te.scrollTable, Name: "A"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "B"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "C"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "D"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "E"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "F"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "G"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "H"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "I"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "J"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "K"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "L"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "M"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "N"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "O"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "P"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "Q"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "R"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "S"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "T"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "U"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "V"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "W"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "X"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "Y"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "Z"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "0"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "1"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "2"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "3"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "4"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "5"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "6"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "7"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "8"},
|
||||
key.Filter{Focus: te.scrollTable, Name: "9"},
|
||||
key.Filter{Focus: te.scrollTable, Name: ","},
|
||||
key.Filter{Focus: te.scrollTable, Name: "."},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch e := e.(type) {
|
||||
case key.Event:
|
||||
if e.State == key.Release {
|
||||
@ -99,7 +142,6 @@ func (te *NoteEditor) Layout(gtx layout.Context, t *Tracker) layout.Dimensions {
|
||||
|
||||
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()
|
||||
key.InputOp{Tag: &te.tag, Keys: "Ctrl-⌫|Ctrl-⌦|⏎|Ctrl-⏎|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|0|1|2|3|4|5|6|7|8|9|,|."}.Add(gtx.Ops)
|
||||
|
||||
return Surface{Gray: 24, Focus: te.scrollTable.Focused()}.Layout(gtx, func(gtx C) D {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
@ -115,19 +157,19 @@ func (te *NoteEditor) Layout(gtx layout.Context, t *Tracker) layout.Dimensions {
|
||||
|
||||
func (te *NoteEditor) layoutButtons(gtx C, t *Tracker) D {
|
||||
return Surface{Gray: 37, Focus: te.scrollTable.Focused() || te.scrollTable.ChildFocused(), FitSize: true}.Layout(gtx, func(gtx C) D {
|
||||
addSemitoneBtnStyle := ActionButton(t.Theme, te.AddSemitoneBtn, "+1")
|
||||
subtractSemitoneBtnStyle := ActionButton(t.Theme, te.SubtractSemitoneBtn, "-1")
|
||||
addOctaveBtnStyle := ActionButton(t.Theme, te.AddOctaveBtn, "+12")
|
||||
subtractOctaveBtnStyle := ActionButton(t.Theme, te.SubtractOctaveBtn, "-12")
|
||||
noteOffBtnStyle := ActionButton(t.Theme, te.NoteOffBtn, "Note Off")
|
||||
deleteTrackBtnStyle := ActionIcon(t.Theme, te.DeleteTrackBtn, icons.ActionDelete, "Delete track\n(Ctrl+Shift+T)")
|
||||
newTrackBtnStyle := ActionIcon(t.Theme, te.NewTrackBtn, icons.ContentAdd, "Add track\n(Ctrl+T)")
|
||||
addSemitoneBtnStyle := ActionButton(gtx, t.Theme, te.AddSemitoneBtn, "+1")
|
||||
subtractSemitoneBtnStyle := ActionButton(gtx, t.Theme, te.SubtractSemitoneBtn, "-1")
|
||||
addOctaveBtnStyle := ActionButton(gtx, t.Theme, te.AddOctaveBtn, "+12")
|
||||
subtractOctaveBtnStyle := ActionButton(gtx, t.Theme, te.SubtractOctaveBtn, "-12")
|
||||
noteOffBtnStyle := ActionButton(gtx, t.Theme, te.NoteOffBtn, "Note Off")
|
||||
deleteTrackBtnStyle := ActionIcon(gtx, t.Theme, te.DeleteTrackBtn, icons.ActionDelete, "Delete track\n(Ctrl+Shift+T)")
|
||||
newTrackBtnStyle := ActionIcon(gtx, t.Theme, te.NewTrackBtn, icons.ContentAdd, "Add track\n(Ctrl+T)")
|
||||
in := layout.UniformInset(unit.Dp(1))
|
||||
voiceUpDown := func(gtx C) D {
|
||||
numStyle := NumericUpDown(t.Theme, te.TrackVoices, "Number of voices for this track")
|
||||
return in.Layout(gtx, numStyle.Layout)
|
||||
}
|
||||
effectBtnStyle := ToggleButton(t.Theme, te.EffectBtn, "Hex")
|
||||
effectBtnStyle := ToggleButton(gtx, t.Theme, te.EffectBtn, "Hex")
|
||||
return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Rigid(func(gtx C) D { return layout.Dimensions{Size: image.Pt(gtx.Dp(unit.Dp(12)), 0)} }),
|
||||
layout.Rigid(addSemitoneBtnStyle.Layout),
|
||||
@ -304,7 +346,7 @@ func (te *NoteEditor) command(gtx C, t *Tracker, e key.Event) {
|
||||
}
|
||||
var n byte
|
||||
if t.Model.Notes().Effect(te.scrollTable.Table.Cursor().X) {
|
||||
if nibbleValue, err := strconv.ParseInt(e.Name, 16, 8); err == nil {
|
||||
if nibbleValue, err := strconv.ParseInt(string(e.Name), 16, 8); err == nil {
|
||||
n = t.Model.Notes().Value(te.scrollTable.Table.Cursor())
|
||||
t.Model.Notes().FillNibble(byte(nibbleValue), t.Model.Notes().LowNibble())
|
||||
goto validNote
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"gioui.org/x/component"
|
||||
|
||||
"gioui.org/gesture"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
@ -160,9 +161,16 @@ func (s *NumericUpDownStyle) layoutText(gtx C) D {
|
||||
func (s *NumericUpDownStyle) layoutDrag(gtx layout.Context) layout.Dimensions {
|
||||
{ // handle dragging
|
||||
pxPerStep := float32(gtx.Dp(s.UnitsPerStep))
|
||||
for _, ev := range gtx.Events(s.NumberInput) {
|
||||
for {
|
||||
ev, ok := gtx.Event(pointer.Filter{
|
||||
Target: s.NumberInput,
|
||||
Kinds: pointer.Press | pointer.Drag | pointer.Release,
|
||||
})
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if e, ok := ev.(pointer.Event); ok {
|
||||
switch e.Type {
|
||||
switch e.Kind {
|
||||
case pointer.Press:
|
||||
s.NumberInput.dragStartValue = s.NumberInput.Int.Value()
|
||||
s.NumberInput.dragStartXY = e.Position.X - e.Position.Y
|
||||
@ -180,10 +188,7 @@ func (s *NumericUpDownStyle) layoutDrag(gtx layout.Context) layout.Dimensions {
|
||||
// register for input
|
||||
dragRect := image.Rect(0, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)
|
||||
area := clip.Rect(dragRect).Push(gtx.Ops)
|
||||
pointer.InputOp{
|
||||
Tag: s.NumberInput,
|
||||
Types: pointer.Press | pointer.Drag | pointer.Release,
|
||||
}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, s.NumberInput)
|
||||
area.Pop()
|
||||
stack.Pop()
|
||||
}
|
||||
@ -192,9 +197,13 @@ func (s *NumericUpDownStyle) layoutDrag(gtx layout.Context) layout.Dimensions {
|
||||
|
||||
func (s *NumericUpDownStyle) layoutClick(gtx layout.Context, delta int, click *gesture.Click) layout.Dimensions {
|
||||
// handle clicking
|
||||
for _, e := range click.Events(gtx) {
|
||||
switch e.Type {
|
||||
case gesture.TypeClick:
|
||||
for {
|
||||
ev, ok := click.Update(gtx.Source)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch ev.Kind {
|
||||
case gesture.KindClick:
|
||||
s.NumberInput.Int.Add(delta)
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
@ -19,9 +20,9 @@ import (
|
||||
"github.com/vsariola/sointu/tracker"
|
||||
)
|
||||
|
||||
const patternCellHeight = 16
|
||||
const patternCellWidth = 16
|
||||
const patternRowMarkerWidth = 30
|
||||
const patternCellHeight = unit.Dp(16)
|
||||
const patternCellWidth = unit.Dp(16)
|
||||
const patternRowMarkerWidth = unit.Dp(30)
|
||||
const orderTitleHeight = unit.Dp(52)
|
||||
|
||||
type OrderEditor struct {
|
||||
@ -57,18 +58,11 @@ func (oe *OrderEditor) Layout(gtx C, t *Tracker) D {
|
||||
t.TrackEditor.scrollTable.RowTitleList.CenterOn(cursor.Y)
|
||||
}
|
||||
|
||||
for _, e := range gtx.Events(&oe.tag) {
|
||||
switch e := e.(type) {
|
||||
case key.Event:
|
||||
if e.State != key.Press {
|
||||
continue
|
||||
}
|
||||
oe.command(gtx, t, e)
|
||||
}
|
||||
}
|
||||
oe.handleEvents(gtx, t)
|
||||
|
||||
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()
|
||||
key.InputOp{Tag: &oe.tag, Keys: "Ctrl-⌫|Ctrl-⌦|⏎|Ctrl-⏎|0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z"}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, &oe.tag)
|
||||
|
||||
colTitle := func(gtx C, i int) D {
|
||||
h := gtx.Dp(orderTitleHeight)
|
||||
@ -77,13 +71,13 @@ func (oe *OrderEditor) Layout(gtx C, t *Tracker) D {
|
||||
gtx.Constraints = layout.Exact(image.Pt(1e6, 1e6))
|
||||
title := t.Model.Order().Title(i)
|
||||
LabelStyle{Alignment: layout.NW, Text: title, FontSize: unit.Sp(12), Color: mediumEmphasisTextColor, Shaper: t.Theme.Shaper}.Layout(gtx)
|
||||
return D{Size: image.Pt(patternCellWidth, h)}
|
||||
return D{Size: image.Pt(gtx.Dp(patternCellWidth), h)}
|
||||
}
|
||||
|
||||
rowTitle := func(gtx C, j int) D {
|
||||
w := gtx.Dp(unit.Dp(30))
|
||||
if playPos := t.PlayPosition(); t.SongPanel.PlayingBtn.Bool.Value() && j == playPos.OrderRow {
|
||||
paint.FillShape(gtx.Ops, patternPlayColor, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, patternCellHeight)}.Op())
|
||||
paint.FillShape(gtx.Ops, patternPlayColor, clip.Rect{Max: image.Pt(gtx.Constraints.Max.X, gtx.Dp(patternCellHeight))}.Op())
|
||||
}
|
||||
color := rowMarkerPatternTextColor
|
||||
if l := t.Loop(); j >= l.Start && j < l.Start+l.Length {
|
||||
@ -92,7 +86,7 @@ func (oe *OrderEditor) Layout(gtx C, t *Tracker) D {
|
||||
paint.ColorOp{Color: color}.Add(gtx.Ops)
|
||||
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{})
|
||||
return D{Size: image.Pt(w, patternCellHeight)}
|
||||
return D{Size: image.Pt(w, gtx.Dp(patternCellHeight))}
|
||||
}
|
||||
|
||||
selection := oe.scrollTable.Table.Range()
|
||||
@ -114,7 +108,7 @@ func (oe *OrderEditor) Layout(gtx C, t *Tracker) D {
|
||||
paint.ColorOp{Color: patternTextColor}.Add(gtx.Ops)
|
||||
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{})
|
||||
return D{Size: image.Pt(patternCellWidth, patternCellHeight)}
|
||||
return D{Size: image.Pt(gtx.Dp(patternCellWidth), gtx.Dp(patternCellHeight))}
|
||||
}
|
||||
|
||||
table := FilledScrollTable(t.Theme, oe.scrollTable, cell, colTitle, rowTitle, nil, nil)
|
||||
@ -123,6 +117,61 @@ func (oe *OrderEditor) Layout(gtx C, t *Tracker) D {
|
||||
return table.Layout(gtx)
|
||||
}
|
||||
|
||||
func (oe *OrderEditor) handleEvents(gtx C, t *Tracker) {
|
||||
for {
|
||||
e, ok := gtx.Event(
|
||||
key.Filter{Focus: oe.scrollTable, Name: key.NameDeleteBackward, Optional: key.ModShortcut},
|
||||
key.Filter{Focus: oe.scrollTable, Name: key.NameDeleteForward, Optional: key.ModShortcut},
|
||||
key.Filter{Focus: oe.scrollTable, Name: key.NameReturn, Optional: key.ModShortcut},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "0"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "1"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "2"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "3"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "4"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "5"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "6"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "7"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "8"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "9"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "A"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "B"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "C"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "D"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "E"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "F"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "G"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "H"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "I"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "J"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "K"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "L"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "M"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "N"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "O"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "P"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "Q"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "R"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "S"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "T"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "U"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "V"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "W"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "X"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "Y"},
|
||||
key.Filter{Focus: oe.scrollTable, Name: "Z"},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if e, ok := e.(key.Event); ok {
|
||||
if e.State != key.Press {
|
||||
continue
|
||||
}
|
||||
oe.command(gtx, t, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (oe *OrderEditor) command(gtx C, t *Tracker, e key.Event) {
|
||||
switch e.Name {
|
||||
case key.NameDeleteBackward:
|
||||
@ -140,7 +189,7 @@ func (oe *OrderEditor) command(gtx C, t *Tracker, e key.Event) {
|
||||
}
|
||||
t.Model.AddOrderRow(!e.Modifiers.Contain(key.ModShortcut)).Do()
|
||||
}
|
||||
if iv, err := strconv.Atoi(e.Name); err == nil {
|
||||
if iv, err := strconv.Atoi(string(e.Name)); err == nil {
|
||||
t.Model.Order().SetValue(oe.scrollTable.Table.Cursor(), iv)
|
||||
oe.scrollTable.EnsureCursorVisible()
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
@ -44,13 +45,19 @@ func (s PopupStyle) Layout(gtx C, contents layout.Widget) D {
|
||||
return D{}
|
||||
}
|
||||
|
||||
for _, ev := range gtx.Events(s.Visible) {
|
||||
e, ok := ev.(pointer.Event)
|
||||
for {
|
||||
event, ok := gtx.Event(pointer.Filter{
|
||||
Target: s.Visible,
|
||||
Kinds: pointer.Press,
|
||||
})
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
e, ok := event.(pointer.Event)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch e.Type {
|
||||
switch e.Kind {
|
||||
case pointer.Press:
|
||||
*s.Visible = false
|
||||
}
|
||||
@ -70,16 +77,10 @@ func (s PopupStyle) Layout(gtx C, contents layout.Widget) D {
|
||||
paint.FillShape(gtx.Ops, s.ShadowColor, rrect2.Op(gtx.Ops))
|
||||
paint.FillShape(gtx.Ops, s.SurfaceColor, rrect.Op(gtx.Ops))
|
||||
area := clip.Rect(image.Rect(-1e6, -1e6, 1e6, 1e6)).Push(gtx.Ops)
|
||||
pointer.InputOp{Tag: s.Visible,
|
||||
Types: pointer.Press,
|
||||
Grab: true,
|
||||
}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, s.Visible)
|
||||
area.Pop()
|
||||
area = clip.Rect(rrect2.Rect).Push(gtx.Ops)
|
||||
pointer.InputOp{Tag: &dummyTag,
|
||||
Types: pointer.Press,
|
||||
Grab: true,
|
||||
}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, &dummyTag)
|
||||
area.Pop()
|
||||
return D{Size: gtx.Constraints.Min}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func NewPopupAlert(alerts *tracker.Alerts, shaper *text.Shaper) *PopupAlert {
|
||||
func (a *PopupAlert) Layout(gtx C) D {
|
||||
now := time.Now()
|
||||
if a.alerts.Update(now.Sub(a.prevUpdate)) {
|
||||
op.InvalidateOp{At: now.Add(50 * time.Millisecond)}.Add(gtx.Ops)
|
||||
gtx.Execute(op.InvalidateCmd{At: now.Add(50 * time.Millisecond)})
|
||||
}
|
||||
a.prevUpdate = now
|
||||
|
||||
|
@ -1,11 +1,15 @@
|
||||
package gioui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"io"
|
||||
|
||||
"gioui.org/io/clipboard"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/io/transfer"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/clip"
|
||||
@ -20,7 +24,6 @@ type ScrollTable struct {
|
||||
Table tracker.Table
|
||||
focused bool
|
||||
requestFocus bool
|
||||
tag bool
|
||||
colTag bool
|
||||
rowTag bool
|
||||
cursorMoved bool
|
||||
@ -85,55 +88,10 @@ func (st *ScrollTable) ChildFocused() bool {
|
||||
|
||||
func (s ScrollTableStyle) Layout(gtx C) D {
|
||||
p := image.Pt(gtx.Dp(s.RowTitleWidth), gtx.Dp(s.ColumnTitleHeight))
|
||||
|
||||
for _, e := range gtx.Events(&s.ScrollTable.tag) {
|
||||
switch e := e.(type) {
|
||||
case key.FocusEvent:
|
||||
s.ScrollTable.focused = e.Focus
|
||||
case pointer.Event:
|
||||
if e.Position.X >= float32(p.X) && e.Position.Y >= float32(p.Y) {
|
||||
if e.Type == pointer.Press {
|
||||
key.FocusOp{Tag: &s.ScrollTable.tag}.Add(gtx.Ops)
|
||||
}
|
||||
dx := (int(e.Position.X) + s.ScrollTable.ColTitleList.List.Position.Offset - p.X) / gtx.Dp(s.CellWidth)
|
||||
dy := (int(e.Position.Y) + s.ScrollTable.RowTitleList.List.Position.Offset - p.Y) / gtx.Dp(s.CellHeight)
|
||||
x := dx + s.ScrollTable.ColTitleList.List.Position.First
|
||||
y := dy + s.ScrollTable.RowTitleList.List.Position.First
|
||||
s.ScrollTable.Table.SetCursor(
|
||||
tracker.Point{X: x, Y: y},
|
||||
)
|
||||
if !e.Modifiers.Contain(key.ModShift) {
|
||||
s.ScrollTable.Table.SetCursor2(s.ScrollTable.Table.Cursor())
|
||||
}
|
||||
s.ScrollTable.cursorMoved = true
|
||||
}
|
||||
case key.Event:
|
||||
if e.State == key.Press {
|
||||
s.ScrollTable.command(gtx, e)
|
||||
}
|
||||
case clipboard.Event:
|
||||
s.ScrollTable.Table.Paste([]byte(e.Text))
|
||||
}
|
||||
}
|
||||
|
||||
for _, e := range gtx.Events(&s.ScrollTable.rowTag) {
|
||||
if e, ok := e.(key.Event); ok && e.State == key.Press {
|
||||
s.ScrollTable.Focus()
|
||||
}
|
||||
}
|
||||
|
||||
for _, e := range gtx.Events(&s.ScrollTable.colTag) {
|
||||
if e, ok := e.(key.Event); ok && e.State == key.Press {
|
||||
s.ScrollTable.Focus()
|
||||
}
|
||||
}
|
||||
s.handleEvents(gtx)
|
||||
|
||||
return Surface{Gray: 24, Focus: s.ScrollTable.Focused() || s.ScrollTable.ChildFocused()}.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()
|
||||
pointer.InputOp{
|
||||
Tag: &s.ScrollTable.tag,
|
||||
Types: pointer.Press,
|
||||
}.Add(gtx.Ops)
|
||||
dims := gtx.Constraints.Max
|
||||
s.layoutColTitles(gtx, p)
|
||||
s.layoutRowTitles(gtx, p)
|
||||
@ -146,14 +104,105 @@ func (s ScrollTableStyle) Layout(gtx C) D {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ScrollTableStyle) handleEvents(gtx layout.Context) {
|
||||
for {
|
||||
e, ok := gtx.Event(
|
||||
key.FocusFilter{Target: s.ScrollTable},
|
||||
transfer.TargetFilter{Target: s.ScrollTable, Type: "application/text"},
|
||||
pointer.Filter{Target: s.ScrollTable, Kinds: pointer.Press},
|
||||
key.Filter{Focus: s.ScrollTable, Name: key.NameLeftArrow, Optional: key.ModShift | key.ModCtrl | key.ModAlt},
|
||||
key.Filter{Focus: s.ScrollTable, Name: key.NameUpArrow, Optional: key.ModShift | key.ModCtrl | key.ModAlt},
|
||||
key.Filter{Focus: s.ScrollTable, Name: key.NameRightArrow, Optional: key.ModShift | key.ModCtrl | key.ModAlt},
|
||||
key.Filter{Focus: s.ScrollTable, Name: key.NameDownArrow, Optional: key.ModShift | key.ModCtrl | key.ModAlt},
|
||||
key.Filter{Focus: s.ScrollTable, Name: key.NamePageUp, Optional: key.ModShift},
|
||||
key.Filter{Focus: s.ScrollTable, Name: key.NamePageDown, Optional: key.ModShift},
|
||||
key.Filter{Focus: s.ScrollTable, Name: key.NameHome, Optional: key.ModShift},
|
||||
key.Filter{Focus: s.ScrollTable, Name: key.NameEnd, Optional: key.ModShift},
|
||||
key.Filter{Focus: s.ScrollTable, Name: key.NameDeleteBackward},
|
||||
key.Filter{Focus: s.ScrollTable, Name: key.NameDeleteForward},
|
||||
key.Filter{Focus: s.ScrollTable, Name: "C", Required: key.ModShortcut},
|
||||
key.Filter{Focus: s.ScrollTable, Name: "V", Required: key.ModShortcut},
|
||||
key.Filter{Focus: s.ScrollTable, Name: "X", Required: key.ModShortcut},
|
||||
key.Filter{Focus: s.ScrollTable, Name: ",", Required: key.ModShift},
|
||||
key.Filter{Focus: s.ScrollTable, Name: ".", Required: key.ModShift},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch e := e.(type) {
|
||||
case key.FocusEvent:
|
||||
s.ScrollTable.focused = e.Focus
|
||||
case pointer.Event:
|
||||
if e.Kind == pointer.Press {
|
||||
gtx.Execute(key.FocusCmd{Tag: s.ScrollTable})
|
||||
}
|
||||
dx := (int(e.Position.X) + s.ScrollTable.ColTitleList.List.Position.Offset) / gtx.Dp(s.CellWidth)
|
||||
dy := (int(e.Position.Y) + s.ScrollTable.RowTitleList.List.Position.Offset) / gtx.Dp(s.CellHeight)
|
||||
x := dx + s.ScrollTable.ColTitleList.List.Position.First
|
||||
y := dy + s.ScrollTable.RowTitleList.List.Position.First
|
||||
s.ScrollTable.Table.SetCursor(
|
||||
tracker.Point{X: x, Y: y},
|
||||
)
|
||||
if !e.Modifiers.Contain(key.ModShift) {
|
||||
s.ScrollTable.Table.SetCursor2(s.ScrollTable.Table.Cursor())
|
||||
}
|
||||
s.ScrollTable.cursorMoved = true
|
||||
case key.Event:
|
||||
if e.State == key.Press {
|
||||
s.ScrollTable.command(gtx, e)
|
||||
}
|
||||
case transfer.DataEvent:
|
||||
if b, err := io.ReadAll(e.Open()); err == nil {
|
||||
s.ScrollTable.Table.Paste(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
e, ok := gtx.Event(
|
||||
key.FocusFilter{
|
||||
Target: &s.ScrollTable.rowTag,
|
||||
},
|
||||
key.Filter{
|
||||
Focus: &s.ScrollTable.rowTag,
|
||||
Name: "→",
|
||||
},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if e, ok := e.(key.Event); ok && e.State == key.Press {
|
||||
s.ScrollTable.Focus()
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
e, ok := gtx.Event(
|
||||
key.FocusFilter{
|
||||
Target: &s.ScrollTable.colTag,
|
||||
},
|
||||
key.Filter{
|
||||
Focus: &s.ScrollTable.colTag,
|
||||
Name: "↓",
|
||||
},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if e, ok := e.(key.Event); ok && e.State == key.Press {
|
||||
s.ScrollTable.Focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s ScrollTableStyle) layoutTable(gtx C, p image.Point) {
|
||||
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Min}).Push(gtx.Ops).Pop()
|
||||
|
||||
if s.ScrollTable.requestFocus {
|
||||
s.ScrollTable.requestFocus = false
|
||||
key.FocusOp{Tag: &s.ScrollTable.tag}.Add(gtx.Ops)
|
||||
gtx.Execute(key.FocusCmd{Tag: s.ScrollTable})
|
||||
}
|
||||
key.InputOp{Tag: &s.ScrollTable.tag, Keys: "←|→|↑|↓|Shift-←|Shift-→|Shift-↑|Shift-↓|Ctrl-←|Ctrl-→|Ctrl-↑|Ctrl-↓|Ctrl-Shift-←|Ctrl-Shift-→|Ctrl-Shift-↑|Ctrl-Shift-↓|Alt-←|Alt-→|Alt-↑|Alt-↓|Alt-Shift-←|Alt-Shift-→|Alt-Shift-↑|Alt-Shift-↓|⇱|⇲|Shift-⇱|Shift-⇲|⌫|⌦|⇞|⇟|Shift-⇞|Shift-⇟|Ctrl-C|Ctrl-V|Ctrl-X|Shift-,|Shift-."}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, s.ScrollTable)
|
||||
cellWidth := gtx.Dp(s.CellWidth)
|
||||
cellHeight := gtx.Dp(s.CellHeight)
|
||||
|
||||
@ -162,14 +211,12 @@ func (s ScrollTableStyle) layoutTable(gtx C, p image.Point) {
|
||||
colP := s.ColTitleStyle.dragList.List.Position
|
||||
rowP := s.RowTitleStyle.dragList.List.Position
|
||||
defer op.Offset(image.Pt(-colP.Offset, -rowP.Offset)).Push(gtx.Ops).Pop()
|
||||
for x := colP.First; x < colP.First+colP.Count; x++ {
|
||||
offs := op.Offset(image.Point{}).Push(gtx.Ops)
|
||||
for y := rowP.First; y < rowP.First+rowP.Count; y++ {
|
||||
s.element(gtx, x, y)
|
||||
op.Offset(image.Pt(0, cellHeight)).Add(gtx.Ops)
|
||||
for x := 0; x < colP.Count; x++ {
|
||||
for y := 0; y < rowP.Count; y++ {
|
||||
o := op.Offset(image.Pt(cellWidth*x, cellHeight*y)).Push(gtx.Ops)
|
||||
s.element(gtx, x+colP.First, y+rowP.First)
|
||||
o.Pop()
|
||||
}
|
||||
offs.Pop()
|
||||
op.Offset(image.Pt(cellWidth, 0)).Add(gtx.Ops)
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +226,7 @@ func (s *ScrollTableStyle) layoutRowTitles(gtx C, p image.Point) {
|
||||
gtx.Constraints.Max.Y -= p.Y
|
||||
gtx.Constraints.Min.Y = gtx.Constraints.Max.Y
|
||||
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Push(gtx.Ops).Pop()
|
||||
key.InputOp{Tag: &s.ScrollTable.rowTag, Keys: "→"}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, &s.ScrollTable.rowTag)
|
||||
s.RowTitleStyle.Layout(gtx)
|
||||
}
|
||||
|
||||
@ -189,7 +236,7 @@ func (s *ScrollTableStyle) layoutColTitles(gtx C, p image.Point) {
|
||||
gtx.Constraints.Max.X -= p.X
|
||||
gtx.Constraints.Min.X = gtx.Constraints.Max.X
|
||||
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Max}).Push(gtx.Ops).Pop()
|
||||
key.InputOp{Tag: &s.ScrollTable.colTag, Keys: "↓"}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, &s.ScrollTable.colTag)
|
||||
s.ColTitleStyle.Layout(gtx)
|
||||
}
|
||||
|
||||
@ -210,7 +257,7 @@ func (s *ScrollTable) command(gtx C, e key.Event) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops)
|
||||
gtx.Execute(clipboard.WriteCmd{Type: "application/text", Data: io.NopCloser(bytes.NewReader(contents))})
|
||||
if e.Name == "X" {
|
||||
s.Table.Clear()
|
||||
}
|
||||
@ -218,7 +265,7 @@ func (s *ScrollTable) command(gtx C, e key.Event) {
|
||||
}
|
||||
case "V":
|
||||
if e.Modifiers.Contain(key.ModShortcut) {
|
||||
clipboard.ReadOp{Tag: &s.tag}.Add(gtx.Ops)
|
||||
gtx.Execute(clipboard.ReadCmd{Tag: s})
|
||||
}
|
||||
return
|
||||
case key.NameDeleteBackward, key.NameDeleteForward:
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"image"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
@ -77,19 +78,22 @@ func (s *ScrollBar) Layout(gtx C, width unit.Dp, numItems int, pos *layout.Posit
|
||||
rect := image.Rect(0, gtx.Constraints.Min.Y-scrWidth, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)
|
||||
area = clip.Rect(rect).Push(gtx.Ops)
|
||||
}
|
||||
pointer.InputOp{Tag: &s.dragStart,
|
||||
Types: pointer.Drag | pointer.Press | pointer.Cancel | pointer.Release,
|
||||
Grab: s.dragging,
|
||||
}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, &s.dragStart)
|
||||
area.Pop()
|
||||
stack.Pop()
|
||||
|
||||
for _, ev := range gtx.Events(&s.dragStart) {
|
||||
for {
|
||||
ev, ok := gtx.Event(
|
||||
pointer.Filter{Target: &s.dragStart, Kinds: pointer.Press | pointer.Cancel | pointer.Release | pointer.Drag},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
e, ok := ev.(pointer.Event)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch e.Type {
|
||||
switch e.Kind {
|
||||
case pointer.Press:
|
||||
if s.Axis == layout.Horizontal {
|
||||
s.dragStart = e.Position.X
|
||||
@ -114,17 +118,22 @@ func (s *ScrollBar) Layout(gtx C, width unit.Dp, numItems int, pos *layout.Posit
|
||||
rect := image.Rect(0, 0, gtx.Constraints.Min.X, gtx.Constraints.Min.Y)
|
||||
area2 := clip.Rect(rect).Push(gtx.Ops)
|
||||
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
|
||||
pointer.InputOp{Tag: &s.tag,
|
||||
Types: pointer.Enter | pointer.Leave,
|
||||
}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, &s.tag)
|
||||
area2.Pop()
|
||||
|
||||
for _, ev := range gtx.Events(&s.tag) {
|
||||
for {
|
||||
ev, ok := gtx.Event(pointer.Filter{
|
||||
Target: &s.tag,
|
||||
Kinds: pointer.Enter | pointer.Leave,
|
||||
})
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
e, ok := ev.(pointer.Event)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch e.Type {
|
||||
switch e.Kind {
|
||||
case pointer.Enter:
|
||||
s.hovering = true
|
||||
case pointer.Leave:
|
||||
|
@ -93,8 +93,8 @@ func (t *SongPanel) layoutMenuBar(gtx C, tr *Tracker) D {
|
||||
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(36))
|
||||
|
||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||
layout.Rigid(tr.layoutMenu("File", &t.MenuBar[0], &t.Menus[0], unit.Dp(200), t.fileMenuItems...)),
|
||||
layout.Rigid(tr.layoutMenu("Edit", &t.MenuBar[1], &t.Menus[1], unit.Dp(200), t.editMenuItems...)),
|
||||
layout.Rigid(tr.layoutMenu(gtx, "File", &t.MenuBar[0], &t.Menus[0], unit.Dp(200), t.fileMenuItems...)),
|
||||
layout.Rigid(tr.layoutMenu(gtx, "Edit", &t.MenuBar[1], &t.Menus[1], unit.Dp(200), t.editMenuItems...)),
|
||||
)
|
||||
}
|
||||
|
||||
@ -103,12 +103,12 @@ func (t *SongPanel) layoutSongOptions(gtx C, tr *Tracker) D {
|
||||
|
||||
in := layout.UniformInset(unit.Dp(1))
|
||||
|
||||
panicBtnStyle := ToggleButton(tr.Theme, t.PanicBtn, "Panic (F12)")
|
||||
rewindBtnStyle := ActionIcon(tr.Theme, t.RewindBtn, icons.AVFastRewind, "Rewind\n(F5)")
|
||||
playBtnStyle := ToggleIcon(tr.Theme, t.PlayingBtn, icons.AVPlayArrow, icons.AVStop, "Play (F6 / Space)", "Stop (F6 / Space)")
|
||||
recordBtnStyle := ToggleIcon(tr.Theme, t.RecordBtn, icons.AVFiberManualRecord, icons.AVFiberSmartRecord, "Record (F7)", "Stop (F7)")
|
||||
noteTrackBtnStyle := ToggleIcon(tr.Theme, t.NoteTracking, icons.ActionSpeakerNotesOff, icons.ActionSpeakerNotes, "Follow\nOff\n(F8)", "Follow\nOn\n(F8)")
|
||||
loopBtnStyle := ToggleIcon(tr.Theme, t.LoopBtn, icons.NavigationArrowForward, icons.AVLoop, "Loop\nOff\n(Ctrl+L)", "Loop\nOn\n(Ctrl+L)")
|
||||
panicBtnStyle := ToggleButton(gtx, tr.Theme, t.PanicBtn, "Panic (F12)")
|
||||
rewindBtnStyle := ActionIcon(gtx, tr.Theme, t.RewindBtn, icons.AVFastRewind, "Rewind\n(F5)")
|
||||
playBtnStyle := ToggleIcon(gtx, tr.Theme, t.PlayingBtn, icons.AVPlayArrow, icons.AVStop, "Play (F6 / Space)", "Stop (F6 / Space)")
|
||||
recordBtnStyle := ToggleIcon(gtx, tr.Theme, t.RecordBtn, icons.AVFiberManualRecord, icons.AVFiberSmartRecord, "Record (F7)", "Stop (F7)")
|
||||
noteTrackBtnStyle := ToggleIcon(gtx, tr.Theme, t.NoteTracking, icons.ActionSpeakerNotesOff, icons.ActionSpeakerNotes, "Follow\nOff\n(F8)", "Follow\nOn\n(F8)")
|
||||
loopBtnStyle := ToggleIcon(gtx, tr.Theme, t.LoopBtn, icons.NavigationArrowForward, icons.AVLoop, "Loop\nOff\n(Ctrl+L)", "Loop\nOn\n(Ctrl+L)")
|
||||
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx C) D {
|
||||
|
@ -3,6 +3,7 @@ package gioui
|
||||
import (
|
||||
"image"
|
||||
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
@ -48,14 +49,21 @@ func (s *Split) Layout(gtx layout.Context, first, second layout.Widget) layout.D
|
||||
|
||||
{ // handle input
|
||||
// Avoid affecting the input tree with pointer events.
|
||||
|
||||
for _, ev := range gtx.Events(s) {
|
||||
for {
|
||||
ev, ok := gtx.Event(pointer.Filter{
|
||||
Target: s,
|
||||
Kinds: pointer.Press | pointer.Drag | pointer.Release,
|
||||
// TODO: there should be a grab; there was Grab: s.drag,
|
||||
})
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
e, ok := ev.(pointer.Event)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch e.Type {
|
||||
switch e.Kind {
|
||||
case pointer.Press:
|
||||
if s.drag {
|
||||
break
|
||||
@ -123,10 +131,7 @@ func (s *Split) Layout(gtx layout.Context, first, second layout.Widget) layout.D
|
||||
barRect = image.Rect(0, firstSize, gtx.Constraints.Max.X, secondOffset)
|
||||
}
|
||||
area := clip.Rect(barRect).Push(gtx.Ops)
|
||||
pointer.InputOp{Tag: s,
|
||||
Types: pointer.Press | pointer.Drag | pointer.Release,
|
||||
Grab: s.drag,
|
||||
}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, s)
|
||||
area.Pop()
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,14 @@ import (
|
||||
"image"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gioui.org/app"
|
||||
"gioui.org/io/clipboard"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/io/system"
|
||||
"gioui.org/io/transfer"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/clip"
|
||||
@ -34,7 +34,7 @@ type (
|
||||
TopHorizontalSplit *Split
|
||||
BottomHorizontalSplit *Split
|
||||
VerticalSplit *Split
|
||||
KeyPlaying map[string]tracker.NoteID
|
||||
KeyPlaying map[key.Name]tracker.NoteID
|
||||
PopupAlert *PopupAlert
|
||||
|
||||
SaveChangesDialog *Dialog
|
||||
@ -76,7 +76,7 @@ func NewTracker(model *tracker.Model) *Tracker {
|
||||
BottomHorizontalSplit: &Split{Ratio: -.6},
|
||||
VerticalSplit: &Split{Axis: layout.Vertical},
|
||||
|
||||
KeyPlaying: make(map[string]tracker.NoteID),
|
||||
KeyPlaying: make(map[key.Name]tracker.NoteID),
|
||||
SaveChangesDialog: NewDialog(model.SaveSong(), model.DiscardSong(), model.Cancel()),
|
||||
WaveTypeDialog: NewDialog(model.ExportInt16(), model.ExportFloat(), model.Cancel()),
|
||||
InstrumentEditor: NewInstrumentEditor(model),
|
||||
@ -107,6 +107,11 @@ func (t *Tracker) Main() {
|
||||
t.InstrumentEditor.Focus()
|
||||
recoveryTicker := time.NewTicker(time.Second * 30)
|
||||
t.Explorer = explorer.NewExplorer(w)
|
||||
// Make a channel to read window events from.
|
||||
events := make(chan event.Event)
|
||||
// Make a channel to signal the end of processing a window event.
|
||||
acks := make(chan struct{})
|
||||
go eventLoop(w, events, acks)
|
||||
var ops op.Ops
|
||||
for {
|
||||
if titleFooter != t.filePathString.Value() {
|
||||
@ -121,9 +126,10 @@ func (t *Tracker) Main() {
|
||||
case e := <-t.PlayerMessages:
|
||||
t.ProcessPlayerMessage(e)
|
||||
w.Invalidate()
|
||||
case e := <-w.Events():
|
||||
case e := <-events:
|
||||
switch e := e.(type) {
|
||||
case system.DestroyEvent:
|
||||
case app.DestroyEvent:
|
||||
acks <- struct{}{}
|
||||
if canQuit {
|
||||
t.Quit().Do()
|
||||
}
|
||||
@ -134,14 +140,18 @@ func (t *Tracker) Main() {
|
||||
app.Title("Sointu Tracker"),
|
||||
)
|
||||
t.Explorer = explorer.NewExplorer(w)
|
||||
go eventLoop(w, events, acks)
|
||||
}
|
||||
case system.FrameEvent:
|
||||
gtx := layout.NewContext(&ops, e)
|
||||
case app.FrameEvent:
|
||||
gtx := app.NewContext(&ops, e)
|
||||
if t.SongPanel.PlayingBtn.Bool.Value() && t.SongPanel.NoteTracking.Bool.Value() {
|
||||
t.TrackEditor.scrollTable.RowTitleList.CenterOn(t.PlaySongRow())
|
||||
}
|
||||
t.Layout(gtx, w)
|
||||
e.Frame(gtx.Ops)
|
||||
acks <- struct{}{}
|
||||
default:
|
||||
acks <- struct{}{}
|
||||
}
|
||||
case <-recoveryTicker.C:
|
||||
t.SaveRecovery()
|
||||
@ -158,6 +168,19 @@ func (t *Tracker) Main() {
|
||||
t.quitWG.Done()
|
||||
}
|
||||
|
||||
func eventLoop(w *app.Window, events chan<- event.Event, acks <-chan struct{}) {
|
||||
// Iterate window events, sending each to the old event loop and waiting for
|
||||
// a signal that processing is complete before iterating again.
|
||||
for {
|
||||
ev := w.NextEvent()
|
||||
events <- ev
|
||||
<-acks
|
||||
if _, ok := ev.(app.DestroyEvent); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracker) Exec() chan<- func() {
|
||||
return t.execChan
|
||||
}
|
||||
@ -167,22 +190,6 @@ func (t *Tracker) WaitQuitted() {
|
||||
}
|
||||
|
||||
func (t *Tracker) Layout(gtx layout.Context, w *app.Window) {
|
||||
// this is the top level input handler for the whole app
|
||||
// it handles all the global key events and clipboard events
|
||||
// we need to tell gio that we handle tabs too; otherwise
|
||||
// it will steal them for focus switching
|
||||
key.InputOp{Tag: t, Keys: "Tab|Shift-Tab"}.Add(gtx.Ops)
|
||||
for _, ev := range gtx.Events(t) {
|
||||
switch e := ev.(type) {
|
||||
case key.Event:
|
||||
t.KeyEvent(e, gtx.Ops)
|
||||
case clipboard.Event:
|
||||
stringReader := strings.NewReader(e.Text)
|
||||
stringReadCloser := io.NopCloser(stringReader)
|
||||
t.ReadSong(stringReadCloser)
|
||||
}
|
||||
}
|
||||
|
||||
paint.FillShape(gtx.Ops, backgroundColor, clip.Rect(image.Rect(0, 0, gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Op())
|
||||
if t.InstrumentEditor.enlargeBtn.Bool.Value() {
|
||||
t.layoutTop(gtx)
|
||||
@ -193,6 +200,27 @@ func (t *Tracker) Layout(gtx layout.Context, w *app.Window) {
|
||||
}
|
||||
t.PopupAlert.Layout(gtx)
|
||||
t.showDialog(gtx)
|
||||
// this is the top level input handler for the whole app
|
||||
// it handles all the global key events and clipboard events
|
||||
// we need to tell gio that we handle tabs too; otherwise
|
||||
// it will steal them for focus switching
|
||||
for {
|
||||
ev, ok := gtx.Event(
|
||||
key.Filter{Name: "", Optional: key.ModAlt | key.ModCommand | key.ModShift | key.ModShortcut | key.ModSuper},
|
||||
key.Filter{Name: key.NameTab, Optional: key.ModShift},
|
||||
transfer.TargetFilter{Target: t, Type: "application/text"},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch e := ev.(type) {
|
||||
case key.Event:
|
||||
t.KeyEvent(e, gtx)
|
||||
case transfer.DataEvent:
|
||||
t.ReadSong(e.Open())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (t *Tracker) showDialog(gtx C) {
|
||||
@ -201,12 +229,12 @@ func (t *Tracker) showDialog(gtx C) {
|
||||
}
|
||||
switch t.Dialog() {
|
||||
case tracker.NewSongChanges, tracker.OpenSongChanges, tracker.QuitChanges:
|
||||
dstyle := ConfirmDialog(t.Theme, t.SaveChangesDialog, "Save changes to song?", "Your changes will be lost if you don't save them.")
|
||||
dstyle := ConfirmDialog(gtx, t.Theme, t.SaveChangesDialog, "Save changes to song?", "Your changes will be lost if you don't save them.")
|
||||
dstyle.OkStyle.Text = "Save"
|
||||
dstyle.AltStyle.Text = "Don't save"
|
||||
dstyle.Layout(gtx)
|
||||
case tracker.Export:
|
||||
dstyle := ConfirmDialog(t.Theme, t.WaveTypeDialog, "", "Export .wav in int16 or float32 sample format?")
|
||||
dstyle := ConfirmDialog(gtx, t.Theme, t.WaveTypeDialog, "", "Export .wav in int16 or float32 sample format?")
|
||||
dstyle.OkStyle.Text = "Int16"
|
||||
dstyle.AltStyle.Text = "Float32"
|
||||
dstyle.Layout(gtx)
|
||||
|
@ -1,11 +1,14 @@
|
||||
package gioui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"gioui.org/io/clipboard"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/layout"
|
||||
@ -29,7 +32,6 @@ type UnitEditor struct {
|
||||
ClearUnitBtn *ActionClickable
|
||||
DisableUnitBtn *BoolClickable
|
||||
SelectTypeBtn *widget.Clickable
|
||||
tag bool
|
||||
caser cases.Caser
|
||||
}
|
||||
|
||||
@ -48,7 +50,15 @@ func NewUnitEditor(m *tracker.Model) *UnitEditor {
|
||||
}
|
||||
|
||||
func (pe *UnitEditor) Layout(gtx C, t *Tracker) D {
|
||||
for _, e := range gtx.Events(&pe.tag) {
|
||||
for {
|
||||
e, ok := gtx.Event(
|
||||
key.Filter{Focus: pe.sliderList, Name: key.NameLeftArrow, Optional: key.ModShift},
|
||||
key.Filter{Focus: pe.sliderList, Name: key.NameRightArrow, Optional: key.ModShift},
|
||||
key.Filter{Focus: pe.sliderList, Name: key.NameEscape},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch e := e.(type) {
|
||||
case key.Event:
|
||||
if e.State == key.Press {
|
||||
@ -58,8 +68,6 @@ func (pe *UnitEditor) Layout(gtx C, t *Tracker) D {
|
||||
}
|
||||
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()
|
||||
key.InputOp{Tag: &pe.tag, Keys: "←|Shift-←|→|Shift-→|⎋"}.Add(gtx.Ops)
|
||||
|
||||
editorFunc := pe.layoutSliders
|
||||
|
||||
if t.UnitSearching().Value() || pe.sliderList.TrackerList.Count() == 0 {
|
||||
@ -108,15 +116,15 @@ func (pe *UnitEditor) layoutSliders(gtx C, t *Tracker) D {
|
||||
}
|
||||
|
||||
func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
|
||||
for pe.CopyUnitBtn.Clickable.Clicked() {
|
||||
for pe.CopyUnitBtn.Clickable.Clicked(gtx) {
|
||||
if contents, ok := t.Units().List().CopyElements(); ok {
|
||||
clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops)
|
||||
gtx.Execute(clipboard.WriteCmd{Type: "application/text", Data: io.NopCloser(bytes.NewReader(contents))})
|
||||
t.Alerts().Add("Unit copied to clipboard", tracker.Info)
|
||||
}
|
||||
}
|
||||
copyUnitBtnStyle := TipIcon(t.Theme, pe.CopyUnitBtn, icons.ContentContentCopy, "Copy unit (Ctrl+C)")
|
||||
deleteUnitBtnStyle := ActionIcon(t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)")
|
||||
disableUnitBtnStyle := ToggleIcon(t.Theme, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, "Disable unit (Ctrl-D)", "Enable unit (Ctrl-D)")
|
||||
deleteUnitBtnStyle := ActionIcon(gtx, t.Theme, pe.DeleteUnitBtn, icons.ActionDelete, "Delete unit (Ctrl+Backspace)")
|
||||
disableUnitBtnStyle := ToggleIcon(gtx, t.Theme, pe.DisableUnitBtn, icons.AVVolumeUp, icons.AVVolumeOff, "Disable unit (Ctrl-D)", "Enable unit (Ctrl-D)")
|
||||
text := t.Units().SelectedType()
|
||||
if text == "" {
|
||||
text = "Choose unit type"
|
||||
@ -131,7 +139,7 @@ func (pe *UnitEditor) layoutFooter(gtx C, t *Tracker) D {
|
||||
layout.Rigid(func(gtx C) D {
|
||||
var dims D
|
||||
if t.Units().SelectedType() != "" {
|
||||
clearUnitBtnStyle := ActionIcon(t.Theme, pe.ClearUnitBtn, icons.ContentClear, "Clear unit")
|
||||
clearUnitBtnStyle := ActionIcon(gtx, t.Theme, pe.ClearUnitBtn, icons.ContentClear, "Clear unit")
|
||||
dims = clearUnitBtnStyle.Layout(gtx)
|
||||
}
|
||||
return D{Size: image.Pt(gtx.Dp(unit.Dp(48)), dims.Size.Y)}
|
||||
@ -151,7 +159,7 @@ func (pe *UnitEditor) layoutUnitTypeChooser(gtx C, t *Tracker) 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}
|
||||
if i == pe.searchList.TrackerList.Selected() {
|
||||
for pe.SelectTypeBtn.Clicked() {
|
||||
for pe.SelectTypeBtn.Clicked(gtx) {
|
||||
t.Units().SetSelectedType(names[i])
|
||||
}
|
||||
return pe.SelectTypeBtn.Layout(gtx, w.Layout)
|
||||
@ -232,28 +240,36 @@ func (p ParameterStyle) Layout(gtx C) D {
|
||||
layout.Rigid(func(gtx C) D {
|
||||
switch p.w.Parameter.Type() {
|
||||
case tracker.IntegerParameter:
|
||||
for _, e := range gtx.Events(&p.w.floatWidget) {
|
||||
if ev, ok := e.(pointer.Event); ok && ev.Type == pointer.Scroll {
|
||||
for p.Focus {
|
||||
e, ok := gtx.Event(pointer.Filter{
|
||||
Target: &p.w.floatWidget,
|
||||
Kinds: pointer.Scroll,
|
||||
ScrollBounds: image.Rectangle{Min: image.Pt(0, -1e6), Max: image.Pt(0, 1e6)},
|
||||
})
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if ev, ok := e.(pointer.Event); ok && ev.Kind == pointer.Scroll {
|
||||
delta := math.Min(math.Max(float64(ev.Scroll.Y), -1), 1)
|
||||
tracker.Int{IntData: p.w.Parameter}.Add(-int(delta))
|
||||
}
|
||||
}
|
||||
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(200))
|
||||
gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(40))
|
||||
if !p.w.floatWidget.Dragging() {
|
||||
p.w.floatWidget.Value = float32(p.w.Parameter.Value())
|
||||
}
|
||||
ra := p.w.Parameter.Range()
|
||||
sliderStyle := material.Slider(p.Theme, &p.w.floatWidget, float32(ra.Min), float32(ra.Max))
|
||||
if !p.w.floatWidget.Dragging() {
|
||||
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.Color = p.Theme.Fg
|
||||
r := image.Rectangle{Max: gtx.Constraints.Min}
|
||||
area := clip.Rect(r).Push(gtx.Ops)
|
||||
if p.Focus {
|
||||
pointer.InputOp{Tag: &p.w.floatWidget, Types: pointer.Scroll, ScrollBounds: image.Rectangle{Min: image.Pt(0, -1e6), Max: image.Pt(0, 1e6)}}.Add(gtx.Ops)
|
||||
event.Op(gtx.Ops, &p.w.floatWidget)
|
||||
}
|
||||
dims := sliderStyle.Layout(gtx)
|
||||
area.Pop()
|
||||
tracker.Int{IntData: p.w.Parameter}.Set(int(p.w.floatWidget.Value + 0.5))
|
||||
tracker.Int{IntData: p.w.Parameter}.Set(int(p.w.floatWidget.Value*float32(ra.Max-ra.Min) + float32(ra.Min) + 0.5))
|
||||
return dims
|
||||
case tracker.BoolParameter:
|
||||
gtx.Constraints.Min.X = gtx.Dp(unit.Dp(60))
|
||||
@ -305,10 +321,10 @@ func (p ParameterStyle) Layout(gtx C) D {
|
||||
}
|
||||
}
|
||||
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
|
||||
layout.Rigid(p.tracker.layoutMenu(instrName, &p.w.instrBtn, &p.w.instrMenu, unit.Dp(200),
|
||||
layout.Rigid(p.tracker.layoutMenu(gtx, instrName, &p.w.instrBtn, &p.w.instrMenu, unit.Dp(200),
|
||||
instrItems...,
|
||||
)),
|
||||
layout.Rigid(p.tracker.layoutMenu(unitName, &p.w.unitBtn, &p.w.unitMenu, unit.Dp(200),
|
||||
layout.Rigid(p.tracker.layoutMenu(gtx, unitName, &p.w.unitBtn, &p.w.unitMenu, unit.Dp(200),
|
||||
unitItems...,
|
||||
)),
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user