Files
gosentry/src/ui/run.go
T
mixeme f533739d6e P5: per-platform transparent, size-appropriate window/tray icons
Give the window titlebar, taskbar, and tray a size-appropriate icon with a
transparent boundary on both Windows and Linux, instead of scaling one PNG to
every size.

Assets:
- Regenerate the PNGs and gosentry.ico with feathered color-to-alpha so the
  rounded-tile boundary is transparent; a binary white-key had left an opaque
  halo that read as a border on dark taskbars/trays.
- Rebuild gosentry.ico as multi-size (16 hand-tuned + 32/48/256 from big) and
  add a single-frame 16x16 gosentry-icon-16x16.ico for the Windows tray.
- assets.go: add IconSmall() and IconSmallICO().

Wiring:
- Windows window/taskbar: embed gosentry.ico under the GLFW_ICON resource and
  skip a.SetIcon so GLFW selects the right frame per size (hand-tuned 16 for the
  titlebar, a larger frame for the taskbar).
- Windows tray: SetSystemTrayIcon(IconSmallICO()), a 16x16 ICO frame.
- Linux window titlebar: a.SetIcon(IconSmall()) for a crisp ~16px _NET_WM_ICON;
  tray uses the big PNG since StatusNotifierItem renders larger.

Also bump Fyne 2.6.3 -> 2.7.4 (systray 1.11.0 -> 1.12.1) and document the full
cross-platform icon strategy in assets.go, gosentry.rc, run.go, and tray.go.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 22:09:04 +03:00

71 lines
2.6 KiB
Go

package ui
import (
"runtime"
"time"
"gitea.mixdep.ru/mix/gosentry/assets"
"gitea.mixdep.ru/mix/gosentry/src/app"
"fyne.io/fyne/v2"
fyneapp "fyne.io/fyne/v2/app"
)
const appID = "ru.mixdep.gosentry.desktop"
// Run is the application entry point. It owns the process lifecycle — single
// instance arbitration, Fyne app + window construction, tray wiring, and the
// startup-timing record — and delegates all view construction to newMainView in
// mainwindow.go. Keeping lifecycle here and the view there is the run.go /
// mainwindow.go split introduced in T4.1.
func Run(startInTray bool) {
started := time.Now()
instanceListener, primary := acquireSingleInstance(!startInTray)
if !primary {
return
}
if instanceListener != nil {
defer instanceListener.Close()
}
// A stable app ID lets Fyne persist desktop preferences consistently across
// launches and gives tray/window integration a predictable identity.
a := fyneapp.NewWithID(appID)
// On Windows the multi-resolution gosentry.ico (embedded under the GLFW_ICON
// resource name) drives the window: GLFW picks the hand-tuned 16x16 for the
// titlebar and the large artwork for the bigger taskbar icon — size-appropriate
// in a way a single Fyne SetIcon resource cannot be, since one PNG would be
// scaled to both sizes.
//
// Other platforms have no PE icon. Fyne's single SetIcon resource feeds
// _NET_WM_ICON, which the window manager renders small (~16px) in the titlebar,
// so use the hand-tuned small icon there to keep it crisp. The larger
// dock/launcher icon comes from the .desktop entry installed by
// InstallDesktopIcon, which uses the big artwork.
if runtime.GOOS != "windows" {
a.SetIcon(assets.IconSmall())
}
w := a.NewWindow("GoSentry " + app.Version)
configureSystemTray(a, w)
w.Resize(fyne.NewSize(1120, 720))
content, recordStartup := newMainView(w)
w.SetContent(content)
serveSingleInstance(instanceListener, w)
if startInTray {
// Autostart launches intentionally stay hidden, so "window shown" would be
// a misleading metric. Record a separate startup event for the tray path
// instead of forcing one timing definition onto two different UX flows.
recordStartup(time.Since(started), false)
a.Run()
return
}
// Show the window before recording startup time. Measuring earlier, during
// widget construction, looked cheaper in History than the user-perceived
// startup really was. The current point is less abstract: it ends when the
// window has actually been handed to the desktop for display.
w.Show()
recordStartup(time.Since(started), true)
a.Run()
}