feat(tracker/gioui): preferences.yml for window size or maximized (#185)

This commit is contained in:
qm210 2024-12-07 12:54:08 +01:00 committed by GitHub
parent 4169356845
commit 7ff3c942cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 94 additions and 21 deletions

View File

@ -26,6 +26,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
([#77][i77]) ([#77][i77])
- User can define own keybindings in `os.UserConfigDir()/sointu/keybindings.yml` - User can define own keybindings in `os.UserConfigDir()/sointu/keybindings.yml`
([#94][i94], [#151][i151]) ([#94][i94], [#151][i151])
- User can define preferred window size in
`os.UserConfigDir()/sointu/preferences.yml` ([#184][i184])
- A small number above the instrument name identifies the MIDI channel / - A small number above the instrument name identifies the MIDI channel /
instrument number, with numbering starting from 1 ([#154][i154]) instrument number, with numbering starting from 1 ([#154][i154])
- The filter unit frequency parameter is displayed in Hz, corresponding roughly - The filter unit frequency parameter is displayed in Hz, corresponding roughly

View File

@ -3,8 +3,6 @@ package gioui
import ( import (
_ "embed" _ "embed"
"fmt" "fmt"
"os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
@ -28,16 +26,7 @@ var keyActionMap = map[KeyAction]string{} // holds an informative string of the
func loadCustomKeyBindings() []KeyBinding { func loadCustomKeyBindings() []KeyBinding {
var keyBindings []KeyBinding var keyBindings []KeyBinding
configDir, err := os.UserConfigDir() _, err := ReadCustomConfigYml("keybindings.yml", &keyBindings)
if err != nil {
return nil
}
path := filepath.Join(configDir, "sointu", "keybindings.yml")
bytes, err := os.ReadFile(path)
if err != nil {
return nil
}
err = yaml.Unmarshal(bytes, &keyBindings)
if err != nil { if err != nil {
return nil return nil
} }

View File

@ -0,0 +1,65 @@
package gioui
import (
_ "embed"
"fmt"
"os"
"path/filepath"
"gopkg.in/yaml.v2"
"gioui.org/unit"
)
type (
Preferences struct {
Window WindowPreferences
YmlError error
}
WindowPreferences struct {
Width int
Height int
Maximized bool `yaml:",omitempty"`
}
)
//go:embed preferences.yml
var defaultPreferencesYaml []byte
func loadDefaultPreferences() Preferences {
var preferences Preferences
err := yaml.Unmarshal(defaultPreferencesYaml, &preferences)
if err != nil {
panic(fmt.Errorf("failed to unmarshal preferences: %w", err))
}
return preferences
}
// ReadCustomConfigYml modifies the target argument, i.e. needs a pointer
func ReadCustomConfigYml(filename string, target interface{}) (exists bool, err error) {
configDir, err := os.UserConfigDir()
if err != nil {
return false, err
}
path := filepath.Join(configDir, "sointu", filename)
bytes, err2 := os.ReadFile(path)
if err2 != nil {
return false, err2
}
err = yaml.Unmarshal(bytes, target)
return true, err
}
func MakePreferences() Preferences {
preferences := loadDefaultPreferences()
exists, err := ReadCustomConfigYml("preferences.yml", &preferences)
if exists {
preferences.YmlError = err
}
return preferences
}
func (p Preferences) WindowSize() (unit.Dp, unit.Dp) {
return unit.Dp(p.Window.Width), unit.Dp(p.Window.Height)
}

View File

@ -0,0 +1,4 @@
window:
width: 800
height: 600
maximized: false

View File

@ -18,7 +18,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/unit"
"gioui.org/widget/material" "gioui.org/widget/material"
"gioui.org/x/explorer" "gioui.org/x/explorer"
"github.com/vsariola/sointu/tracker" "github.com/vsariola/sointu/tracker"
@ -51,8 +50,9 @@ type (
filePathString tracker.String filePathString tracker.String
quitWG sync.WaitGroup quitWG sync.WaitGroup
execChan chan func() execChan chan func()
preferences Preferences
*tracker.Model *tracker.Model
} }
@ -89,9 +89,16 @@ func NewTracker(model *tracker.Model) *Tracker {
Model: model, Model: model,
filePathString: model.FilePath().String(), filePathString: model.FilePath().String(),
preferences: MakePreferences(),
} }
t.Theme.Shaper = text.NewShaper(text.WithCollection(fontCollection)) t.Theme.Shaper = text.NewShaper(text.WithCollection(fontCollection))
t.PopupAlert = NewPopupAlert(model.Alerts(), t.Theme.Shaper) t.PopupAlert = NewPopupAlert(model.Alerts(), t.Theme.Shaper)
if t.preferences.YmlError != nil {
model.Alerts().Add(
fmt.Sprintf("Preferences YML Error: %s", t.preferences.YmlError),
tracker.Warning,
)
}
t.Theme.Palette.Fg = primaryColor t.Theme.Palette.Fg = primaryColor
t.Theme.Palette.ContrastFg = black t.Theme.Palette.ContrastFg = black
t.TrackEditor.scrollTable.Focus() t.TrackEditor.scrollTable.Focus()
@ -101,9 +108,7 @@ func NewTracker(model *tracker.Model) *Tracker {
func (t *Tracker) Main() { func (t *Tracker) Main() {
titleFooter := "" titleFooter := ""
w := new(app.Window) w := t.newWindow()
w.Option(app.Title("Sointu Tracker"))
w.Option(app.Size(unit.Dp(800), unit.Dp(600)))
t.InstrumentEditor.Focus() t.InstrumentEditor.Focus()
recoveryTicker := time.NewTicker(time.Second * 30) recoveryTicker := time.NewTicker(time.Second * 30)
t.Explorer = explorer.NewExplorer(w) t.Explorer = explorer.NewExplorer(w)
@ -127,9 +132,7 @@ func (t *Tracker) Main() {
} }
if !t.Quitted() { if !t.Quitted() {
// TODO: uh oh, there's no way of canceling the destroyevent in gioui? so we create a new window just to show the dialog // TODO: uh oh, there's no way of canceling the destroyevent in gioui? so we create a new window just to show the dialog
w = new(app.Window) w = t.newWindow()
w.Option(app.Title("Sointu Tracker"))
w.Option(app.Size(unit.Dp(800), unit.Dp(600)))
t.Explorer = explorer.NewExplorer(w) t.Explorer = explorer.NewExplorer(w)
go eventLoop(w, events, acks) go eventLoop(w, events, acks)
} }
@ -165,6 +168,16 @@ func (t *Tracker) Main() {
t.quitWG.Done() t.quitWG.Done()
} }
func (t *Tracker) newWindow() *app.Window {
w := new(app.Window)
w.Option(app.Title("Sointu Tracker"))
w.Option(app.Size(t.preferences.WindowSize()))
if t.preferences.Window.Maximized {
w.Option(app.Maximized.Option())
}
return w
}
func eventLoop(w *app.Window, events chan<- event.Event, acks <-chan struct{}) { 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 // Iterate window events, sending each to the old event loop and waiting for
// a signal that processing is complete before iterating again. // a signal that processing is complete before iterating again.