381 lines
8.3 KiB
Go
381 lines
8.3 KiB
Go
package application
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/wailsapp/wails/v3/pkg/events"
|
|
)
|
|
|
|
// Context mirrors the callback context type exposed by Wails.
|
|
type Context struct{}
|
|
|
|
// Logger is a minimal logger surface used by the GUI packages.
|
|
type Logger struct{}
|
|
|
|
func (l Logger) Info(message string, args ...any) {}
|
|
|
|
// RGBA stores a colour with alpha.
|
|
type RGBA struct {
|
|
Red, Green, Blue, Alpha uint8
|
|
}
|
|
|
|
// NewRGBA constructs an RGBA value.
|
|
func NewRGBA(red, green, blue, alpha uint8) RGBA {
|
|
return RGBA{Red: red, Green: green, Blue: blue, Alpha: alpha}
|
|
}
|
|
|
|
// MenuRole identifies a platform menu role.
|
|
type MenuRole int
|
|
|
|
const (
|
|
AppMenu MenuRole = iota
|
|
FileMenu
|
|
EditMenu
|
|
ViewMenu
|
|
WindowMenu
|
|
HelpMenu
|
|
)
|
|
|
|
// MenuItem is a minimal menu item implementation.
|
|
type MenuItem struct {
|
|
Label string
|
|
Accelerator string
|
|
Tooltip string
|
|
Checked bool
|
|
Enabled bool
|
|
onClick func(*Context)
|
|
}
|
|
|
|
func (mi *MenuItem) SetAccelerator(accel string) { mi.Accelerator = accel }
|
|
func (mi *MenuItem) SetTooltip(text string) { mi.Tooltip = text }
|
|
func (mi *MenuItem) SetChecked(checked bool) { mi.Checked = checked }
|
|
func (mi *MenuItem) SetEnabled(enabled bool) { mi.Enabled = enabled }
|
|
func (mi *MenuItem) OnClick(fn func(*Context)) { mi.onClick = fn }
|
|
|
|
// Menu is a minimal menu tree used by the GUI wrappers.
|
|
type Menu struct {
|
|
Items []*MenuItem
|
|
}
|
|
|
|
func NewMenu() *Menu { return &Menu{} }
|
|
|
|
func (m *Menu) Add(label string) *MenuItem {
|
|
item := &MenuItem{Label: label, Enabled: true}
|
|
m.Items = append(m.Items, item)
|
|
return item
|
|
}
|
|
|
|
func (m *Menu) AddSeparator() {
|
|
m.Items = append(m.Items, &MenuItem{Label: "---"})
|
|
}
|
|
|
|
func (m *Menu) AddSubmenu(label string) *Menu {
|
|
submenu := &Menu{}
|
|
m.Items = append(m.Items, &MenuItem{Label: label})
|
|
return submenu
|
|
}
|
|
|
|
func (m *Menu) AddRole(role MenuRole) {
|
|
m.Items = append(m.Items, &MenuItem{Label: role.String(), Enabled: true})
|
|
}
|
|
|
|
func (role MenuRole) String() string {
|
|
switch role {
|
|
case AppMenu:
|
|
return "app"
|
|
case FileMenu:
|
|
return "file"
|
|
case EditMenu:
|
|
return "edit"
|
|
case ViewMenu:
|
|
return "view"
|
|
case WindowMenu:
|
|
return "window"
|
|
case HelpMenu:
|
|
return "help"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// MenuManager owns the application menu.
|
|
type MenuManager struct {
|
|
applicationMenu *Menu
|
|
}
|
|
|
|
func (m *MenuManager) SetApplicationMenu(menu *Menu) { m.applicationMenu = menu }
|
|
|
|
// SystemTray represents a tray instance.
|
|
type SystemTray struct {
|
|
icon []byte
|
|
templateIcon []byte
|
|
tooltip string
|
|
label string
|
|
menu *Menu
|
|
attachedWindow *WebviewWindow
|
|
}
|
|
|
|
func (t *SystemTray) SetIcon(data []byte) { t.icon = append([]byte(nil), data...) }
|
|
func (t *SystemTray) SetTemplateIcon(data []byte) { t.templateIcon = append([]byte(nil), data...) }
|
|
func (t *SystemTray) SetTooltip(text string) { t.tooltip = text }
|
|
func (t *SystemTray) SetLabel(text string) { t.label = text }
|
|
func (t *SystemTray) SetMenu(menu *Menu) { t.menu = menu }
|
|
func (t *SystemTray) AttachWindow(w *WebviewWindow) {
|
|
t.attachedWindow = w
|
|
}
|
|
|
|
// SystemTrayManager creates tray instances.
|
|
type SystemTrayManager struct{}
|
|
|
|
func (m *SystemTrayManager) New() *SystemTray { return &SystemTray{} }
|
|
|
|
// WindowEventContext carries drag-and-drop details for a window event.
|
|
type WindowEventContext struct {
|
|
droppedFiles []string
|
|
dropDetails *DropTargetDetails
|
|
}
|
|
|
|
func (c *WindowEventContext) DroppedFiles() []string {
|
|
return append([]string(nil), c.droppedFiles...)
|
|
}
|
|
|
|
func (c *WindowEventContext) DropTargetDetails() *DropTargetDetails {
|
|
if c.dropDetails == nil {
|
|
return nil
|
|
}
|
|
details := *c.dropDetails
|
|
return &details
|
|
}
|
|
|
|
// DropTargetDetails mirrors the fields consumed by the GUI wrappers.
|
|
type DropTargetDetails struct {
|
|
ElementID string
|
|
}
|
|
|
|
// WindowEvent mirrors the event object passed to window callbacks.
|
|
type WindowEvent struct {
|
|
ctx *WindowEventContext
|
|
}
|
|
|
|
func (e *WindowEvent) Context() *WindowEventContext {
|
|
if e.ctx == nil {
|
|
e.ctx = &WindowEventContext{}
|
|
}
|
|
return e.ctx
|
|
}
|
|
|
|
// WebviewWindowOptions configures a window instance.
|
|
type WebviewWindowOptions struct {
|
|
Name string
|
|
Title string
|
|
URL string
|
|
Width, Height int
|
|
X, Y int
|
|
MinWidth, MinHeight int
|
|
MaxWidth, MaxHeight int
|
|
Frameless bool
|
|
Hidden bool
|
|
AlwaysOnTop bool
|
|
DisableResize bool
|
|
EnableFileDrop bool
|
|
BackgroundColour RGBA
|
|
}
|
|
|
|
// WebviewWindow is a lightweight, in-memory window implementation.
|
|
type WebviewWindow struct {
|
|
mu sync.RWMutex
|
|
opts WebviewWindowOptions
|
|
title string
|
|
x, y int
|
|
width, height int
|
|
maximised bool
|
|
minimised bool
|
|
focused bool
|
|
visible bool
|
|
alwaysOnTop bool
|
|
fullscreen bool
|
|
closed bool
|
|
eventHandlers map[events.WindowEventType][]func(*WindowEvent)
|
|
}
|
|
|
|
func newWebviewWindow(options WebviewWindowOptions) *WebviewWindow {
|
|
return &WebviewWindow{
|
|
opts: options,
|
|
title: options.Title,
|
|
x: options.X,
|
|
y: options.Y,
|
|
width: options.Width,
|
|
height: options.Height,
|
|
visible: !options.Hidden,
|
|
alwaysOnTop: options.AlwaysOnTop,
|
|
eventHandlers: make(map[events.WindowEventType][]func(*WindowEvent)),
|
|
}
|
|
}
|
|
|
|
func (w *WebviewWindow) Name() string { return w.opts.Name }
|
|
func (w *WebviewWindow) Title() string {
|
|
w.mu.RLock()
|
|
defer w.mu.RUnlock()
|
|
return w.title
|
|
}
|
|
func (w *WebviewWindow) Position() (int, int) {
|
|
w.mu.RLock()
|
|
defer w.mu.RUnlock()
|
|
return w.x, w.y
|
|
}
|
|
func (w *WebviewWindow) Size() (int, int) {
|
|
w.mu.RLock()
|
|
defer w.mu.RUnlock()
|
|
return w.width, w.height
|
|
}
|
|
func (w *WebviewWindow) IsMaximised() bool {
|
|
w.mu.RLock()
|
|
defer w.mu.RUnlock()
|
|
return w.maximised
|
|
}
|
|
func (w *WebviewWindow) IsMinimised() bool {
|
|
w.mu.RLock()
|
|
defer w.mu.RUnlock()
|
|
return w.minimised
|
|
}
|
|
func (w *WebviewWindow) IsVisible() bool {
|
|
w.mu.RLock()
|
|
defer w.mu.RUnlock()
|
|
return w.visible
|
|
}
|
|
func (w *WebviewWindow) IsFocused() bool {
|
|
w.mu.RLock()
|
|
defer w.mu.RUnlock()
|
|
return w.focused
|
|
}
|
|
|
|
func (w *WebviewWindow) SetTitle(title string) {
|
|
w.mu.Lock()
|
|
w.title = title
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) SetPosition(x, y int) {
|
|
w.mu.Lock()
|
|
w.x = x
|
|
w.y = y
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) SetSize(width, height int) {
|
|
w.mu.Lock()
|
|
w.width = width
|
|
w.height = height
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) SetBackgroundColour(colour RGBA) {}
|
|
|
|
func (w *WebviewWindow) SetAlwaysOnTop(alwaysOnTop bool) {
|
|
w.mu.Lock()
|
|
w.alwaysOnTop = alwaysOnTop
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) Maximise() {
|
|
w.mu.Lock()
|
|
w.maximised = true
|
|
w.minimised = false
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) Restore() {
|
|
w.mu.Lock()
|
|
w.maximised = false
|
|
w.minimised = false
|
|
w.fullscreen = false
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) Minimise() {
|
|
w.mu.Lock()
|
|
w.maximised = false
|
|
w.minimised = true
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) Focus() {
|
|
w.mu.Lock()
|
|
w.focused = true
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) Close() {
|
|
w.mu.Lock()
|
|
w.closed = true
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) Show() {
|
|
w.mu.Lock()
|
|
w.visible = true
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) Hide() {
|
|
w.mu.Lock()
|
|
w.visible = false
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) Fullscreen() {
|
|
w.mu.Lock()
|
|
w.fullscreen = true
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) UnFullscreen() {
|
|
w.mu.Lock()
|
|
w.fullscreen = false
|
|
w.mu.Unlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) OnWindowEvent(eventType events.WindowEventType, callback func(event *WindowEvent)) func() {
|
|
w.mu.Lock()
|
|
w.eventHandlers[eventType] = append(w.eventHandlers[eventType], callback)
|
|
w.mu.Unlock()
|
|
return func() {}
|
|
}
|
|
|
|
// WindowManager manages in-memory windows.
|
|
type WindowManager struct {
|
|
mu sync.RWMutex
|
|
windows []*WebviewWindow
|
|
}
|
|
|
|
func (wm *WindowManager) NewWithOptions(options WebviewWindowOptions) *WebviewWindow {
|
|
window := newWebviewWindow(options)
|
|
wm.mu.Lock()
|
|
wm.windows = append(wm.windows, window)
|
|
wm.mu.Unlock()
|
|
return window
|
|
}
|
|
|
|
func (wm *WindowManager) GetAll() []any {
|
|
wm.mu.RLock()
|
|
defer wm.mu.RUnlock()
|
|
out := make([]any, 0, len(wm.windows))
|
|
for _, window := range wm.windows {
|
|
out = append(out, window)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// App is the top-level application object used by the GUI packages.
|
|
type App struct {
|
|
Logger Logger
|
|
Window WindowManager
|
|
Menu MenuManager
|
|
SystemTray SystemTrayManager
|
|
}
|
|
|
|
func (a *App) Quit() {}
|
|
|
|
func (a *App) NewMenu() *Menu {
|
|
return NewMenu()
|
|
}
|