mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 05:11:29 +08:00

* Tidy up runtime JS * Initial implementation of runtime over http * Update runtime deps. Fix test task. * Support Clipboard. Message Processor refactor. * Add `Window.Screen()` Clipboard `GetText` -> `Text` * Support most dialogs Better JS->Go object mapping Implement Go->JS callback mechanism Rename `window.runtime` -> `window.wails` to better reflect the Go API * Support SaveFile dialog * Remove go.work * Tidy up * Event->CustomEvent to prevent potential clash with native JS Event object Support Eventing * Support application calls * Support logging * Support named windows Remove debug info * Update v3 changes
644 lines
12 KiB
Go
644 lines
12 KiB
Go
package application
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/wailsapp/wails/v3/pkg/logger"
|
|
|
|
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
|
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
|
|
assetserveroptions "github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
|
"github.com/wailsapp/wails/v3/internal/runtime"
|
|
"github.com/wailsapp/wails/v3/pkg/events"
|
|
"github.com/wailsapp/wails/v3/pkg/options"
|
|
)
|
|
|
|
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)
|
|
restore()
|
|
setBackgroundColour(color *options.RGBA)
|
|
run()
|
|
center()
|
|
size() (int, int)
|
|
width() int
|
|
height() int
|
|
position() (int, int)
|
|
destroy()
|
|
reload()
|
|
forceReload()
|
|
toggleDevTools()
|
|
zoomReset()
|
|
zoomIn()
|
|
zoomOut()
|
|
getZoom() float64
|
|
setZoom(zoom float64)
|
|
close()
|
|
zoom()
|
|
minimize()
|
|
setHTML(html string)
|
|
setPosition(x int, y int)
|
|
on(eventID uint)
|
|
minimise()
|
|
unminimise()
|
|
maximise()
|
|
unmaximise()
|
|
fullscreen()
|
|
unfullscreen()
|
|
isMinimised() bool
|
|
isMaximised() bool
|
|
isFullscreen() bool
|
|
disableSizeConstraints()
|
|
setFullscreenButtonEnabled(enabled bool)
|
|
show()
|
|
hide()
|
|
getScreen() (*Screen, error)
|
|
setFrameless(bool)
|
|
}
|
|
)
|
|
|
|
type WebviewWindow struct {
|
|
options *options.WebviewWindow
|
|
impl webviewWindowImpl
|
|
implLock sync.RWMutex
|
|
id uint
|
|
|
|
assets *assetserver.AssetServer
|
|
messageProcessor *MessageProcessor
|
|
|
|
eventListeners map[uint][]func()
|
|
eventListenersLock sync.RWMutex
|
|
}
|
|
|
|
var windowID uint
|
|
var windowIDLock sync.RWMutex
|
|
|
|
func getWindowID() uint {
|
|
windowIDLock.Lock()
|
|
defer windowIDLock.Unlock()
|
|
windowID++
|
|
return windowID
|
|
}
|
|
|
|
func NewWindow(options *options.WebviewWindow) *WebviewWindow {
|
|
if options.Width == 0 {
|
|
options.Width = 800
|
|
}
|
|
if options.Height == 0 {
|
|
options.Height = 600
|
|
}
|
|
if options.URL == "" {
|
|
options.URL = "/"
|
|
}
|
|
|
|
opts := assetserveroptions.Options{Assets: options.Assets.FS, Handler: options.Assets.Handler, Middleware: options.Assets.Middleware}
|
|
// TODO Bindings, ServingFrom disk?
|
|
srv, err := assetserver.NewAssetServer("", opts, false, nil, runtime.RuntimeAssetsBundle)
|
|
if err != nil {
|
|
globalApplication.fatal(err.Error())
|
|
}
|
|
|
|
result := &WebviewWindow{
|
|
id: getWindowID(),
|
|
options: options,
|
|
eventListeners: make(map[uint][]func()),
|
|
|
|
assets: srv,
|
|
}
|
|
|
|
result.messageProcessor = NewMessageProcessor(result)
|
|
srv.UseRuntimeHandler(result.messageProcessor)
|
|
|
|
return result
|
|
}
|
|
|
|
func (w *WebviewWindow) SetTitle(title string) *WebviewWindow {
|
|
w.implLock.RLock()
|
|
defer w.implLock.RUnlock()
|
|
w.options.Title = title
|
|
if w.impl != nil {
|
|
w.impl.setTitle(title)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) Name() string {
|
|
if w.options.Name != "" {
|
|
return w.options.Name
|
|
}
|
|
return fmt.Sprintf("Window %d", w.id)
|
|
}
|
|
|
|
func (w *WebviewWindow) SetSize(width, height int) *WebviewWindow {
|
|
// 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 {
|
|
w.impl.setSize(width, height)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) run() {
|
|
if w.impl != nil {
|
|
return
|
|
}
|
|
w.implLock.Lock()
|
|
w.impl = newWindowImpl(w)
|
|
w.implLock.Unlock()
|
|
w.impl.run()
|
|
}
|
|
|
|
func (w *WebviewWindow) SetAlwaysOnTop(b bool) *WebviewWindow {
|
|
w.options.AlwaysOnTop = b
|
|
if w.impl == nil {
|
|
w.impl.setAlwaysOnTop(b)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) Show() *WebviewWindow {
|
|
if globalApplication.impl == nil {
|
|
return w
|
|
}
|
|
if w.impl == nil {
|
|
w.run()
|
|
return w
|
|
}
|
|
w.impl.show()
|
|
return w
|
|
}
|
|
func (w *WebviewWindow) Hide() *WebviewWindow {
|
|
w.options.Hidden = true
|
|
if w.impl != nil {
|
|
w.impl.hide()
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) SetURL(s string) *WebviewWindow {
|
|
w.options.URL = s
|
|
if w.impl != nil {
|
|
w.impl.setURL(s)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) SetZoom(magnification float64) *WebviewWindow {
|
|
w.options.Zoom = magnification
|
|
if w.impl != nil {
|
|
w.impl.setZoom(magnification)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) GetZoom() float64 {
|
|
if w.impl != nil {
|
|
return w.impl.getZoom()
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func (w *WebviewWindow) SetResizable(b bool) *WebviewWindow {
|
|
w.options.DisableResize = !b
|
|
if w.impl != nil {
|
|
w.impl.setResizable(b)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) Resizable() bool {
|
|
return !w.options.DisableResize
|
|
}
|
|
|
|
func (w *WebviewWindow) SetMinSize(minWidth, minHeight int) *WebviewWindow {
|
|
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 {
|
|
w.impl.setSize(newWidth, newHeight)
|
|
}
|
|
w.impl.setMinSize(minWidth, minHeight)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) SetMaxSize(maxWidth, maxHeight int) *WebviewWindow {
|
|
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 {
|
|
w.impl.setSize(newWidth, newHeight)
|
|
}
|
|
w.impl.setMaxSize(maxWidth, maxHeight)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) ExecJS(js string) {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.execJS(js)
|
|
}
|
|
|
|
func (w *WebviewWindow) Fullscreen() *WebviewWindow {
|
|
if w.impl == nil {
|
|
w.options.StartState = options.WindowStateFullscreen
|
|
return w
|
|
}
|
|
if !w.IsFullscreen() {
|
|
w.disableSizeConstraints()
|
|
w.impl.fullscreen()
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) SetFullscreenButtonEnabled(enabled bool) *WebviewWindow {
|
|
w.options.FullscreenButtonEnabled = enabled
|
|
if w.impl != nil {
|
|
w.impl.setFullscreenButtonEnabled(enabled)
|
|
}
|
|
return w
|
|
}
|
|
|
|
// IsMinimised returns true if the window is minimised
|
|
func (w *WebviewWindow) IsMinimised() bool {
|
|
if w.impl == nil {
|
|
return false
|
|
}
|
|
return w.impl.isMinimised()
|
|
}
|
|
|
|
// IsMaximised returns true if the window is maximised
|
|
func (w *WebviewWindow) IsMaximised() bool {
|
|
if w.impl == nil {
|
|
return false
|
|
}
|
|
return w.impl.isMaximised()
|
|
}
|
|
|
|
// Size returns the size of the window
|
|
func (w *WebviewWindow) Size() (width int, height int) {
|
|
if w.impl == nil {
|
|
return 0, 0
|
|
}
|
|
return w.impl.size()
|
|
}
|
|
|
|
// IsFullscreen returns true if the window is fullscreen
|
|
func (w *WebviewWindow) IsFullscreen() bool {
|
|
w.implLock.RLock()
|
|
defer w.implLock.RUnlock()
|
|
if w.impl == nil {
|
|
return false
|
|
}
|
|
return w.impl.isFullscreen()
|
|
}
|
|
|
|
func (w *WebviewWindow) SetBackgroundColour(colour *options.RGBA) *WebviewWindow {
|
|
w.options.BackgroundColour = colour
|
|
if w.impl != nil {
|
|
w.impl.setBackgroundColour(colour)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) handleMessage(message string) {
|
|
w.info(message)
|
|
// Check for special messages
|
|
if message == "test" {
|
|
w.SetTitle("Hello World")
|
|
}
|
|
w.messageProcessor.ProcessMessage(message)
|
|
|
|
}
|
|
|
|
func (w *WebviewWindow) handleWebViewRequest(request webview.Request) {
|
|
url, _ := request.URL()
|
|
w.info("Request: %s", url)
|
|
w.assets.ServeWebViewRequest(request)
|
|
}
|
|
|
|
func (w *WebviewWindow) Center() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.center()
|
|
}
|
|
|
|
func (w *WebviewWindow) On(eventType events.WindowEventType, callback func()) {
|
|
eventID := uint(eventType)
|
|
w.eventListenersLock.Lock()
|
|
defer w.eventListenersLock.Unlock()
|
|
w.eventListeners[eventID] = append(w.eventListeners[eventID], callback)
|
|
if w.impl != nil {
|
|
w.impl.on(eventID)
|
|
}
|
|
}
|
|
|
|
func (w *WebviewWindow) handleWindowEvent(id uint) {
|
|
w.eventListenersLock.RLock()
|
|
for _, callback := range w.eventListeners[id] {
|
|
go callback()
|
|
}
|
|
w.eventListenersLock.RUnlock()
|
|
}
|
|
|
|
func (w *WebviewWindow) Width() int {
|
|
if w.impl == nil {
|
|
return 0
|
|
}
|
|
return w.impl.width()
|
|
}
|
|
|
|
func (w *WebviewWindow) Height() int {
|
|
if w.impl == nil {
|
|
return 0
|
|
}
|
|
return w.impl.height()
|
|
}
|
|
|
|
func (w *WebviewWindow) Position() (int, int) {
|
|
w.implLock.RLock()
|
|
defer w.implLock.RUnlock()
|
|
if w.impl == nil {
|
|
return 0, 0
|
|
}
|
|
return w.impl.position()
|
|
}
|
|
|
|
func (w *WebviewWindow) Destroy() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.destroy()
|
|
}
|
|
|
|
func (w *WebviewWindow) Reload() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.reload()
|
|
}
|
|
|
|
func (w *WebviewWindow) ForceReload() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.forceReload()
|
|
}
|
|
|
|
func (w *WebviewWindow) ToggleFullscreen() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
if w.IsFullscreen() {
|
|
w.UnFullscreen()
|
|
} else {
|
|
w.Fullscreen()
|
|
}
|
|
}
|
|
|
|
func (w *WebviewWindow) ToggleDevTools() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.toggleDevTools()
|
|
}
|
|
|
|
func (w *WebviewWindow) ZoomReset() *WebviewWindow {
|
|
if w.impl != nil {
|
|
w.impl.zoomReset()
|
|
}
|
|
return w
|
|
|
|
}
|
|
|
|
func (w *WebviewWindow) ZoomIn() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.zoomIn()
|
|
}
|
|
|
|
func (w *WebviewWindow) ZoomOut() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.zoomOut()
|
|
}
|
|
|
|
func (w *WebviewWindow) Close() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.close()
|
|
}
|
|
|
|
func (w *WebviewWindow) Minimize() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.minimize()
|
|
}
|
|
|
|
func (w *WebviewWindow) Zoom() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.zoom()
|
|
}
|
|
|
|
func (w *WebviewWindow) SetHTML(html string) *WebviewWindow {
|
|
w.options.HTML = html
|
|
if w.impl != nil {
|
|
w.impl.setHTML(html)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) SetPosition(x, y int) *WebviewWindow {
|
|
w.options.X = x
|
|
w.options.Y = y
|
|
if w.impl != nil {
|
|
w.impl.setPosition(x, y)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) Minimise() *WebviewWindow {
|
|
if w.impl == nil {
|
|
w.options.StartState = options.WindowStateMinimised
|
|
return w
|
|
}
|
|
if !w.IsMinimised() {
|
|
w.impl.minimise()
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) Maximise() *WebviewWindow {
|
|
if w.impl == nil {
|
|
w.options.StartState = options.WindowStateMaximised
|
|
return w
|
|
}
|
|
if !w.IsMaximised() {
|
|
w.disableSizeConstraints()
|
|
w.impl.maximise()
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) UnMinimise() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.unminimise()
|
|
}
|
|
|
|
func (w *WebviewWindow) UnMaximise() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.enableSizeConstraints()
|
|
w.impl.unmaximise()
|
|
}
|
|
|
|
func (w *WebviewWindow) UnFullscreen() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.enableSizeConstraints()
|
|
w.impl.unfullscreen()
|
|
}
|
|
|
|
func (w *WebviewWindow) Restore() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
if w.IsMinimised() {
|
|
w.UnMinimise()
|
|
} else if w.IsMaximised() {
|
|
w.UnMaximise()
|
|
} else if w.IsFullscreen() {
|
|
w.UnFullscreen()
|
|
}
|
|
}
|
|
|
|
func (w *WebviewWindow) disableSizeConstraints() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.impl.setMinSize(0, 0)
|
|
w.impl.setMaxSize(0, 0)
|
|
}
|
|
|
|
func (w *WebviewWindow) enableSizeConstraints() {
|
|
if w.impl == nil {
|
|
return
|
|
}
|
|
w.SetMinSize(w.options.MinWidth, w.options.MinHeight)
|
|
w.SetMaxSize(w.options.MaxWidth, w.options.MaxHeight)
|
|
}
|
|
|
|
func (w *WebviewWindow) GetScreen() (*Screen, error) {
|
|
if w.impl == nil {
|
|
return nil, nil
|
|
}
|
|
return w.impl.getScreen()
|
|
}
|
|
|
|
func (w *WebviewWindow) SetFrameless(frameless bool) *WebviewWindow {
|
|
w.options.Frameless = frameless
|
|
if w.impl != nil {
|
|
w.impl.setFrameless(frameless)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *WebviewWindow) dispatchCustomEvent(event *CustomEvent) {
|
|
msg := fmt.Sprintf("_wails.dispatchCustomEvent(%s);", event.ToJSON())
|
|
w.ExecJS(msg)
|
|
}
|
|
|
|
func (w *WebviewWindow) info(message string, args ...any) {
|
|
|
|
globalApplication.Log(&logger.Message{
|
|
Level: "INFO",
|
|
Message: message,
|
|
Data: args,
|
|
Sender: w.Name(),
|
|
Time: time.Now(),
|
|
})
|
|
}
|