Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: preferences.yml for window size #185

Merged
merged 2 commits into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
([#77][i77])
- User can define own keybindings in `os.UserConfigDir()/sointu/keybindings.yml`
([#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 /
instrument number, with numbering starting from 1 ([#154][i154])
- The filter unit frequency parameter is displayed in Hz, corresponding roughly
Expand Down
13 changes: 1 addition & 12 deletions tracker/gioui/keyevent.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package gioui
import (
_ "embed"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"

Expand All @@ -28,16 +26,7 @@ var keyActionMap = map[KeyAction]string{} // holds an informative string of the

func loadCustomKeyBindings() []KeyBinding {
var keyBindings []KeyBinding
configDir, err := os.UserConfigDir()
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)
_, err := ReadCustomConfigYml("keybindings.yml", &keyBindings)
if err != nil {
return nil
}
Expand Down
65 changes: 65 additions & 0 deletions tracker/gioui/preferences.go
Original file line number Diff line number Diff line change
@@ -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"`
Copy link
Owner

@vsariola vsariola Nov 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: To me, "maximized" means a different concept from fullscreen. Maximized means the window takes maximal amount of space, but keeping the decoration. Fullscreen means the bottom bars and window decorations are gone. So please change this to either "fullscreen" or consider implementing both "fullscreen" and "maximized"

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gioui seems to agree: maximized, minimized, windowed and fullscreen are different options: https://github.com/gioui/gio/blob/8daff13af6cdbe4cb689e324f6ba343062dad050/app/window.go#L853

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, yeah, this confuses me. All I ever meant was "Maximized" (and I also see no use for actual Fullscreen), but when I try the gioui options, the app.Fullscreen.Option() that I used and the app.Maximized.Option() give the same effect (i.e. Maximized).

as the app.Fullscreen.Option() gave me what I wanted, I didn't get the idea of looking further; but as no other people really asked for Fullscreen, I would then just use the different Option, even if they look the same for me.

}
)

//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)
}
4 changes: 4 additions & 0 deletions tracker/gioui/preferences.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
window:
width: 800
height: 600
maximized: false
31 changes: 22 additions & 9 deletions tracker/gioui/tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/text"
"gioui.org/unit"
"gioui.org/widget/material"
"gioui.org/x/explorer"
"github.com/vsariola/sointu/tracker"
Expand Down Expand Up @@ -51,8 +50,9 @@ type (

filePathString tracker.String

quitWG sync.WaitGroup
execChan chan func()
quitWG sync.WaitGroup
execChan chan func()
preferences Preferences

*tracker.Model
}
Expand Down Expand Up @@ -89,9 +89,16 @@ func NewTracker(model *tracker.Model) *Tracker {
Model: model,

filePathString: model.FilePath().String(),
preferences: MakePreferences(),
}
t.Theme.Shaper = text.NewShaper(text.WithCollection(fontCollection))
t.PopupAlert = NewPopupAlert(model.Alerts(), t.Theme.Shaper)
if t.preferences.YmlError != nil {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine for now. In future, and even for the keybindings, I'm thinking of loading them every time a new tracker is created instead of init(). Because:

In the VST, init is called only once, when the DLL is loaded. But the DLL stays loaded even if you close the plugin. So, to make new config take effect, you have to close the entire DAW.

model.Alerts().Add(
fmt.Sprintf("Preferences YML Error: %s", t.preferences.YmlError),
tracker.Warning,
)
}
t.Theme.Palette.Fg = primaryColor
t.Theme.Palette.ContrastFg = black
t.TrackEditor.scrollTable.Focus()
Expand All @@ -101,9 +108,7 @@ func NewTracker(model *tracker.Model) *Tracker {

func (t *Tracker) Main() {
titleFooter := ""
w := new(app.Window)
w.Option(app.Title("Sointu Tracker"))
w.Option(app.Size(unit.Dp(800), unit.Dp(600)))
w := t.newWindow()
t.InstrumentEditor.Focus()
recoveryTicker := time.NewTicker(time.Second * 30)
t.Explorer = explorer.NewExplorer(w)
Expand All @@ -127,9 +132,7 @@ func (t *Tracker) Main() {
}
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
w = new(app.Window)
w.Option(app.Title("Sointu Tracker"))
w.Option(app.Size(unit.Dp(800), unit.Dp(600)))
w = t.newWindow()
t.Explorer = explorer.NewExplorer(w)
go eventLoop(w, events, acks)
}
Expand Down Expand Up @@ -165,6 +168,16 @@ func (t *Tracker) Main() {
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{}) {
// Iterate window events, sending each to the old event loop and waiting for
// a signal that processing is complete before iterating again.
Expand Down
Loading