Skip to content
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
47 changes: 46 additions & 1 deletion desktop/src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import (
//go:embed assets/*
var assets embed.FS

const trayDoubleClick = 400 * time.Millisecond
const (
trayDoubleClick = 400 * time.Millisecond
windowRestoreGrace = 1500 * time.Millisecond
)

func webviewAcceptanceArgs() []string {
args := []string{"--disable-gpu", "--disable-gpu-compositing"}
Expand Down Expand Up @@ -50,6 +53,8 @@ type App struct {
quitting bool
trayHidePending bool
trayHideGeneration uint64
restoreGeneration uint64
restoreUntil time.Time

trayClickMu sync.Mutex
lastTrayClick time.Time
Expand Down Expand Up @@ -333,6 +338,42 @@ func (a *App) showWindow() {
if a.window == nil {
return
}
generation := a.beginWindowRestore(time.Now())
a.restoreWindowOnce()
for _, delay := range []time.Duration{75 * time.Millisecond, 250 * time.Millisecond, 750 * time.Millisecond, 1250 * time.Millisecond} {
delay := delay
time.AfterFunc(delay, func() {
if !a.windowRestoreCurrent(generation, time.Now()) {
return
}
a.restoreWindowOnce()
})
}
}

func (a *App) beginWindowRestore(now time.Time) uint64 {
a.mu.Lock()
defer a.mu.Unlock()
a.trayHidePending = false
a.trayHideGeneration++
a.restoreGeneration++
a.restoreUntil = now.Add(windowRestoreGrace)
return a.restoreGeneration
}

func (a *App) windowRestoreCurrent(generation uint64, now time.Time) bool {
a.mu.Lock()
defer a.mu.Unlock()
return a.restoreGeneration == generation && now.Before(a.restoreUntil)
}

func (a *App) windowRestoreInProgress(now time.Time) bool {
a.mu.Lock()
defer a.mu.Unlock()
return now.Before(a.restoreUntil)
}

func (a *App) restoreWindowOnce() {
if a.window.IsMinimised() {
a.window.UnMinimise()
}
Expand Down Expand Up @@ -532,6 +573,10 @@ func main() {
})
win.OnWindowEvent(events.Common.WindowMinimise, func(_ *application.WindowEvent) {
log.Printf("WindowMinimise: minimised=%v", api.window.IsMinimised())
if api.windowRestoreInProgress(time.Now()) {
log.Printf("WindowMinimise ignored while restoring the main window")
return
}
if api.store.get().MinimizeToTray && api.window.IsMinimised() {
api.hideToTray()
}
Expand Down
30 changes: 29 additions & 1 deletion desktop/src/tray_click_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package main

import "testing"
import (
"testing"
"time"
)

func TestDarwinTrayLeftClickShowsSettings(t *testing.T) {
if !trayLeftClickShowsSettings("darwin") {
Expand All @@ -25,3 +28,28 @@ func TestTrayHideClosingIsConsumedOnce(t *testing.T) {
t.Fatal("a later user close must not be mistaken for the tray-hide event")
}
}

func TestWindowRestoreIgnoresDelayedMinimiseEvent(t *testing.T) {
now := time.Now()
app := &App{trayHidePending: true, trayHideGeneration: 4}
generation := app.beginWindowRestore(now)

if app.trayHidePending {
t.Fatal("restoring must cancel a pending hide-to-tray close event")
}
if app.trayHideGeneration != 5 {
t.Fatal("restoring must invalidate the pending hide-to-tray timeout")
}
if !app.windowRestoreInProgress(now.Add(windowRestoreGrace - time.Millisecond)) {
t.Fatal("a delayed minimise event must be ignored during restore")
}
if app.windowRestoreInProgress(now.Add(windowRestoreGrace)) {
t.Fatal("a later user minimise must not be ignored")
}
if !app.windowRestoreCurrent(generation, now.Add(time.Second)) {
t.Fatal("scheduled restore attempts must remain active during the grace period")
}
if app.windowRestoreCurrent(generation+1, now.Add(time.Second)) {
t.Fatal("stale restore attempts must not affect a newer restore")
}
}
Loading