aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlawl <github@dumbinter.net>2021-03-20 13:49:25 +0100
committerlawl <github@dumbinter.net>2021-03-20 13:49:25 +0100
commit6f901716ed6394ed503a22eda8b8f9c3fe09e526 (patch)
tree38a1f94eeed172f4ba09e6cbabe8e1eb2befc56c
parentfe14585a37dbbdd8926a7d074c3d8d0c8143276f (diff)
downloadnoisetorch-6f901716ed6394ed503a22eda8b8f9c3fe09e526.tar.gz
noisetorch-6f901716ed6394ed503a22eda8b8f9c3fe09e526.zip
Refactor UI: Use a stack of views to handle what to display
Previously we dispatched calls to different drawing function with a bunch of if statements in the main draw function. Use a stack of drawing functions instead.
-rw-r--r--main.go12
-rw-r--r--ui.go91
-rw-r--r--views.go50
3 files changed, 92 insertions, 61 deletions
diff --git a/main.go b/main.go
index e75b7b2..c195765 100644
--- a/main.go
+++ b/main.go
@@ -209,16 +209,20 @@ func main() {
go updateCheck(&ctx)
}
- go paConnectionWatchdog(&ctx)
-
ctx.haveCapabilities = hasCapSysResource(getCurrentCaps())
ctx.capsMismatch = hasCapSysResource(getCurrentCaps()) != hasCapSysResource(getSelfFileCaps())
+ resetUI(&ctx)
+
wnd := nucular.NewMasterWindowSize(0, appName, image.Point{600, 400}, func(w *nucular.Window) {
updatefn(&ctx, w)
})
ctx.masterWindow = &wnd
+ (*ctx.masterWindow).Changed()
+
+ go paConnectionWatchdog(&ctx)
+
style := style.FromTheme(style.DarkTheme, 2.0)
style.Font = font.DefaultFont(16, 1)
wnd.SetStyle(style)
@@ -315,6 +319,9 @@ func paConnectionWatchdog(ctx *ntcontext) {
continue
}
+ ctx.views.Push(connectScreen)
+ (*ctx.masterWindow).Changed()
+
paClient, err := pulseaudio.NewClient()
if err != nil {
log.Printf("Couldn't create pulseaudio client: %v\n", err)
@@ -328,6 +335,7 @@ func paConnectionWatchdog(ctx *ntcontext) {
ctx.outputList = preselectDevice(ctx, getSinks(paClient), ctx.config.LastUsedOutput, getDefaultSinkID)
resetUI(ctx)
+ (*ctx.masterWindow).Changed()
time.Sleep(500 * time.Millisecond)
}
diff --git a/ui.go b/ui.go
index 7792c3a..a516ce4 100644
--- a/ui.go
+++ b/ui.go
@@ -26,16 +26,13 @@ type ntcontext struct {
librnnoise string
sourceListColdWidthIndex int
config *config
- loadingScreen bool
- licenseScreen bool
- versionScreen bool
licenseTextArea nucular.TextEditor
masterWindow *nucular.MasterWindow
update updateui
reloadRequired bool
haveCapabilities bool
- errorMsg string
capsMismatch bool
+ views *ViewStack
}
var green = color.RGBA{34, 187, 69, 255}
@@ -45,38 +42,11 @@ var orange = color.RGBA{255, 140, 0, 255}
var patreonImg *image.RGBA
func updatefn(ctx *ntcontext, w *nucular.Window) {
+ currView := ctx.views.Peek()
+ currView(ctx, w)
+}
- //TODO: this is disgusting
-
- if ctx.errorMsg != "" {
- errorScreen(ctx, w)
- return
- }
-
- if !ctx.haveCapabilities {
- capabilitiesScreen(ctx, w)
- return
- }
-
- if !ctx.paClient.Connected() {
- connectScreen(ctx, w)
- return
- }
-
- if ctx.loadingScreen {
- loadingScreen(ctx, w)
- return
- }
-
- if ctx.licenseScreen {
- licenseScreen(ctx, w)
- return
- }
-
- if ctx.versionScreen {
- versionScreen(ctx, w)
- return
- }
+func mainScreen(ctx *ntcontext, w *nucular.Window) {
w.MenubarBegin()
@@ -84,14 +54,14 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
if w := w.Menu(label.TA("About", "LC"), 120, nil); w != nil {
w.Row(10).Dynamic(1)
if w.MenuItem(label.T("Licenses")) {
- ctx.licenseScreen = true
+ ctx.views.Push(licenseScreen)
}
w.Row(10).Dynamic(1)
if w.MenuItem(label.T("Source code")) {
exec.Command("xdg-open", "https://github.com/lawl/NoiseTorch").Run()
}
if w.MenuItem(label.T("Version")) {
- ctx.versionScreen = true
+ ctx.views.Push(versionScreen)
}
}
@@ -248,7 +218,7 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
w.Row(25).Dynamic(2)
if ctx.noiseSupressorState != unloaded {
if w.ButtonText("Unload NoiseTorch") {
- ctx.loadingScreen = true
+ ctx.views.Push(loadingScreen)
ctx.reloadRequired = false
go func() { // don't block the UI thread, just display a working screen so user can't run multiple loads/unloads
if err := unloadSupressor(ctx); err != nil {
@@ -260,7 +230,7 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
time.Sleep(time.Millisecond * 500)
}
}
- ctx.loadingScreen = false
+ ctx.views.Pop()
(*ctx.masterWindow).Changed()
}()
}
@@ -280,7 +250,7 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
((ctx.config.FilterOutput && ctx.config.GuiltTripped) || !ctx.config.FilterOutput) &&
ctx.noiseSupressorState != inconsistent {
if w.ButtonText(txt) {
- ctx.loadingScreen = true
+ ctx.views.Push(loadingScreen)
ctx.reloadRequired = false
go func() { // don't block the UI thread, just display a working screen so user can't run multiple loads/unloads
if ctx.noiseSupressorState == loaded {
@@ -301,7 +271,7 @@ func updatefn(ctx *ntcontext, w *nucular.Window) {
ctx.config.LastUsedInput = inp.ID
ctx.config.LastUsedOutput = out.ID
go writeConfig(ctx.config)
- ctx.loadingScreen = false
+ ctx.views.Pop()
(*ctx.masterWindow).Changed()
}()
}
@@ -367,7 +337,7 @@ func licenseScreen(ctx *ntcontext, w *nucular.Window) {
w.Row(20).Dynamic(2)
w.Spacing(1)
if w.ButtonText("OK") {
- ctx.licenseScreen = false
+ ctx.views.Pop()
}
}
@@ -381,7 +351,7 @@ func versionScreen(ctx *ntcontext, w *nucular.Window) {
w.Row(20).Dynamic(2)
w.Spacing(1)
if w.ButtonText("OK") {
- ctx.versionScreen = false
+ ctx.views.Pop()
}
}
@@ -406,40 +376,43 @@ func capabilitiesScreen(ctx *ntcontext, w *nucular.Window) {
if w.ButtonText("Grant capability (requires root)") {
err := pkexecSetcapSelf()
if err != nil {
- ctx.errorMsg = err.Error()
+ ctx.views.Push(makeErrorScreen(ctx, w, err.Error()))
return
}
self, err := os.Executable()
if err != nil {
- ctx.errorMsg = err.Error()
+ ctx.views.Push(makeErrorScreen(ctx, w, err.Error()))
return
}
err = syscall.Exec(self, []string{""}, os.Environ())
if err != nil {
- ctx.errorMsg = err.Error()
+ ctx.views.Push(makeErrorScreen(ctx, w, err.Error()))
return
}
}
}
-func errorScreen(ctx *ntcontext, w *nucular.Window) {
- w.Row(15).Dynamic(1)
- w.Label("Error", "CB")
- w.Row(15).Dynamic(1)
- w.Label(ctx.errorMsg, "CB")
- w.Row(40).Dynamic(1)
- w.Row(25).Dynamic(1)
- if w.ButtonText("OK") {
- ctx.errorMsg = ""
- return
+func makeErrorScreen(ctx *ntcontext, w *nucular.Window, errorMsg string) func(ctx *ntcontext, w *nucular.Window) {
+ return func(ctx *ntcontext, w *nucular.Window) {
+ w.Row(15).Dynamic(1)
+ w.Label("Error", "CB")
+ w.Row(15).Dynamic(1)
+ w.Label(errorMsg, "CB")
+ w.Row(40).Dynamic(1)
+ w.Row(25).Dynamic(1)
+ if w.ButtonText("OK") {
+ ctx.views.Pop()
+ return
+ }
}
}
func resetUI(ctx *ntcontext) {
- ctx.loadingScreen = false
+ ctx.views = NewViewStack()
+ ctx.views.Push(mainScreen)
- if ctx.masterWindow != nil {
- (*ctx.masterWindow).Changed()
+ if !ctx.haveCapabilities {
+ ctx.views.Push(capabilitiesScreen)
}
}
diff --git a/views.go b/views.go
new file mode 100644
index 0000000..0e3d95e
--- /dev/null
+++ b/views.go
@@ -0,0 +1,50 @@
+package main
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/aarzilli/nucular"
+)
+
+type ViewFunc func(ctx *ntcontext, w *nucular.Window)
+
+type ViewStack struct {
+ stack [100]ViewFunc
+ sp int8
+ mu sync.Mutex
+}
+
+func NewViewStack() *ViewStack {
+ return &ViewStack{sp: -1}
+}
+
+func (v *ViewStack) Push(f ViewFunc) {
+ v.mu.Lock()
+ defer v.mu.Unlock()
+
+ v.stack[v.sp+1] = f
+ v.sp++
+}
+
+func (v *ViewStack) Pop() (ViewFunc, error) {
+ v.mu.Lock()
+
+ if v.sp <= 0 {
+ return nil, fmt.Errorf("Cannot pop root element from ViewStack")
+ }
+
+ defer (func() {
+ v.stack[v.sp] = nil
+ v.sp--
+ v.mu.Unlock()
+ })()
+
+ return v.stack[v.sp], nil
+}
+
+func (v *ViewStack) Peek() ViewFunc {
+ v.mu.Lock()
+ defer v.mu.Unlock()
+ return v.stack[v.sp]
+}