Files

1204 lines
28 KiB
Go
Raw Permalink Normal View History

2023-01-18 21:42:49 +11:00
package application
import (
2023-09-28 11:38:59 -05:00
"encoding/json"
"errors"
2023-01-18 21:42:49 +11:00
"fmt"
"runtime"
"strings"
2023-01-18 21:42:49 +11:00
"sync"
2023-09-11 17:08:19 -05:00
2023-12-06 21:55:04 +01:00
"github.com/leaanthony/u"
2023-09-11 17:08:19 -05:00
"github.com/samber/lo"
"github.com/wailsapp/wails/v3/pkg/events"
2023-01-18 21:42:49 +11:00
)
2023-09-30 15:23:56 +10:00
// Enabled means the feature should be enabled
var Enabled = u.True
// Disabled means the feature should be disabled
var Disabled = u.False
2024-04-25 15:21:38 +10:00
// LRTB is a struct that holds Left, Right, Top, Bottom values
type LRTB struct {
Left int
Right int
Top int
Bottom int
}
2023-01-18 21:42:49 +11:00
type (
webviewWindowImpl interface {
setTitle(title string)
setSize(width, height int)
setAlwaysOnTop(alwaysOnTop bool)
setURL(url string)
setResizable(resizable bool)
setMinSize(width, height int)
setMaxSize(width, height int)
execJS(js string)
setBackgroundColour(color RGBA)
2023-01-18 21:42:49 +11:00
run()
center()
size() (int, int)
width() int
height() int
relativePosition() (int, int)
2023-01-18 21:42:49 +11:00
destroy()
reload()
forceReload()
2024-02-09 20:06:38 +11:00
openDevTools()
2023-02-06 20:50:11 +11:00
zoomReset()
2023-01-18 21:42:49 +11:00
zoomIn()
zoomOut()
2023-02-06 20:50:11 +11:00
getZoom() float64
setZoom(zoom float64)
2023-01-18 21:42:49 +11:00
close()
zoom()
setHTML(html string)
setRelativePosition(x int, y int)
2023-01-18 21:42:49 +11:00
on(eventID uint)
minimise()
unminimise()
maximise()
unmaximise()
fullscreen()
unfullscreen()
isMinimised() bool
isMaximised() bool
isFullscreen() bool
isNormal() bool
isVisible() bool
2023-09-13 08:57:09 +10:00
isFocused() bool
2023-01-18 21:42:49 +11:00
setFullscreenButtonEnabled(enabled bool)
2024-02-09 21:31:41 +11:00
setMinimiseButtonEnabled(enabled bool)
setMaximiseButtonEnabled(enabled bool)
focus()
2023-01-18 21:42:49 +11:00
show()
hide()
getScreen() (*Screen, error)
setFrameless(bool)
2023-02-12 13:36:58 +11:00
openContextMenu(menu *Menu, data *ContextMenuData)
nativeWindowHandle() uintptr
2023-06-13 17:44:07 +10:00
startDrag() error
startResize(border string) error
2023-06-18 17:12:49 +10:00
print() error
setEnabled(enabled bool)
absolutePosition() (int, int)
setAbsolutePosition(x int, y int)
2023-07-11 20:10:18 +10:00
flash(enabled bool)
2023-09-20 20:35:35 +10:00
handleKeyEvent(acceleratorString string)
2024-04-25 15:21:38 +10:00
getBorderSizes() *LRTB
2023-01-18 21:42:49 +11:00
}
)
2023-07-12 20:31:13 +10:00
type WindowEvent struct {
ctx *WindowEventContext
2023-07-12 20:31:13 +10:00
Cancelled bool
}
func (w *WindowEvent) Context() *WindowEventContext {
return w.ctx
}
func NewWindowEvent() *WindowEvent {
return &WindowEvent{}
}
2023-07-12 20:31:13 +10:00
func (w *WindowEvent) Cancel() {
w.Cancelled = true
}
type WindowEventListener struct {
callback func(event *WindowEvent)
2023-07-12 20:31:13 +10:00
}
2023-01-18 21:42:49 +11:00
type WebviewWindow struct {
2023-06-21 19:37:39 +10:00
options WebviewWindowOptions
impl webviewWindowImpl
id uint
2023-01-18 21:42:49 +11:00
eventListeners map[uint][]*WindowEventListener
2023-01-18 21:42:49 +11:00
eventListenersLock sync.RWMutex
eventHooks map[uint][]*WindowEventListener
2023-07-12 20:31:13 +10:00
eventHooksLock sync.RWMutex
2023-02-12 13:36:58 +11:00
contextMenus map[string]*Menu
contextMenusLock sync.RWMutex
// A map of listener cancellation functions
cancellersLock sync.RWMutex
cancellers []func()
2023-09-17 20:48:30 +10:00
// keyBindings holds the keybindings for the window
keyBindings map[string]func(window *WebviewWindow)
2024-01-09 07:45:56 +11:00
// Indicates that the window is destroyed
destroyed bool
destroyedLock sync.RWMutex
// Flags for managing the runtime
// runtimeLoaded indicates that the runtime has been loaded
runtimeLoaded bool
// pendingJS holds JS that was sent to the window before the runtime was loaded
pendingJS []string
2023-01-18 21:42:49 +11:00
}
var windowID uint
var windowIDLock sync.RWMutex
func getWindowID() uint {
windowIDLock.Lock()
defer windowIDLock.Unlock()
windowID++
return windowID
}
2023-09-28 11:38:59 -05:00
// FIXME: This should like be an interface method (TDM)
// Use onApplicationEvent to register a callback for an application event from a window.
// This will handle tidying up the callback when the window is destroyed
2023-07-12 20:31:13 +10:00
func (w *WebviewWindow) onApplicationEvent(eventType events.ApplicationEventType, callback func(*Event)) {
cancelFn := globalApplication.On(eventType, callback)
w.addCancellationFunction(cancelFn)
}
2024-01-09 07:45:56 +11:00
func (w *WebviewWindow) markAsDestroyed() {
w.destroyedLock.Lock()
defer w.destroyedLock.Unlock()
w.destroyed = true
}
func (w *WebviewWindow) setupEventMapping() {
var mapping map[events.WindowEventType]events.WindowEventType
switch runtime.GOOS {
case "darwin":
mapping = w.options.Mac.EventMapping
case "windows":
mapping = w.options.Windows.EventMapping
case "linux":
// TBD
}
if mapping == nil {
mapping = events.DefaultWindowEventMapping()
}
for source, target := range mapping {
source := source
target := target
w.On(source, func(event *WindowEvent) {
w.emit(target)
})
}
}
2023-09-28 11:38:59 -05:00
// NewWindow creates a new window with the given options
func NewWindow(options WebviewWindowOptions) *WebviewWindow {
2023-01-18 21:42:49 +11:00
if options.Width == 0 {
options.Width = 800
}
if options.Height == 0 {
options.Height = 600
}
if options.URL == "" {
options.URL = "/"
}
2023-01-18 21:42:49 +11:00
result := &WebviewWindow{
2023-01-18 21:42:49 +11:00
id: getWindowID(),
options: options,
eventListeners: make(map[uint][]*WindowEventListener),
2023-02-12 13:36:58 +11:00
contextMenus: make(map[string]*Menu),
eventHooks: make(map[uint][]*WindowEventListener),
2023-01-18 21:42:49 +11:00
}
result.setupEventMapping()
// Listen for window closing events and de
result.On(events.Common.WindowClosing, func(event *WindowEvent) {
shouldClose := true
if result.options.ShouldClose != nil {
shouldClose = result.options.ShouldClose(result)
}
if shouldClose {
globalApplication.deleteWindowByID(result.id)
2023-09-24 08:57:22 +10:00
InvokeSync(result.impl.close)
}
})
2023-09-17 20:48:30 +10:00
// Process keybindings
if result.options.KeyBindings != nil {
result.keyBindings = processKeyBindingOptions(result.options.KeyBindings)
}
return result
}
func processKeyBindingOptions(keyBindings map[string]func(window *WebviewWindow)) map[string]func(window *WebviewWindow) {
result := make(map[string]func(window *WebviewWindow))
for key, callback := range keyBindings {
// Parse the key to an accelerator
acc, err := parseAccelerator(key)
if err != nil {
globalApplication.error("Invalid keybinding: %s", err.Error())
continue
}
result[acc.String()] = callback
2023-10-15 13:53:31 +11:00
globalApplication.debug("Added Keybinding", "accelerator", acc.String())
2023-09-17 20:48:30 +10:00
}
return result
2023-01-18 21:42:49 +11:00
}
func (w *WebviewWindow) addCancellationFunction(canceller func()) {
w.cancellersLock.Lock()
defer w.cancellersLock.Unlock()
w.cancellers = append(w.cancellers, canceller)
}
2023-09-28 11:38:59 -05:00
// formatJS ensures the 'data' provided marshals to valid json or panics
func (w *WebviewWindow) formatJS(f string, callID string, data string) string {
j, err := json.Marshal(data)
if err != nil {
panic(err)
}
return fmt.Sprintf(f, callID, j)
}
func (w *WebviewWindow) CallError(callID string, result string) {
if w.impl != nil {
w.impl.execJS(w.formatJS("_wails.callErrorHandler('%s', %s);", callID, result))
2023-09-28 11:38:59 -05:00
}
}
func (w *WebviewWindow) CallResponse(callID string, result string) {
if w.impl != nil {
w.impl.execJS(w.formatJS("_wails.callResultHandler('%s', %s, true);", callID, result))
2023-09-28 11:38:59 -05:00
}
}
func (w *WebviewWindow) DialogError(dialogID string, result string) {
if w.impl != nil {
w.impl.execJS(w.formatJS("_wails.dialogErrorCallback('%s', %s);", dialogID, result))
}
}
func (w *WebviewWindow) DialogResponse(dialogID string, result string, isJSON bool) {
2023-09-28 11:38:59 -05:00
if w.impl != nil {
if isJSON {
w.impl.execJS(w.formatJS("_wails.dialogResultCallback('%s', %s, true);", dialogID, result))
} else {
w.impl.execJS(fmt.Sprintf("_wails.dialogResultCallback('%s', '%s', false);", dialogID, result))
}
2023-09-28 11:38:59 -05:00
}
}
func (w *WebviewWindow) ID() uint {
return w.id
}
// SetTitle sets the title of the window
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetTitle(title string) Window {
2023-01-18 21:42:49 +11:00
w.options.Title = title
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setTitle(title)
})
2023-01-18 21:42:49 +11:00
}
return w
}
// Name returns the name of the window
2023-02-06 20:50:11 +11:00
func (w *WebviewWindow) Name() string {
2023-04-07 19:47:01 +10:00
return w.options.Name
2023-02-06 20:50:11 +11:00
}
// SetSize sets the size of the window
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetSize(width, height int) Window {
2023-01-18 21:42:49 +11:00
// Don't set size if fullscreen
if w.IsFullscreen() {
return w
}
w.options.Width = width
w.options.Height = height
var newMaxWidth = w.options.MaxWidth
var newMaxHeight = w.options.MaxHeight
if width > w.options.MaxWidth && w.options.MaxWidth != 0 {
newMaxWidth = width
}
if height > w.options.MaxHeight && w.options.MaxHeight != 0 {
newMaxHeight = height
}
if newMaxWidth != 0 || newMaxHeight != 0 {
w.SetMaxSize(newMaxWidth, newMaxHeight)
}
var newMinWidth = w.options.MinWidth
var newMinHeight = w.options.MinHeight
if width < w.options.MinWidth && w.options.MinWidth != 0 {
newMinWidth = width
}
if height < w.options.MinHeight && w.options.MinHeight != 0 {
newMinHeight = height
}
if newMinWidth != 0 || newMinHeight != 0 {
w.SetMinSize(newMinWidth, newMinHeight)
}
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setSize(width, height)
})
2023-01-18 21:42:49 +11:00
}
return w
}
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) Run() {
2023-01-18 21:42:49 +11:00
if w.impl != nil {
return
}
w.impl = newWindowImpl(w)
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.run)
2023-01-18 21:42:49 +11:00
}
// SetAlwaysOnTop sets the window to be always on top.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetAlwaysOnTop(b bool) Window {
2023-01-18 21:42:49 +11:00
w.options.AlwaysOnTop = b
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setAlwaysOnTop(b)
})
2023-01-18 21:42:49 +11:00
}
return w
}
// Show shows the window.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) Show() Window {
if globalApplication.impl == nil {
return w
}
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-09-28 11:38:59 -05:00
InvokeSync(w.Run)
2023-01-18 21:42:49 +11:00
return w
}
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.show)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowShow)
2023-01-18 21:42:49 +11:00
return w
}
// Hide hides the window.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) Hide() Window {
2023-01-18 21:42:49 +11:00
w.options.Hidden = true
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.hide)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowHide)
2023-01-18 21:42:49 +11:00
}
return w
}
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetURL(s string) Window {
2023-01-18 21:42:49 +11:00
w.options.URL = s
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setURL(s)
})
2023-01-18 21:42:49 +11:00
}
return w
}
2024-04-25 15:21:38 +10:00
func (w *WebviewWindow) GetBorderSizes() *LRTB {
if w.impl != nil {
return InvokeSyncWithResult(w.impl.getBorderSizes)
}
return &LRTB{}
}
// SetZoom sets the zoom level of the window.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetZoom(magnification float64) Window {
2023-02-06 20:50:11 +11:00
w.options.Zoom = magnification
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setZoom(magnification)
})
2023-02-06 20:50:11 +11:00
}
return w
}
// GetZoom returns the current zoom level of the window.
2023-02-06 20:50:11 +11:00
func (w *WebviewWindow) GetZoom() float64 {
if w.impl != nil {
2023-09-24 08:57:22 +10:00
return InvokeSyncWithResult(w.impl.getZoom)
2023-02-06 20:50:11 +11:00
}
return 1
}
// SetResizable sets whether the window is resizable.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetResizable(b bool) Window {
2023-01-18 21:42:49 +11:00
w.options.DisableResize = !b
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setResizable(b)
})
2023-01-18 21:42:49 +11:00
}
return w
}
// Resizable returns true if the window is resizable.
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) Resizable() bool {
return !w.options.DisableResize
}
// SetMinSize sets the minimum size of the window.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetMinSize(minWidth, minHeight int) Window {
2023-01-18 21:42:49 +11:00
w.options.MinWidth = minWidth
w.options.MinHeight = minHeight
currentWidth, currentHeight := w.Size()
newWidth, newHeight := currentWidth, currentHeight
var newSize bool
if minHeight != 0 && currentHeight < minHeight {
newHeight = minHeight
w.options.Height = newHeight
newSize = true
}
if minWidth != 0 && currentWidth < minWidth {
newWidth = minWidth
w.options.Width = newWidth
newSize = true
}
if w.impl != nil {
if newSize {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setSize(newWidth, newHeight)
})
2023-01-18 21:42:49 +11:00
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setMinSize(minWidth, minHeight)
})
2023-01-18 21:42:49 +11:00
}
return w
}
// SetMaxSize sets the maximum size of the window.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetMaxSize(maxWidth, maxHeight int) Window {
2023-01-18 21:42:49 +11:00
w.options.MaxWidth = maxWidth
w.options.MaxHeight = maxHeight
currentWidth, currentHeight := w.Size()
newWidth, newHeight := currentWidth, currentHeight
var newSize bool
if maxHeight != 0 && currentHeight > maxHeight {
newHeight = maxHeight
w.options.Height = maxHeight
newSize = true
}
if maxWidth != 0 && currentWidth > maxWidth {
newWidth = maxWidth
w.options.Width = maxWidth
newSize = true
}
if w.impl != nil {
if newSize {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setSize(newWidth, newHeight)
})
2023-01-18 21:42:49 +11:00
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setMaxSize(maxWidth, maxHeight)
})
2023-01-18 21:42:49 +11:00
}
return w
}
// ExecJS executes the given javascript in the context of the window.
2024-03-14 08:57:10 +11:00
func (w *WebviewWindow) ExecJS(js string) {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
if w.runtimeLoaded {
w.impl.execJS(js)
} else {
w.pendingJS = append(w.pendingJS, js)
}
2023-01-18 21:42:49 +11:00
}
// Fullscreen sets the window to fullscreen mode. Min/Max size constraints are disabled.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) Fullscreen() Window {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-02-12 13:36:58 +11:00
w.options.StartState = WindowStateFullscreen
2023-01-18 21:42:49 +11:00
return w
}
if !w.IsFullscreen() {
2023-09-28 11:38:59 -05:00
w.DisableSizeConstraints()
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.fullscreen)
2023-01-18 21:42:49 +11:00
}
return w
}
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetFullscreenButtonEnabled(enabled bool) Window {
2023-01-18 21:42:49 +11:00
w.options.FullscreenButtonEnabled = enabled
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setFullscreenButtonEnabled(enabled)
})
2023-01-18 21:42:49 +11:00
}
return w
}
2024-02-09 21:31:41 +11:00
func (w *WebviewWindow) SetMinimiseButtonEnabled(enabled bool) Window {
w.options.FullscreenButtonEnabled = enabled
if w.impl != nil {
InvokeSync(func() {
w.impl.setMinimiseButtonEnabled(enabled)
})
}
return w
}
func (w *WebviewWindow) SetMaximiseButtonEnabled(enabled bool) Window {
w.options.FullscreenButtonEnabled = enabled
if w.impl != nil {
InvokeSync(func() {
w.impl.setMaximiseButtonEnabled(enabled)
})
}
return w
}
2023-10-09 20:56:19 +11:00
// Flash flashes the window's taskbar button/icon.
// Useful to indicate that attention is required. Windows only.
2023-07-11 20:10:18 +10:00
func (w *WebviewWindow) Flash(enabled bool) {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-07-11 20:10:18 +10:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
2023-07-11 20:10:18 +10:00
w.impl.flash(enabled)
})
}
2023-01-18 21:42:49 +11:00
// IsMinimised returns true if the window is minimised
func (w *WebviewWindow) IsMinimised() bool {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return false
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithResult(w.impl.isMinimised)
2023-01-18 21:42:49 +11:00
}
// IsVisible returns true if the window is visible
func (w *WebviewWindow) IsVisible() bool {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
return false
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithResult(w.impl.isVisible)
}
2023-01-18 21:42:49 +11:00
// IsMaximised returns true if the window is maximised
func (w *WebviewWindow) IsMaximised() bool {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return false
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithResult(w.impl.isMaximised)
2023-01-18 21:42:49 +11:00
}
// Size returns the size of the window
func (w *WebviewWindow) Size() (int, int) {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return 0, 0
}
var width, height int
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
width, height = w.impl.size()
})
return width, height
2023-01-18 21:42:49 +11:00
}
2023-09-13 08:57:09 +10:00
// IsFocused returns true if the window is currently focused
func (w *WebviewWindow) IsFocused() bool {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-09-13 08:57:09 +10:00
return false
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithResult(w.impl.isFocused)
2023-09-13 08:57:09 +10:00
}
2023-01-18 21:42:49 +11:00
// IsFullscreen returns true if the window is fullscreen
func (w *WebviewWindow) IsFullscreen() bool {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return false
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithResult(w.impl.isFullscreen)
2023-01-18 21:42:49 +11:00
}
// SetBackgroundColour sets the background colour of the window
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetBackgroundColour(colour RGBA) Window {
2023-01-18 21:42:49 +11:00
w.options.BackgroundColour = colour
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setBackgroundColour(colour)
})
2023-01-18 21:42:49 +11:00
}
return w
}
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) HandleMessage(message string) {
2023-01-18 21:42:49 +11:00
// Check for special messages
switch true {
case message == "drag":
2023-06-13 17:44:07 +10:00
if !w.IsFullscreen() {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
2023-06-13 17:44:07 +10:00
err := w.startDrag()
if err != nil {
2023-09-28 11:38:59 -05:00
w.Error("Failed to start drag: %s", err)
2023-06-13 17:44:07 +10:00
}
})
}
case strings.HasPrefix(message, "resize:"):
if !w.IsFullscreen() {
sl := strings.Split(message, ":")
if len(sl) != 2 {
2023-09-28 11:38:59 -05:00
w.Error("Unknown message returned from dispatcher: %+v", message)
return
}
err := w.startResize(sl[1])
if err != nil {
2023-09-28 11:38:59 -05:00
w.Error(err.Error())
}
}
case message == "wails:runtime:ready":
w.emit(events.Common.WindowRuntimeReady)
w.runtimeLoaded = true
2024-02-01 06:47:49 +11:00
w.SetResizable(!w.options.DisableResize)
for _, js := range w.pendingJS {
2024-03-14 08:57:10 +11:00
w.ExecJS(js)
}
}
2023-01-18 21:42:49 +11:00
}
func (w *WebviewWindow) startResize(border string) error {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
return nil
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithResult(func() error {
return w.impl.startResize(border)
})
}
// Center centers the window on the screen
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) Center() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
w.options.Centered = true
2023-01-18 21:42:49 +11:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.center)
2023-01-18 21:42:49 +11:00
}
// On registers a callback for the given window event
func (w *WebviewWindow) On(eventType events.WindowEventType, callback func(event *WindowEvent)) func() {
2023-01-18 21:42:49 +11:00
eventID := uint(eventType)
w.eventListenersLock.Lock()
defer w.eventListenersLock.Unlock()
windowEventListener := &WindowEventListener{
callback: callback,
}
w.eventListeners[eventID] = append(w.eventListeners[eventID], windowEventListener)
2023-01-18 21:42:49 +11:00
if w.impl != nil {
w.impl.on(eventID)
}
return func() {
w.eventListenersLock.Lock()
defer w.eventListenersLock.Unlock()
w.eventListeners[eventID] = lo.Without(w.eventListeners[eventID], windowEventListener)
}
2023-07-12 20:31:13 +10:00
}
2023-07-12 20:31:13 +10:00
// RegisterHook registers a hook for the given window event
func (w *WebviewWindow) RegisterHook(eventType events.WindowEventType, callback func(event *WindowEvent)) func() {
2023-07-12 20:31:13 +10:00
eventID := uint(eventType)
w.eventHooksLock.Lock()
defer w.eventHooksLock.Unlock()
windowEventHook := &WindowEventListener{
2023-07-12 20:31:13 +10:00
callback: callback,
}
w.eventHooks[eventID] = append(w.eventHooks[eventID], windowEventHook)
return func() {
w.eventHooksLock.Lock()
defer w.eventHooksLock.Unlock()
w.eventHooks[eventID] = lo.Without(w.eventHooks[eventID], windowEventHook)
}
2023-01-18 21:42:49 +11:00
}
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) HandleWindowEvent(id uint) {
2023-01-18 21:42:49 +11:00
w.eventListenersLock.RLock()
defer w.eventListenersLock.RUnlock()
2023-07-12 20:31:13 +10:00
// Get hooks
w.eventHooksLock.RLock()
hooks := w.eventHooks[id]
w.eventHooksLock.RUnlock()
// Create new WindowEvent
thisEvent := NewWindowEvent()
2023-07-12 20:31:13 +10:00
for _, thisHook := range hooks {
thisHook.callback(thisEvent)
if thisEvent.Cancelled {
return
}
}
for _, listener := range w.eventListeners[id] {
go listener.callback(thisEvent)
2023-01-18 21:42:49 +11:00
}
w.dispatchWindowEvent(id)
2023-01-18 21:42:49 +11:00
}
// Width returns the width of the window
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) Width() int {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return 0
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithResult(w.impl.width)
2023-01-18 21:42:49 +11:00
}
// Height returns the height of the window
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) Height() int {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return 0
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithResult(w.impl.height)
2023-01-18 21:42:49 +11:00
}
// RelativePosition returns the relative position of the window to the screen
func (w *WebviewWindow) RelativePosition() (int, int) {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return 0, 0
}
var x, y int
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
x, y = w.impl.relativePosition()
})
return x, y
2023-01-18 21:42:49 +11:00
}
// AbsolutePosition returns the absolute position of the window to the screen
func (w *WebviewWindow) AbsolutePosition() (int, int) {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
return 0, 0
}
var x, y int
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
x, y = w.impl.absolutePosition()
})
return x, y
}
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) Destroy() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2023-09-11 17:08:19 -05:00
// Cancel the callbacks
for _, cancelFunc := range w.cancellers {
cancelFunc()
}
2023-09-11 17:08:19 -05:00
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.destroy)
2023-01-18 21:42:49 +11:00
}
// Reload reloads the page assets
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) Reload() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.reload)
2023-01-18 21:42:49 +11:00
}
// ForceReload forces the window to reload the page assets
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) ForceReload() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.forceReload)
2023-01-18 21:42:49 +11:00
}
// ToggleFullscreen toggles the window between fullscreen and normal
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) ToggleFullscreen() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
if w.IsFullscreen() {
w.UnFullscreen()
} else {
w.Fullscreen()
}
})
2023-01-18 21:42:49 +11:00
}
// ToggleMaximise toggles the window between maximised and normal
func (w *WebviewWindow) ToggleMaximise() {
if w.impl == nil && !w.isDestroyed() {
return
}
InvokeSync(func() {
if w.IsMaximised() {
w.UnMaximise()
} else {
w.Maximise()
}
})
}
2024-02-09 20:06:38 +11:00
func (w *WebviewWindow) OpenDevTools() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2024-02-09 20:06:38 +11:00
InvokeSync(w.impl.openDevTools)
2023-01-18 21:42:49 +11:00
}
// ZoomReset resets the zoom level of the webview content to 100%
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) ZoomReset() Window {
2023-01-18 21:42:49 +11:00
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.zoomReset)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowZoomReset)
2023-01-18 21:42:49 +11:00
}
return w
}
// ZoomIn increases the zoom level of the webview content
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) ZoomIn() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.zoomIn)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowZoomIn)
2023-01-18 21:42:49 +11:00
}
// ZoomOut decreases the zoom level of the webview content
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) ZoomOut() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.zoomOut)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowZoomOut)
2023-01-18 21:42:49 +11:00
}
// Close closes the window
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) Close() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
w.emit(events.Common.WindowClosing)
2023-01-18 21:42:49 +11:00
}
func (w *WebviewWindow) Zoom() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.zoom)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowZoom)
2023-01-18 21:42:49 +11:00
}
// SetHTML sets the HTML of the window to the given html string.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetHTML(html string) Window {
2023-01-18 21:42:49 +11:00
w.options.HTML = html
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setHTML(html)
})
2023-01-18 21:42:49 +11:00
}
return w
}
// SetRelativePosition sets the position of the window.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetRelativePosition(x, y int) Window {
2023-01-18 21:42:49 +11:00
w.options.X = x
w.options.Y = y
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setRelativePosition(x, y)
})
2023-01-18 21:42:49 +11:00
}
return w
}
// Minimise minimises the window.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) Minimise() Window {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-02-12 13:36:58 +11:00
w.options.StartState = WindowStateMinimised
2023-01-18 21:42:49 +11:00
return w
}
if !w.IsMinimised() {
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.minimise)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowMinimise)
2023-01-18 21:42:49 +11:00
}
return w
}
// Maximise maximises the window. Min/Max size constraints are disabled.
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) Maximise() Window {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-02-12 13:36:58 +11:00
w.options.StartState = WindowStateMaximised
2023-01-18 21:42:49 +11:00
return w
}
if !w.IsMaximised() {
2023-09-28 11:38:59 -05:00
w.DisableSizeConstraints()
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.maximise)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowMaximise)
2023-01-18 21:42:49 +11:00
}
return w
}
// UnMinimise un-minimises the window. Min/Max size constraints are re-enabled.
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) UnMinimise() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
if w.IsMinimised() {
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.unminimise)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowUnMinimise)
}
2023-01-18 21:42:49 +11:00
}
// UnMaximise un-maximises the window.
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) UnMaximise() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
if w.IsMaximised() {
2023-09-28 11:38:59 -05:00
w.EnableSizeConstraints()
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.unmaximise)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowUnMaximise)
}
2023-01-18 21:42:49 +11:00
}
// UnFullscreen un-fullscreens the window.
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) UnFullscreen() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
if w.IsFullscreen() {
2023-09-28 11:38:59 -05:00
w.EnableSizeConstraints()
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.unfullscreen)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowUnFullscreen)
}
2023-01-18 21:42:49 +11:00
}
// Restore restores the window to its previous state if it was previously minimised, maximised or fullscreen.
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) Restore() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
if w.IsMinimised() {
w.UnMinimise()
} else if w.IsFullscreen() {
w.UnFullscreen()
} else if w.IsMaximised() {
w.UnMaximise()
}
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowRestore)
})
2023-01-18 21:42:49 +11:00
}
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) DisableSizeConstraints() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
if w.options.MinWidth > 0 && w.options.MinHeight > 0 {
w.impl.setMinSize(0, 0)
}
if w.options.MaxWidth > 0 && w.options.MaxHeight > 0 {
w.impl.setMaxSize(0, 0)
}
})
2023-01-18 21:42:49 +11:00
}
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) EnableSizeConstraints() {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
if w.options.MinWidth > 0 && w.options.MinHeight > 0 {
w.SetMinSize(w.options.MinWidth, w.options.MinHeight)
}
if w.options.MaxWidth > 0 && w.options.MaxHeight > 0 {
w.SetMaxSize(w.options.MaxWidth, w.options.MaxHeight)
}
})
2023-01-18 21:42:49 +11:00
}
// GetScreen returns the screen that the window is on
2023-01-18 21:42:49 +11:00
func (w *WebviewWindow) GetScreen() (*Screen, error) {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-01-18 21:42:49 +11:00
return nil, nil
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithResultAndError(w.impl.getScreen)
2023-01-18 21:42:49 +11:00
}
// SetFrameless removes the window frame and title bar
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) SetFrameless(frameless bool) Window {
2023-01-18 21:42:49 +11:00
w.options.Frameless = frameless
if w.impl != nil {
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setFrameless(frameless)
})
2023-01-18 21:42:49 +11:00
}
return w
}
2023-02-06 20:50:11 +11:00
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) DispatchWailsEvent(event *WailsEvent) {
msg := fmt.Sprintf("_wails.dispatchWailsEvent(%s);", event.ToJSON())
2024-03-14 08:57:10 +11:00
w.ExecJS(msg)
2023-02-06 20:50:11 +11:00
}
func (w *WebviewWindow) dispatchWindowEvent(id uint) {
// TODO: Make this more efficient by keeping a list of which events have been registered
// and only dispatching those.
jsEvent := &WailsEvent{
Name: events.JSEvent(id),
}
2023-09-28 11:38:59 -05:00
w.DispatchWailsEvent(jsEvent)
}
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) Info(message string, args ...any) {
var messageArgs []interface{}
messageArgs = append(messageArgs, args...)
messageArgs = append(messageArgs, "sender", w.Name())
globalApplication.info(message, messageArgs...)
2023-02-06 20:50:11 +11:00
}
2023-02-12 13:36:58 +11:00
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) Error(message string, args ...any) {
var messageArgs []interface{}
messageArgs = append(messageArgs, args...)
messageArgs = append(messageArgs, "sender", w.Name())
globalApplication.error(message, messageArgs...)
2023-02-12 13:36:58 +11:00
}
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) HandleDragAndDropMessage(filenames []string) {
thisEvent := NewWindowEvent()
2023-02-12 13:36:58 +11:00
ctx := newWindowEventContext()
2023-09-28 11:38:59 -05:00
ctx.setDroppedFiles(filenames)
thisEvent.ctx = ctx
2023-10-21 11:21:10 +11:00
for _, listener := range w.eventListeners[uint(events.Common.WindowFilesDropped)] {
listener.callback(thisEvent)
2023-02-12 13:36:58 +11:00
}
}
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) OpenContextMenu(data *ContextMenuData) {
2023-02-12 13:36:58 +11:00
menu, ok := w.contextMenus[data.Id]
if !ok {
// try application level context menu
menu, ok = globalApplication.getContextMenu(data.Id)
if !ok {
2023-09-28 11:38:59 -05:00
w.Error("No context menu found for id: %s", data.Id)
2023-02-12 13:36:58 +11:00
return
}
}
menu.setContextData(data)
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-02-12 13:36:58 +11:00
return
}
2023-10-10 19:59:45 +11:00
InvokeSync(func() {
w.impl.openContextMenu(menu, data)
})
2023-02-12 13:36:58 +11:00
}
// RegisterContextMenu registers a context menu and assigns it the given name.
2023-02-12 13:36:58 +11:00
func (w *WebviewWindow) RegisterContextMenu(name string, menu *Menu) {
w.contextMenusLock.Lock()
defer w.contextMenusLock.Unlock()
w.contextMenus[name] = menu
}
// NativeWindowHandle returns the platform native window handle for the window.
func (w *WebviewWindow) NativeWindowHandle() (uintptr, error) {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
return 0, errors.New("native handle unavailable as window is not running")
}
return w.impl.nativeWindowHandle(), nil
}
func (w *WebviewWindow) Focus() {
2023-09-24 08:57:22 +10:00
InvokeSync(w.impl.focus)
2023-06-12 10:27:30 +10:00
w.emit(events.Common.WindowFocus)
}
func (w *WebviewWindow) emit(eventType events.WindowEventType) {
2023-07-12 20:31:13 +10:00
windowEvents <- &windowEvent{
2023-06-12 10:27:30 +10:00
WindowID: w.id,
EventID: uint(eventType),
}
}
2023-06-13 17:44:07 +10:00
func (w *WebviewWindow) startDrag() error {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-06-13 17:44:07 +10:00
return nil
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithError(w.impl.startDrag)
2023-06-13 17:44:07 +10:00
}
2023-06-18 17:12:49 +10:00
func (w *WebviewWindow) Print() error {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-06-18 17:12:49 +10:00
return nil
}
2023-09-24 08:57:22 +10:00
return InvokeSyncWithError(w.impl.print)
2023-06-18 17:12:49 +10:00
}
func (w *WebviewWindow) SetEnabled(enabled bool) {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setEnabled(enabled)
})
}
func (w *WebviewWindow) SetAbsolutePosition(x int, y int) {
// set absolute position
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
w.impl.setAbsolutePosition(x, y)
})
}
2023-09-17 20:48:30 +10:00
func (w *WebviewWindow) processKeyBinding(acceleratorString string) bool {
// Check key bindings
if w.keyBindings != nil {
if callback := w.keyBindings[acceleratorString]; callback != nil {
// Execute callback
go callback(w)
2023-09-17 20:48:30 +10:00
return true
}
}
return globalApplication.processKeyBinding(acceleratorString, w)
2023-09-17 20:48:30 +10:00
}
2023-09-20 20:35:35 +10:00
2023-09-28 11:38:59 -05:00
func (w *WebviewWindow) HandleKeyEvent(acceleratorString string) {
2024-01-09 07:45:56 +11:00
if w.impl == nil && !w.isDestroyed() {
2023-09-20 20:35:35 +10:00
return
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
2023-09-20 20:35:35 +10:00
w.impl.handleKeyEvent(acceleratorString)
})
}
2024-01-09 07:45:56 +11:00
func (w *WebviewWindow) isDestroyed() bool {
w.destroyedLock.RLock()
defer w.destroyedLock.RUnlock()
return w.destroyed
}