diff --git a/v3/examples/build/main.go b/v3/examples/build/main.go index b8a0db3078f..cebeb467f3c 100755 --- a/v3/examples/build/main.go +++ b/v3/examples/build/main.go @@ -29,7 +29,7 @@ func main() { log.Println("ApplicationDidFinishLaunching") }) - currentWindow := func(fn func(window *application.WebviewWindow)) { + currentWindow := func(fn func(window application.Window)) { if app.CurrentWindow() != nil { fn(app.CurrentWindow()) } else { @@ -118,122 +118,122 @@ func main() { sizeMenu := menu.AddSubmenu("Size") sizeMenu.Add("Set Size (800,600)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetSize(800, 600) }) }) sizeMenu.Add("Set Size (Random)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetSize(rand.Intn(800)+200, rand.Intn(600)+200) }) }) sizeMenu.Add("Set Min Size (200,200)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMinSize(200, 200) }) }) sizeMenu.Add("Set Max Size (600,600)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMaximiseButtonState(application.ButtonDisabled) w.SetMaxSize(600, 600) }) }) sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { width, height := w.Size() application.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show() }) }) sizeMenu.Add("Reset Min Size").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMinSize(0, 0) }) }) sizeMenu.Add("Reset Max Size").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMaxSize(0, 0) w.SetMaximiseButtonState(application.ButtonEnabled) }) }) positionMenu := menu.AddSubmenu("Position") positionMenu.Add("Set Relative Position (0,0)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetRelativePosition(0, 0) }) }) positionMenu.Add("Set Relative Position (Random)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetRelativePosition(rand.Intn(1000), rand.Intn(800)) }) }) positionMenu.Add("Get Relative Position").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { x, y := w.RelativePosition() application.InfoDialog().SetTitle("Current WebviewWindow Relative Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show() }) }) positionMenu.Add("Center").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Center() }) }) stateMenu := menu.AddSubmenu("State") stateMenu.Add("Minimise (for 2 secs)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Minimise() time.Sleep(2 * time.Second) w.Restore() }) }) stateMenu.Add("Maximise").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Maximise() }) }) stateMenu.Add("Fullscreen").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Fullscreen() }) }) stateMenu.Add("UnFullscreen").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.UnFullscreen() }) }) stateMenu.Add("Restore").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Restore() }) }) stateMenu.Add("Hide (for 2 seconds)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Hide() time.Sleep(2 * time.Second) w.Show() }) }) stateMenu.Add("Always on Top").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetAlwaysOnTop(true) }) }) stateMenu.Add("Not always on Top").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetAlwaysOnTop(false) }) }) stateMenu.Add("Google.com").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetURL("https://google.com") }) }) stateMenu.Add("wails.io").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetURL("https://wails.io") }) }) @@ -258,7 +258,7 @@ func main() { } }) stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { screen, err := w.GetScreen() if err != nil { application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show() diff --git a/v3/examples/events-bug/main.go b/v3/examples/events-bug/main.go index 02af2385678..88130ddf0f8 100644 --- a/v3/examples/events-bug/main.go +++ b/v3/examples/events-bug/main.go @@ -13,8 +13,8 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, - KeyBindings: map[string]func(window *application.WebviewWindow){ - "shift+ctrl+c": func(window *application.WebviewWindow) { + KeyBindings: map[string]func(window application.Window){ + "shift+ctrl+c": func(window application.Window) { selection, err := application.OpenFileDialog(). CanChooseFiles(true). CanCreateDirectories(true). diff --git a/v3/examples/keybindings/main.go b/v3/examples/keybindings/main.go index ee9dee82568..c816e6bbf17 100644 --- a/v3/examples/keybindings/main.go +++ b/v3/examples/keybindings/main.go @@ -13,8 +13,8 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, - KeyBindings: map[string]func(window *application.WebviewWindow){ - "shift+ctrl+c": func(window *application.WebviewWindow) { + KeyBindings: map[string]func(window application.Window){ + "shift+ctrl+c": func(window application.Window) { window.Center() }, }, diff --git a/v3/examples/window/main.go b/v3/examples/window/main.go index a0c3ceda9bb..15cad7cef0f 100644 --- a/v3/examples/window/main.go +++ b/v3/examples/window/main.go @@ -88,9 +88,9 @@ func main() { log.Println("ApplicationDidFinishLaunching") }) - var hiddenWindows []*application.WebviewWindow + var hiddenWindows []application.Window - currentWindow := func(fn func(window *application.WebviewWindow)) { + currentWindow := func(fn func(window application.Window)) { if app.CurrentWindow() != nil { fn(app.CurrentWindow()) } else { @@ -120,6 +120,20 @@ func main() { Show() windowCounter++ }) + if runtime.GOOS == "darwin" { + myMenu.Add("New Panel Window").SetAccelerator("CmdOrCtrl+P").OnClick(func(ctx *application.Context) { + app.NewWebviewPanelWithOptions(application.WebviewPanelOptions{ + Floating: true, + ShouldClose: nil, + KeyBindings: nil, + }). + SetTitle("PanelWindow "+strconv.Itoa(windowCounter)). + SetRelativePosition(rand.Intn(1000), rand.Intn(800)). + SetURL("https://wails.io"). + Show() + windowCounter++ + }) + } if runtime.GOOS != "linux" { myMenu.Add("New WebviewWindow (Disable Minimise)"). OnClick(func(ctx *application.Context) { @@ -289,7 +303,7 @@ func main() { // If the user clicks the close button again, the window will // close. ShouldClose: func(window *application.WebviewWindow) bool { - if !lo.Contains(hiddenWindows, window) { + if !lo.Contains(hiddenWindows, application.Window(window)) { hiddenWindows = append(hiddenWindows, window) go func() { time.Sleep(5 * time.Second) @@ -299,7 +313,7 @@ func main() { return false } // Remove the window from the hiddenWindows list - hiddenWindows = lo.Without(hiddenWindows, window) + hiddenWindows = lo.Without(hiddenWindows, application.Window(window)) return true }, }). @@ -430,41 +444,41 @@ func main() { sizeMenu := menu.AddSubmenu("Size") sizeMenu.Add("Set Size (800,600)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetSize(800, 600) }) }) sizeMenu.Add("Set Size (Random)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetSize(rand.Intn(800)+200, rand.Intn(600)+200) }) }) sizeMenu.Add("Set Min Size (200,200)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMinSize(200, 200) }) }) sizeMenu.Add("Set Max Size (600,600)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMaxSize(600, 600) w.SetMaximiseButtonState(application.ButtonDisabled) }) }) sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { width, height := w.Size() application.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show() }) }) sizeMenu.Add("Reset Min Size").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMinSize(0, 0) }) }) sizeMenu.Add("Reset Max Size").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMaxSize(0, 0) w.SetMaximiseButtonState(application.ButtonEnabled) }) @@ -472,151 +486,151 @@ func main() { positionMenu := menu.AddSubmenu("Position") positionMenu.Add("Set Position (0,0)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetPosition(0, 0) }) }) positionMenu.Add("Set Position (Random)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetPosition(rand.Intn(1000), rand.Intn(800)) }) }) positionMenu.Add("Get Position").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { x, y := w.Position() application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show() }) }) positionMenu.Add("Set Relative Position (0,0)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetRelativePosition(0, 0) }) }) positionMenu.Add("Set Relative Position (Corner)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { screen, _ := w.GetScreen() w.SetRelativePosition(screen.WorkArea.Width-w.Width(), screen.WorkArea.Height-w.Height()) }) }) positionMenu.Add("Set Relative Position (Random)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetRelativePosition(rand.Intn(1000), rand.Intn(800)) }) }) positionMenu.Add("Get Relative Position").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { x, y := w.RelativePosition() application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show() }) }) positionMenu.Add("Center").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Center() }) }) titleBarMenu := menu.AddSubmenu("Controls") titleBarMenu.Add("Disable Minimise").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMinimiseButtonState(application.ButtonDisabled) }) }) titleBarMenu.Add("Enable Minimise").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMinimiseButtonState(application.ButtonEnabled) }) }) titleBarMenu.Add("Hide Minimise").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMinimiseButtonState(application.ButtonHidden) }) }) titleBarMenu.Add("Disable Maximise").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMaximiseButtonState(application.ButtonDisabled) }) }) titleBarMenu.Add("Enable Maximise").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMaximiseButtonState(application.ButtonEnabled) }) }) titleBarMenu.Add("Hide Maximise").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMaximiseButtonState(application.ButtonHidden) }) }) titleBarMenu.Add("Disable Close").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetCloseButtonState(application.ButtonDisabled) }) }) titleBarMenu.Add("Enable Close").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetCloseButtonState(application.ButtonEnabled) }) }) titleBarMenu.Add("Hide Close").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetCloseButtonState(application.ButtonHidden) }) }) stateMenu := menu.AddSubmenu("State") stateMenu.Add("Minimise (for 2 secs)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Minimise() time.Sleep(2 * time.Second) w.Restore() }) }) stateMenu.Add("Maximise").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Maximise() }) }) stateMenu.Add("Fullscreen").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Fullscreen() }) }) stateMenu.Add("UnFullscreen").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.UnFullscreen() }) }) stateMenu.Add("Restore").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Restore() }) }) stateMenu.Add("Hide (for 2 seconds)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Hide() time.Sleep(2 * time.Second) w.Show() }) }) stateMenu.Add("Always on Top").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetAlwaysOnTop(true) }) }) stateMenu.Add("Not always on Top").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetAlwaysOnTop(false) }) }) stateMenu.Add("Google.com").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetURL("https://google.com") }) }) stateMenu.Add("wails.io").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetURL("https://wails.io") }) }) @@ -641,7 +655,7 @@ func main() { } }) stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { screen, err := w.GetScreen() if err != nil { application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show() @@ -652,21 +666,21 @@ func main() { }) }) stateMenu.Add("Disable for 5s").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetEnabled(false) time.Sleep(5 * time.Second) w.SetEnabled(true) }) }) stateMenu.Add("Open Dev Tools").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.OpenDevTools() }) }) if runtime.GOOS != "darwin" { stateMenu.Add("Flash for 5s").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { time.Sleep(2 * time.Second) w.Flash(true) time.Sleep(5 * time.Second) @@ -677,7 +691,7 @@ func main() { printMenu := menu.AddSubmenu("Print") printMenu.Add("Print").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { _ = w.Print() }) }) diff --git a/v3/go.mod b/v3/go.mod index 5e9928d3cb5..352a1e9c8f8 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -14,6 +14,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.4.0 + github.com/goreleaser/nfpm/v2 v2.41.1 github.com/jackmordaunt/icns/v2 v2.2.7 github.com/jaypipes/ghw v0.12.0 github.com/leaanthony/clir v1.6.0 @@ -32,9 +33,9 @@ require ( github.com/tc-hib/winres v0.3.1 github.com/wailsapp/go-webview2 v1.0.18-0.20241130004144-dd8667af33c1 github.com/wailsapp/mimetype v1.4.1 + golang.org/x/sys v0.27.0 golang.org/x/term v0.25.0 golang.org/x/tools v0.23.0 - golang.org/x/sys v0.27.0 gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.21.0 ) @@ -53,14 +54,10 @@ require ( github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/alecthomas/chroma/v2 v2.14.0 // indirect - github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect - github.com/buger/jsonparser v1.1.1 // indirect - github.com/caarlos0/go-version v0.1.1 // indirect github.com/cavaliergopher/cpio v1.0.1 // indirect github.com/cloudflare/circl v1.3.8 // indirect github.com/containerd/console v1.0.4 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/cyphar/filepath-securejoin v0.2.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect @@ -79,11 +76,8 @@ require ( github.com/gookit/color v1.5.2 // indirect github.com/goreleaser/chglog v0.6.1 // indirect github.com/goreleaser/fileglob v1.3.0 // indirect - github.com/goreleaser/nfpm/v2 v2.41.1 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.16 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/invopop/jsonschema v0.12.0 // indirect github.com/jaypipes/pcidb v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect @@ -94,7 +88,6 @@ require ( github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/lithammer/fuzzysearch v1.1.5 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-zglob v0.0.6 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -102,27 +95,20 @@ require ( github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/mango v0.1.0 // indirect - github.com/muesli/mango-cobra v1.2.0 // indirect - github.com/muesli/mango-pflag v0.1.0 // indirect - github.com/muesli/roff v0.1.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/radovskyb/watcher v1.0.7 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rjeczalik/notify v0.9.3 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sajari/fuzzy v1.0.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/skeema/knownhosts v1.2.2 // indirect github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/ulikunitz/xz v0.5.12 // indirect github.com/whilp/git-urls v1.0.0 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect diff --git a/v3/go.sum b/v3/go.sum index 614c68ae100..21d68b42399 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -4,8 +4,6 @@ atomicgo.dev/cursor v0.1.1 h1:0t9sxQomCTRh5ug+hAMCs59x/UmC9QL6Ci5uosINKD4= atomicgo.dev/cursor v0.1.1/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= atomicgo.dev/keyboard v0.2.8 h1:Di09BitwZgdTV1hPyX/b9Cqxi8HVuJQwWivnZUEqlj4= atomicgo.dev/keyboard v0.2.8/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= @@ -28,8 +26,6 @@ github.com/MarvinJWendt/testza v0.5.1/go.mod h1:L7csM8IBqCc0HH4TRYZSPCIRg6zJeqzM github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= @@ -37,8 +33,6 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= @@ -49,8 +43,12 @@ github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDO github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY= github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4= +github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= +github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -58,36 +56,23 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= github.com/atterpac/refresh v0.8.3 h1:Xj0rtd6Wfv/u03wZOdauASBOPxGZOJeik065S0wISNg= github.com/atterpac/refresh v0.8.3/go.mod h1:fJpWySLdpbANS8Ej5OvfZVZIVvi/9bmnhTjKS5EjQes= -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/caarlos0/go-version v0.1.1 h1:1bikKHkGGVIIxqCmufhSSs3hpBScgHGacrvsi8FuIfc= -github.com/caarlos0/go-version v0.1.1/go.mod h1:Ze5Qx4TsBBi5FyrSKVg1Ibc44KGV/llAaKGp86oTwZ0= +github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8= github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk= github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI= github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo= github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -105,39 +90,29 @@ github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcej github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= -github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= -github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= +github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= +github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= -github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= -github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-task/task/v3 v3.31.0 h1:o6iyj9gPJXxvxPi/u/l8e025PmM2BqKgtLNPS2i7hV4= -github.com/go-task/task/v3 v3.31.0/go.mod h1:/CPDAu9nS3+soqY/e1tTrSo/zxk76lnljEV9aBTeKrg= -github.com/go-task/task/v3 v3.35.1 h1:zjQ3tLv+LIStDDTzOQx8F97NE/8FSTanjZuwgy/hwro= -github.com/go-task/task/v3 v3.35.1/go.mod h1:7F6HetCXjlBkgxNjXeTKQYpsA5Q34k4fV94fWXq8GTY= github.com/go-task/task/v3 v3.40.0 h1:1gKx+2UDz06Jtm0MBiN+EqVN87wWEyspuEze4LRGusk= github.com/go-task/task/v3 v3.40.0/go.mod h1:Eb9p9TYX2LpNrd8rBL+Ceht7LzSqA+WniSFeHAJlsnI= github.com/go-task/template v0.1.0 h1:ym/r2G937RZA1bsgiWedNnY9e5kxDT+3YcoAnuIetTE= @@ -157,29 +132,27 @@ github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a/go.mod h1:uqVAUVQ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/goreleaser/chglog v0.6.1 h1:NZKiX8l0FTQPRzBgKST7knvNZmZ04f7PEGkN2wInfhE= github.com/goreleaser/chglog v0.6.1/go.mod h1:Bnnfo07jMZkaAb0uRNASMZyOsX6ROW6X1qbXqN3guUo= github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I= github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU= github.com/goreleaser/nfpm/v2 v2.41.1 h1:4tyZ9b817msLuyGKw53ed3suZNApkGHVZDekdGe8ZEE= github.com/goreleaser/nfpm/v2 v2.41.1/go.mod h1:VPc5kF5OgfA+BosV/A2aB+Vg34honjWvp0Vt8ogsSi0= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= -github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jackmordaunt/icns/v2 v2.2.7 h1:K/RbfvuzjmjVY5y4g+XENRs8ZZatwz4YnLHypa2KwQg= github.com/jackmordaunt/icns/v2 v2.2.7/go.mod h1:ovoTxGguSuoUGKMk5Nn3R7L7BgMQkylsO+bblBuI22A= github.com/jaypipes/ghw v0.12.0 h1:xU2/MDJfWmBhJnujHY9qwXQLs3DBsf0/Xa9vECY0Tho= @@ -193,7 +166,8 @@ github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEE github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= @@ -203,8 +177,6 @@ github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= @@ -234,8 +206,6 @@ github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMr github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc= github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -248,8 +218,6 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-zglob v0.0.4 h1:LQi2iOm0/fGgu80AioIJ/1j9w9Oh+9DZ39J4VAGzHQM= -github.com/mattn/go-zglob v0.0.4/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY= github.com/mattn/go-zglob v0.0.6 h1:mP8RnmCgho4oaUYDIDn6GNxYk+qJGUs8fJLn+twYj2A= github.com/mattn/go-zglob v0.0.6/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= @@ -264,19 +232,10 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI= -github.com/muesli/mango v0.1.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4= -github.com/muesli/mango-cobra v1.2.0 h1:DQvjzAM0PMZr85Iv9LIMaYISpTOliMEg+uMFtNbYvWg= -github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA= -github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg= -github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0= -github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8= -github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= @@ -304,41 +263,35 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY= github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY= github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg= github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= -github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= +github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= +github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= +github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -358,8 +311,6 @@ github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhw github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= @@ -379,14 +330,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= -golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -394,8 +339,6 @@ golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -406,8 +349,6 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -429,7 +370,6 @@ golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -447,8 +387,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -464,8 +402,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -515,9 +451,5 @@ modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= -mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= -mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= -mvdan.cc/sh/v3 v3.8.0 h1:ZxuJipLZwr/HLbASonmXtcvvC9HXY9d2lXZHnKGjFc8= -mvdan.cc/sh/v3 v3.8.0/go.mod h1:w04623xkgBVo7/IUK89E0g8hBykgEpN0vgOj3RJr6MY= mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4= mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY= diff --git a/v3/internal/commands/appimage_testfiles/main.go b/v3/internal/commands/appimage_testfiles/main.go index e63f9f4d5d0..f0df3300787 100644 --- a/v3/internal/commands/appimage_testfiles/main.go +++ b/v3/internal/commands/appimage_testfiles/main.go @@ -28,9 +28,9 @@ func main() { log.Println("ApplicationDidFinishLaunching") }) - var hiddenWindows []*application.WebviewWindow + var hiddenWindows []application.Window - currentWindow := func(fn func(window *application.WebviewWindow)) { + currentWindow := func(fn func(window application.Window)) { if app.CurrentWindow() != nil { fn(app.CurrentWindow()) } else { @@ -66,7 +66,7 @@ func main() { // If the user clicks the close button again, the window will // close. ShouldClose: func(window *application.WebviewWindow) bool { - if !lo.Contains(hiddenWindows, window) { + if !lo.Contains(hiddenWindows, application.Window(window)) { hiddenWindows = append(hiddenWindows, window) go func() { time.Sleep(5 * time.Second) @@ -76,7 +76,7 @@ func main() { return false } // Remove the window from the hiddenWindows list - hiddenWindows = lo.Without(hiddenWindows, window) + hiddenWindows = lo.Without(hiddenWindows, application.Window(window)) return true }, }). @@ -207,141 +207,141 @@ func main() { sizeMenu := menu.AddSubmenu("Size") sizeMenu.Add("Set Size (800,600)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetSize(800, 600) }) }) sizeMenu.Add("Set Size (Random)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetSize(rand.Intn(800)+200, rand.Intn(600)+200) }) }) sizeMenu.Add("Set Min Size (200,200)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMinSize(200, 200) }) }) sizeMenu.Add("Set Max Size (600,600)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMaximiseButtonState(application.ButtonDisabled) w.SetMaxSize(600, 600) }) }) sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { width, height := w.Size() application.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show() }) }) sizeMenu.Add("Reset Min Size").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMinSize(0, 0) }) }) sizeMenu.Add("Reset Max Size").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetMaxSize(0, 0) w.SetMaximiseButtonState(application.ButtonEnabled) }) }) positionMenu := menu.AddSubmenu("Position") positionMenu.Add("Set Relative Position (0,0)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetRelativePosition(0, 0) }) }) positionMenu.Add("Set Relative Position (Random)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetRelativePosition(rand.Intn(1000), rand.Intn(800)) }) }) positionMenu.Add("Get Relative Position").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { x, y := w.RelativePosition() application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show() }) }) positionMenu.Add("Set Position (0,0)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetPosition(0, 0) }) }) positionMenu.Add("Set Position (Random)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetPosition(rand.Intn(1000), rand.Intn(800)) }) }) positionMenu.Add("Get Position").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { x, y := w.Position() application.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show() }) }) positionMenu.Add("Center").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Center() }) }) stateMenu := menu.AddSubmenu("State") stateMenu.Add("Minimise (for 2 secs)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Minimise() time.Sleep(2 * time.Second) w.Restore() }) }) stateMenu.Add("Maximise").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Maximise() }) }) stateMenu.Add("Fullscreen").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Fullscreen() }) }) stateMenu.Add("UnFullscreen").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.UnFullscreen() }) }) stateMenu.Add("Restore").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Restore() }) }) stateMenu.Add("Hide (for 2 seconds)").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.Hide() time.Sleep(2 * time.Second) w.Show() }) }) stateMenu.Add("Always on Top").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetAlwaysOnTop(true) }) }) stateMenu.Add("Not always on Top").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetAlwaysOnTop(false) }) }) stateMenu.Add("Google.com").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetURL("https://google.com") }) }) stateMenu.Add("wails.io").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetURL("https://wails.io") }) }) @@ -366,7 +366,7 @@ func main() { } }) stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { screen, err := w.GetScreen() if err != nil { application.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show() @@ -377,7 +377,7 @@ func main() { }) }) stateMenu.Add("Disable for 5s").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { w.SetEnabled(false) time.Sleep(5 * time.Second) w.SetEnabled(true) @@ -386,7 +386,7 @@ func main() { if runtime.GOOS == "windows" { stateMenu.Add("Flash Start").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { time.Sleep(2 * time.Second) w.Flash(true) }) @@ -395,7 +395,7 @@ func main() { printMenu := menu.AddSubmenu("Print") printMenu.Add("Print").OnClick(func(ctx *application.Context) { - currentWindow(func(w *application.WebviewWindow) { + currentWindow(func(w application.Window) { _ = w.Print() }) }) diff --git a/v3/internal/commands/build-assets.go b/v3/internal/commands/build-assets.go index 19105f6cf39..5f5dedff0ce 100644 --- a/v3/internal/commands/build-assets.go +++ b/v3/internal/commands/build-assets.go @@ -127,10 +127,10 @@ type WailsConfig struct { CompanyName string `yaml:"companyName"` ProductName string `yaml:"productName"` ProductIdentifier string `yaml:"productIdentifier"` - Description string `yaml:"description"` - Copyright string `yaml:"copyright"` - Comments string `yaml:"comments"` - Version string `yaml:"version"` + Description string `yaml:"description"` + Copyright string `yaml:"copyright"` + Comments string `yaml:"comments"` + Version string `yaml:"version"` } `yaml:"info"` FileAssociations []FileAssociation `yaml:"fileAssociations"` } diff --git a/v3/internal/commands/tool_package_test.go b/v3/internal/commands/tool_package_test.go index 21d52962dab..7db09f08bd3 100644 --- a/v3/internal/commands/tool_package_test.go +++ b/v3/internal/commands/tool_package_test.go @@ -58,12 +58,12 @@ func TestToolPackage(t *testing.T) { } return &flags.ToolPackage{ - Format: "DEB", - ConfigPath: configPath, - ExecutableName: "myapp", - }, func() { - os.RemoveAll(filepath.Join(dir, "bin")) - } + Format: "DEB", + ConfigPath: configPath, + ExecutableName: "myapp", + }, func() { + os.RemoveAll(filepath.Join(dir, "bin")) + } }, wantErr: false, }, @@ -85,12 +85,12 @@ func TestToolPackage(t *testing.T) { } return &flags.ToolPackage{ - Format: "RPM", - ConfigPath: configPath, - ExecutableName: "myapp", - }, func() { - os.RemoveAll(filepath.Join(dir, "bin")) - } + Format: "RPM", + ConfigPath: configPath, + ExecutableName: "myapp", + }, func() { + os.RemoveAll(filepath.Join(dir, "bin")) + } }, wantErr: false, }, @@ -112,12 +112,12 @@ func TestToolPackage(t *testing.T) { } return &flags.ToolPackage{ - Format: "ARCHLINUX", - ConfigPath: configPath, - ExecutableName: "myapp", - }, func() { - os.RemoveAll(filepath.Join(dir, "bin")) - } + Format: "ARCHLINUX", + ConfigPath: configPath, + ExecutableName: "myapp", + }, func() { + os.RemoveAll(filepath.Join(dir, "bin")) + } }, wantErr: false, }, diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go index 33d47a70cfc..2f093d035ec 100644 --- a/v3/pkg/application/application.go +++ b/v3/pkg/application/application.go @@ -172,6 +172,21 @@ func New(appOptions Options) *App { return result } +func processKeyBindingOptions(keyBindings map[string]func(window Window)) map[string]func(window Window) { + result := make(map[string]func(window Window)) + 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 + globalApplication.debug("Added Keybinding", "accelerator", acc.String()) + } + return result +} + func mergeApplicationDefaults(o *Options) { if o.Name == "" { o.Name = "My Wails Application" @@ -335,7 +350,7 @@ type App struct { isDebugMode bool // Keybindings - keyBindings map[string]func(window *WebviewWindow) + keyBindings map[string]func(window Window) keyBindingsLock sync.RWMutex // Shutdown @@ -422,7 +437,7 @@ func (a *App) init() { a.windows = make(map[uint]Window) a.systemTrays = make(map[uint]*SystemTray) a.contextMenus = make(map[string]*Menu) - a.keyBindings = make(map[string]func(window *WebviewWindow)) + a.keyBindings = make(map[string]func(window Window)) a.Logger = a.options.Logger a.pid = os.Getpid() a.wailsEventListeners = make([]WailsEventListener, 0) @@ -536,20 +551,31 @@ func (a *App) error(message string, args ...any) { func (a *App) NewWebviewWindowWithOptions(windowOptions WebviewWindowOptions) *WebviewWindow { newWindow := NewWindow(windowOptions) - id := newWindow.ID() + a.addNewWindow(newWindow) - a.windowsLock.Lock() - a.windows[id] = newWindow - a.windowsLock.Unlock() + return newWindow +} - // Call hooks - for _, hook := range a.windowCreatedCallbacks { - hook(newWindow) - } +func (a *App) NewWebviewPanelWithOptions(panelOptions WebviewPanelOptions) *WebviewPanel { + newPanel := NewPanel(panelOptions) + a.addNewWindow(newPanel) - a.runOrDeferToAppRun(newWindow) + return newPanel +} - return newWindow +func (a *App) addNewWindow(newWindow Window) { + id := newWindow.ID() + + a.windowsLock.Lock() + a.windows[id] = newWindow + a.windowsLock.Unlock() + + // Call hooks + for _, hook := range a.windowCreatedCallbacks { + hook(newWindow) + } + + a.runOrDeferToAppRun(newWindow) } func (a *App) NewSystemTray() *SystemTray { @@ -692,7 +718,7 @@ func (a *App) handleDragAndDropMessage(event *dragAndDropMessage) { window, ok := a.windows[event.windowId] a.windowsLock.Unlock() if !ok { - log.Printf("WebviewWindow #%d not found", event.windowId) + a.error("DragAndDropMessage: Window #%d not found", event.windowId) return } // Get callback from window @@ -705,7 +731,7 @@ func (a *App) handleWindowMessage(event *windowMessage) { window, ok := a.windows[event.windowId] a.windowsLock.RUnlock() if !ok { - log.Printf("WebviewWindow #%d not found", event.windowId) + a.error("WindowMessage: Window #%d not found", event.windowId) return } // Check if the message starts with "wails:" @@ -728,7 +754,7 @@ func (a *App) handleWindowEvent(event *windowEvent) { window, ok := a.windows[event.WindowID] a.windowsLock.RUnlock() if !ok { - log.Printf("Window #%d not found", event.WindowID) + a.error("WindowEvent %d: Window #%d not found", event.EventID, event.WindowID) return } window.HandleWindowEvent(event.EventID) @@ -743,7 +769,7 @@ func (a *App) handleMenuItemClicked(menuItemID uint) { menuItem.handleClick() } -func (a *App) CurrentWindow() *WebviewWindow { +func (a *App) CurrentWindow() Window { if a.impl == nil { return nil } @@ -754,7 +780,7 @@ func (a *App) CurrentWindow() *WebviewWindow { if result == nil { return nil } - return result.(*WebviewWindow) + return result } // OnShutdown adds a function to be run when the application is shutting down. @@ -959,7 +985,7 @@ func (a *App) runOrDeferToAppRun(r runnable) { } } -func (a *App) processKeyBinding(acceleratorString string, window *WebviewWindow) bool { +func (a *App) processKeyBinding(acceleratorString string, window Window) bool { if len(a.keyBindings) == 0 { return false } @@ -979,7 +1005,7 @@ func (a *App) processKeyBinding(acceleratorString string, window *WebviewWindow) return true } -func (a *App) addKeyBinding(acceleratorString string, callback func(window *WebviewWindow)) { +func (a *App) addKeyBinding(acceleratorString string, callback func(window Window)) { a.keyBindingsLock.Lock() defer a.keyBindingsLock.Unlock() a.keyBindings[acceleratorString] = callback @@ -997,7 +1023,7 @@ func (a *App) handleWindowKeyEvent(event *windowKeyEvent) { window, ok := a.windows[event.windowId] a.windowsLock.RUnlock() if !ok { - log.Printf("WebviewWindow #%d not found", event.windowId) + a.error("WindowKeyEvent: Window #%d not found", event.windowId) return } // Get callback from window diff --git a/v3/pkg/application/application_options.go b/v3/pkg/application/application_options.go index 171b7e8a543..fa92a66d3af 100644 --- a/v3/pkg/application/application_options.go +++ b/v3/pkg/application/application_options.go @@ -89,8 +89,11 @@ type Options struct { // DisableDefaultSignalHandler disables the default signal handler DisableDefaultSignalHandler bool - // KeyBindings is a map of key bindings to functions - KeyBindings map[string]func(window *WebviewWindow) + // KeyBindings is a map of key bindings to functions. The function provides the [Window] + // interface, so if you need more granular control over the window (e.g. [*WebviewWindow]), it's possible + // to declare the key binding directly in the specific window options or switch over + // the types that implement the [Window] interface + KeyBindings map[string]func(window Window) // OnShutdown is called when the application is about to terminate. // This is useful for cleanup tasks. diff --git a/v3/pkg/application/keys_linux.go b/v3/pkg/application/keys_linux.go index e7b44e380e9..d6c92ad835c 100644 --- a/v3/pkg/application/keys_linux.go +++ b/v3/pkg/application/keys_linux.go @@ -15,7 +15,7 @@ var VirtualKeyCodes = map[uint]string{ 0xffff: "delete", 0xff50: "home", 0xff51: "left", - + 0xffe1: "lshift", 0xffe2: "rshift", 0xffe3: "lcontrol", @@ -51,7 +51,6 @@ var VirtualKeyCodes = map[uint]string{ 0xff57: "end", 0xff58: "begin", - // Alphabet 0x41: "a", 0x42: "b", @@ -160,6 +159,4 @@ var VirtualKeyCodes = map[uint]string{ 0xffd3: "f22", 0xffd4: "f23", 0xffd5: "f24", - - } diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go index ac9465b9dce..0f6ae0b3ea9 100644 --- a/v3/pkg/application/linux_cgo.go +++ b/v3/pkg/application/linux_cgo.go @@ -482,12 +482,12 @@ func clipboardGet() string { func clipboardSet(text string) { cText := C.CString(text) + defer C.free(unsafe.Pointer(cText)) clip := C.gtk_clipboard_get(C.GDK_SELECTION_CLIPBOARD) C.gtk_clipboard_set_text(clip, cText, -1) clip = C.gtk_clipboard_get(C.GDK_SELECTION_PRIMARY) C.gtk_clipboard_set_text(clip, cText, -1) - C.free(unsafe.Pointer(cText)) } // Menu @@ -662,10 +662,10 @@ func menuItemSetDisabled(widget pointer, disabled bool) { func menuItemSetLabel(widget pointer, label string) { value := C.CString(label) + defer C.free(unsafe.Pointer(value)) C.gtk_menu_item_set_label( (*C.GtkMenuItem)(widget), value) - C.free(unsafe.Pointer(value)) } func menuItemRemoveBitmap(widget pointer) { @@ -710,10 +710,11 @@ func menuItemSetBitmap(widget pointer, bitmap []byte) { func menuItemSetToolTip(widget pointer, tooltip string) { value := C.CString(tooltip) + defer C.free(unsafe.Pointer(value)) + C.gtk_widget_set_tooltip_text( (*C.GtkWidget)(widget), value) - C.free(unsafe.Pointer(value)) } func menuItemSignalBlock(widget pointer, handlerId uint, block bool) { @@ -827,15 +828,18 @@ func (w *linuxWebviewWindow) enableDND() { func (w *linuxWebviewWindow) execJS(js string) { value := C.CString(js) + defer C.free(unsafe.Pointer(value)) + blank := C.CString("") + defer C.free(unsafe.Pointer(blank)) C.webkit_web_view_evaluate_javascript(w.webKitWebView(), value, C.long(len(js)), nil, - C.CString(""), + blank, nil, nil, nil) - C.free(unsafe.Pointer(value)) + } func getMousePosition() (int, int, *Screen) { @@ -1105,6 +1109,7 @@ func (w *linuxWebviewWindow) setBackgroundColour(colour RGBA) { colour.Alpha = 255 cssStr := C.CString(fmt.Sprintf("#webview-box {background-color: rgba(%d, %d, %d, %1.1f);}", colour.Red, colour.Green, colour.Blue, float32(colour.Alpha)/255.0)) + defer C.free(unsafe.Pointer(cssStr)) provider := C.gtk_css_provider_new() C.gtk_style_context_add_provider( C.gtk_widget_get_style_context((*C.GtkWidget)(w.vbox)), @@ -1112,7 +1117,6 @@ func (w *linuxWebviewWindow) setBackgroundColour(colour RGBA) { C.GTK_STYLE_PROVIDER_PRIORITY_USER) C.g_object_unref(C.gpointer(provider)) C.gtk_css_provider_load_from_data(provider, cssStr, -1, nil) - C.free(unsafe.Pointer(cssStr)) } func getPrimaryScreen() (*Screen, error) { @@ -1185,8 +1189,8 @@ func (w *linuxWebviewWindow) flash(_ bool) { func (w *linuxWebviewWindow) setTitle(title string) { if !w.parent.options.Frameless { cTitle := C.CString(title) + defer C.free(unsafe.Pointer(cTitle)) C.gtk_window_set_title(w.gtkWindow(), cTitle) - C.free(unsafe.Pointer(cTitle)) } } @@ -1212,8 +1216,8 @@ func (w *linuxWebviewWindow) setTransparent() { func (w *linuxWebviewWindow) setURL(uri string) { target := C.CString(uri) + defer C.free(unsafe.Pointer(target)) C.webkit_web_view_load_uri(w.webKitWebView(), target) - C.free(unsafe.Pointer(target)) } //export emit @@ -1369,8 +1373,8 @@ func (w *linuxWebviewWindow) zoomReset() { func (w *linuxWebviewWindow) reload() { uri := C.CString("wails://") + defer C.free(unsafe.Pointer(uri)) C.webkit_web_view_load_uri(w.webKitWebView(), uri) - C.free(unsafe.Pointer(uri)) } func (w *linuxWebviewWindow) setZoom(zoom float64) { @@ -1481,8 +1485,9 @@ func onUriList(extracted **C.char, data unsafe.Pointer) { } } -var debounceTimer *time.Timer +var debounceTimer *time.Timer var isDebouncing bool = false + //export onKeyPressEvent func onKeyPressEvent(_ *C.GtkWidget, event *C.GdkEventKey, userData C.uintptr_t) C.gboolean { // Keypress re-emits if the key is pressed over a certain threshold so we need a debounce @@ -1617,12 +1622,14 @@ func runChooserDialog(window pointer, allowMultiple, createFolders, showHidden b gtkFilters := []*C.GtkFileFilter{} for _, filter := range filters { f := C.gtk_file_filter_new() + displayStr := C.CString(filter.DisplayName) - C.gtk_file_filter_set_name(f, displayStr) - C.free(unsafe.Pointer(displayStr)) + defer C.free(unsafe.Pointer(displayStr)) patternStr := C.CString(filter.Pattern) + defer C.free(unsafe.Pointer(patternStr)) + + C.gtk_file_filter_set_name(f, displayStr) C.gtk_file_filter_add_pattern(f, patternStr) - C.free(unsafe.Pointer(patternStr)) C.gtk_file_chooser_add_filter((*C.GtkFileChooser)(fc), f) gtkFilters = append(gtkFilters, f) } @@ -1638,10 +1645,10 @@ func runChooserDialog(window pointer, allowMultiple, createFolders, showHidden b if currentFolder != "" { path := C.CString(currentFolder) + defer C.free(unsafe.Pointer(path)) C.gtk_file_chooser_set_current_folder( (*C.GtkFileChooser)(fc), path) - C.free(unsafe.Pointer(path)) } // FIXME: This should be consolidated - duplicate exists in linux_purego.go diff --git a/v3/pkg/application/menu_windows.go b/v3/pkg/application/menu_windows.go index e0d3d8a0cc2..525454fc47b 100644 --- a/v3/pkg/application/menu_windows.go +++ b/v3/pkg/application/menu_windows.go @@ -73,7 +73,7 @@ func (w *windowsMenu) processMenu(parentMenu w32.HMENU, inputMenu *Menu) { if w.parentWindow != nil { w.parentWindow.parent.addMenuBinding(item.accelerator, item) } else { - globalApplication.addKeyBinding(item.accelerator.String(), func(w *WebviewWindow) { + globalApplication.addKeyBinding(item.accelerator.String(), func(w Window) { item.handleClick() }) } diff --git a/v3/pkg/application/menuitem_roles.go b/v3/pkg/application/menuitem_roles.go index 6f7091f0b96..905bcc06f3a 100644 --- a/v3/pkg/application/menuitem_roles.go +++ b/v3/pkg/application/menuitem_roles.go @@ -47,7 +47,12 @@ func NewCutMenuItem() *MenuItem { SetAccelerator("CmdOrCtrl+x").OnClick(func(ctx *Context) { currentWindow := globalApplication.CurrentWindow() if currentWindow != nil { - currentWindow.cut() + switch currentWindow := currentWindow.(type) { + case *WebviewWindow: + currentWindow.cut() + case *WebviewPanel: + currentWindow.cut() + } } }) } diff --git a/v3/pkg/application/messageprocessor.go b/v3/pkg/application/messageprocessor.go index 62d7950dc3c..dd47ddf17c4 100644 --- a/v3/pkg/application/messageprocessor.go +++ b/v3/pkg/application/messageprocessor.go @@ -28,7 +28,7 @@ const ( ) type MessageProcessor struct { - logger *slog.Logger + logger *slog.Logger runningCalls map[string]context.CancelFunc l sync.Mutex diff --git a/v3/pkg/application/popupmenu_windows.go b/v3/pkg/application/popupmenu_windows.go index 11a155b4401..1e7c156a601 100644 --- a/v3/pkg/application/popupmenu_windows.go +++ b/v3/pkg/application/popupmenu_windows.go @@ -123,7 +123,7 @@ func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) { if p.parentWindow != nil { p.parentWindow.parent.addMenuBinding(item.accelerator, item) } else { - globalApplication.addKeyBinding(item.accelerator.String(), func(w *WebviewWindow) { + globalApplication.addKeyBinding(item.accelerator.String(), func(w Window) { item.handleClick() }) } diff --git a/v3/pkg/application/screen_darwin.go b/v3/pkg/application/screen_darwin.go index ad5285e341a..1e93fc4b29c 100644 --- a/v3/pkg/application/screen_darwin.go +++ b/v3/pkg/application/screen_darwin.go @@ -185,6 +185,11 @@ func getScreenForWindow(window *macosWebviewWindow) (*Screen, error) { return cScreenToScreen(cScreen), nil } +func getScreenForPanel(window *macosWebviewPanel) (*Screen, error) { + cScreen := C.getScreenForWindow(window.nsPanel) + return cScreenToScreen(cScreen), nil +} + func getScreenForSystray(systray *macosSystemTray) (*Screen, error) { // Get the Window for the status item // https://stackoverflow.com/a/5875019/4188138 diff --git a/v3/pkg/application/webview_panel.go b/v3/pkg/application/webview_panel.go new file mode 100644 index 00000000000..743ffe9505a --- /dev/null +++ b/v3/pkg/application/webview_panel.go @@ -0,0 +1,90 @@ +package application + +type webviewPanelImpl interface { + webviewWindowImpl + getWebviewWindowImpl() webviewWindowImpl + setFloating(floating bool) +} + +type WebviewPanel struct { + *WebviewWindow + + options WebviewPanelOptions + impl webviewPanelImpl + // keyBindings holds the keybindings for the panel + keyBindings map[string]func(*WebviewPanel) +} + +// NewPanel creates a new panel with the given options +func NewPanel(options WebviewPanelOptions) *WebviewPanel { + window := NewWindow(options.WebviewWindowOptions) + options.WebviewWindowOptions = window.options + + result := &WebviewPanel{ + WebviewWindow: window, + options: options, + } + + // Process keybindings specific to the WebviewPanel + if result.options.KeyBindings != nil || result.options.WebviewWindowOptions.KeyBindings != nil { + result.keyBindings = processKeyBindingOptionsForPanel(result.options.KeyBindings, result.options.WebviewWindowOptions.KeyBindings) + } + + return result +} + +func (p *WebviewPanel) Run() { + if p.impl != nil { + return + } + + p.impl = newPanelImpl(p) + p.WebviewWindow.impl = p.impl.getWebviewWindowImpl() + + InvokeSync(p.impl.run) +} + +// SetFloating makes the panel float above other application in every workspace. +func (p *WebviewPanel) SetFloating(b bool) Window { + p.options.Floating = b + if p.impl != nil { + InvokeSync(func() { + p.impl.setFloating(b) + }) + } + return p +} + +func (p *WebviewPanel) HandleKeyEvent(acceleratorString string) { + if p.impl == nil || p.isDestroyed() { + return + } + InvokeSync(func() { + p.impl.handleKeyEvent(acceleratorString) + }) +} + +func (p *WebviewPanel) processKeyBinding(acceleratorString string) bool { + // Check menu bindings + if p.menuBindings != nil { + p.menuBindingsLock.RLock() + defer p.menuBindingsLock.RUnlock() + if menuItem := p.menuBindings[acceleratorString]; menuItem != nil { + menuItem.handleClick() + return true + } + } + + // Check key bindings + if p.keyBindings != nil { + p.keyBindingsLock.RLock() + defer p.keyBindingsLock.RUnlock() + if callback := p.keyBindings[acceleratorString]; callback != nil { + // Execute callback + go callback(p) + return true + } + } + + return globalApplication.processKeyBinding(acceleratorString, p.WebviewWindow) +} diff --git a/v3/pkg/application/webview_panel_darwin.go b/v3/pkg/application/webview_panel_darwin.go new file mode 100644 index 00000000000..b7d56e82cf0 --- /dev/null +++ b/v3/pkg/application/webview_panel_darwin.go @@ -0,0 +1,85 @@ +package application + +/* +#cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c +#cgo LDFLAGS: -framework Cocoa -framework WebKit + +#include "webview_window_bindings_darwin.h" + +void *panelNew(unsigned int id, int width, int height, bool fraudulentWebsiteWarningEnabled, bool frameless, bool enableDragAndDrop, struct WebviewPreferences preferences) { + return createWindow(WindowTypePanel, id, width, height, fraudulentWebsiteWarningEnabled, frameless, enableDragAndDrop, preferences); +} + +// Set NSPanel floating +void panelSetFloating(void* nsPanel, bool floating) { + // Set panel floating on main thread + NSWindow *window = ((WebviewWindow*)nsPanel).w; + NSPanel *panel = (NSPanel *) window; + + [panel setLevel:floating ? NSFloatingWindowLevel : NSNormalWindowLevel]; + [panel setFloatingPanel:floating ? YES : NO]; + [panel setStyleMask:floating ? panel.styleMask | NSWindowStyleMaskNonactivatingPanel : panel.styleMask & ~NSWindowStyleMaskNonactivatingPanel]; + NSWindowCollectionBehavior panelCB = NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorFullScreenAuxiliary; + [panel setCollectionBehavior:floating ? panel.collectionBehavior | panelCB : panel.collectionBehavior & ~panelCB]; +} +*/ +import "C" +import ( + "unsafe" +) + +type macosWebviewPanel struct { + *macosWebviewWindow + + nsPanel unsafe.Pointer + parent *WebviewPanel +} + +func newPanelImpl(parent *WebviewPanel) *macosWebviewPanel { + result := &macosWebviewPanel{ + macosWebviewWindow: newWindowImpl(parent.WebviewWindow), + parent: parent, + } + return result +} + +func (p *macosWebviewPanel) getWebviewWindowImpl() webviewWindowImpl { + return p.macosWebviewWindow +} + +func (p *macosWebviewPanel) run() { + for eventId := range p.parent.eventListeners { + p.on(eventId) + } + globalApplication.dispatchOnMainThread(func() { + options := p.parent.options + macOptions := options.Mac + + p.nsPanel = C.panelNew(C.uint(p.parent.id), + C.int(options.Width), + C.int(options.Height), + C.bool(macOptions.EnableFraudulentWebsiteWarnings), + C.bool(options.Frameless), + C.bool(options.EnableDragAndDrop), + p.getWebviewPreferences(), + ) + p.macosWebviewWindow.nsWindow = p.nsPanel + + p.setup(&options.WebviewWindowOptions, &macOptions) + p.setFloating(options.Floating) + }) +} + +func (p *macosWebviewPanel) handleKeyEvent(acceleratorString string) { + // Parse acceleratorString + accelerator, err := parseAccelerator(acceleratorString) + if err != nil { + globalApplication.error("unable to parse accelerator: %s", err.Error()) + return + } + p.parent.processKeyBinding(accelerator.String()) +} + +func (p *macosWebviewPanel) setFloating(floating bool) { + C.panelSetFloating(p.nsPanel, C.bool(floating)) +} diff --git a/v3/pkg/application/webview_panel_linux.go b/v3/pkg/application/webview_panel_linux.go new file mode 100644 index 00000000000..26e45b66747 --- /dev/null +++ b/v3/pkg/application/webview_panel_linux.go @@ -0,0 +1,5 @@ +package application + +func newPanelImpl(parent *WebviewPanel) webviewPanelImpl { + panic("Linux Panel not implemented") +} diff --git a/v3/pkg/application/webview_panel_options.go b/v3/pkg/application/webview_panel_options.go new file mode 100644 index 00000000000..241b2b87788 --- /dev/null +++ b/v3/pkg/application/webview_panel_options.go @@ -0,0 +1,46 @@ +package application + +type WebviewPanelOptions struct { + WebviewWindowOptions + + // Floating will make the panel float above other application in every workspace. + Floating bool + + // ShouldClose is called when the panel is about to close. + // Return true to allow the panel to close, or false to prevent it from closing. + ShouldClose func(panel *WebviewPanel) bool + + // KeyBindings is a map of key bindings to functions. Other key bindings provided from + // the embedded field [WebviewWindowOptions] are still valid and the two maps are merged + // together, with the [WebviewPanelOptions] values overriding the others if they have the + // same key. + KeyBindings map[string]func(panel *WebviewPanel) +} + +func processKeyBindingOptionsForPanel(keyBindings map[string]func(panel *WebviewPanel), windowKeyBindings map[string]func(panel *WebviewWindow)) map[string]func(panel *WebviewPanel) { + result := make(map[string]func(panel *WebviewPanel)) + + for key, callback := range windowKeyBindings { + acc, err := parseAccelerator(key) + if err != nil { + globalApplication.error("Invalid keybinding: %s", err.Error()) + continue + } + result[acc.String()] = func(panel *WebviewPanel) { + callback(panel.WebviewWindow) + } + globalApplication.debug("Added Keybinding", "accelerator", acc.String()) + } + + 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 + globalApplication.debug("Added Keybinding", "accelerator", acc.String()) + } + return result +} diff --git a/v3/pkg/application/webview_panel_windows.go b/v3/pkg/application/webview_panel_windows.go new file mode 100644 index 00000000000..da2af6019f1 --- /dev/null +++ b/v3/pkg/application/webview_panel_windows.go @@ -0,0 +1,5 @@ +package application + +func newPanelImpl(parent *WebviewPanel) webviewPanelImpl { + panic("Windows Panel not implemented") +} diff --git a/v3/pkg/application/webview_responder_darwin.h b/v3/pkg/application/webview_responder_darwin.h new file mode 100644 index 00000000000..19cab3e2cd4 --- /dev/null +++ b/v3/pkg/application/webview_responder_darwin.h @@ -0,0 +1,16 @@ +#ifndef WEBVIEW_RESPONDER_DARWIN +#define WEBVIEW_RESPONDER_DARWIN + +#import +#import + +@interface WebviewResponder : NSResponder +@property(assign) NSWindow *w; + +- (WebviewResponder *) initAttachToWindow:(NSWindow *)window; +- (void)keyDown:(NSEvent *)event; +- (NSString *)keyStringFromEvent:(NSEvent *)event; +- (NSString *)specialKeyStringFromEvent:(NSEvent *)event; +@end + +#endif /* WEBVIEW_RESPONDER_DARWIN */ diff --git a/v3/pkg/application/webview_responder_darwin.m b/v3/pkg/application/webview_responder_darwin.m new file mode 100644 index 00000000000..f5b861766d4 --- /dev/null +++ b/v3/pkg/application/webview_responder_darwin.m @@ -0,0 +1,169 @@ +#include "webview_responder_darwin.h" +#include "webview_window_darwin.h" + +extern void processWindowKeyDownEvent(unsigned int, const char*); + +@implementation WebviewResponder +- (WebviewResponder *) initAttachToWindow:(NSWindow *)window { + self = [super init]; + + self.w = window; + [window setNextResponder:self]; + + return self; +} +- (void)keyDown:(NSEvent *)event { + // TODO: FIX: ctrl+l never reaches this function + NSUInteger modifierFlags = event.modifierFlags; + // Create an array to hold the modifier strings + NSMutableArray *modifierStrings = [NSMutableArray array]; + // Check for modifier flags and add corresponding strings to the array + if (modifierFlags & NSEventModifierFlagShift) { + [modifierStrings addObject:@"shift"]; + } + if (modifierFlags & NSEventModifierFlagControl) { + [modifierStrings addObject:@"ctrl"]; + } + if (modifierFlags & NSEventModifierFlagOption) { + [modifierStrings addObject:@"option"]; + } + if (modifierFlags & NSEventModifierFlagCommand) { + [modifierStrings addObject:@"cmd"]; + } + NSString *keyString = [self keyStringFromEvent:event]; + if (keyString.length > 0) { + [modifierStrings addObject:keyString]; + } + // Combine the modifier strings with the key character + NSString *keyEventString = [modifierStrings componentsJoinedByString:@"+"]; + const char* utf8String = [keyEventString UTF8String]; + WebviewWindowDelegate *delegate = (WebviewWindowDelegate*)self.w.delegate; + processWindowKeyDownEvent(delegate.windowId, utf8String); +} +- (NSString *)keyStringFromEvent:(NSEvent *)event { + // Get the pressed key + switch ([event keyCode]) { + // Function keys + case kVK_F1: return @"f1"; + case kVK_F2: return @"f2"; + case kVK_F3: return @"f3"; + case kVK_F4: return @"f4"; + case kVK_F5: return @"f5"; + case kVK_F6: return @"f6"; + case kVK_F7: return @"f7"; + case kVK_F8: return @"f8"; + case kVK_F9: return @"f9"; + case kVK_F10: return @"f10"; + case kVK_F11: return @"f11"; + case kVK_F12: return @"f12"; + case kVK_F13: return @"f13"; + case kVK_F14: return @"f14"; + case kVK_F15: return @"f15"; + case kVK_F16: return @"f16"; + case kVK_F17: return @"f17"; + case kVK_F18: return @"f18"; + case kVK_F19: return @"f19"; + case kVK_F20: return @"f20"; + // Letter keys + case kVK_ANSI_A: return @"a"; + case kVK_ANSI_B: return @"b"; + case kVK_ANSI_C: return @"c"; + case kVK_ANSI_D: return @"d"; + case kVK_ANSI_E: return @"e"; + case kVK_ANSI_F: return @"f"; + case kVK_ANSI_G: return @"g"; + case kVK_ANSI_H: return @"h"; + case kVK_ANSI_I: return @"i"; + case kVK_ANSI_J: return @"j"; + case kVK_ANSI_K: return @"k"; + case kVK_ANSI_L: return @"l"; + case kVK_ANSI_M: return @"m"; + case kVK_ANSI_N: return @"n"; + case kVK_ANSI_O: return @"o"; + case kVK_ANSI_P: return @"p"; + case kVK_ANSI_Q: return @"q"; + case kVK_ANSI_R: return @"r"; + case kVK_ANSI_S: return @"s"; + case kVK_ANSI_T: return @"t"; + case kVK_ANSI_U: return @"u"; + case kVK_ANSI_V: return @"v"; + case kVK_ANSI_W: return @"w"; + case kVK_ANSI_X: return @"x"; + case kVK_ANSI_Y: return @"y"; + case kVK_ANSI_Z: return @"z"; + // Number keys + case kVK_ANSI_0: return @"0"; + case kVK_ANSI_1: return @"1"; + case kVK_ANSI_2: return @"2"; + case kVK_ANSI_3: return @"3"; + case kVK_ANSI_4: return @"4"; + case kVK_ANSI_5: return @"5"; + case kVK_ANSI_6: return @"6"; + case kVK_ANSI_7: return @"7"; + case kVK_ANSI_8: return @"8"; + case kVK_ANSI_9: return @"9"; + // Other special keys + case kVK_Delete: return @"delete"; + case kVK_ForwardDelete: return @"forward delete"; + case kVK_LeftArrow: return @"left"; + case kVK_RightArrow: return @"right"; + case kVK_UpArrow: return @"up"; + case kVK_DownArrow: return @"down"; + case kVK_Tab: return @"tab"; + case kVK_Escape: return @"escape"; + case kVK_Space: return @"space"; + // Punctuation and other keys (for a standard US layout) + case kVK_ANSI_LeftBracket: return @"["; + case kVK_ANSI_RightBracket: return @"]"; + case kVK_ANSI_Comma: return @","; + case kVK_ANSI_Minus: return @"-"; + case kVK_ANSI_Quote: return @"'"; + case kVK_ANSI_Slash: return @"/"; + case kVK_ANSI_Period: return @"."; + case kVK_ANSI_Semicolon: return @";"; + case kVK_ANSI_Equal: return @"="; + case kVK_ANSI_Grave: return @"`"; + case kVK_ANSI_Backslash: return @"\\"; + default: return [self specialKeyStringFromEvent:event]; + } +} +- (NSString *)specialKeyStringFromEvent:(NSEvent *)event { + // Check for special keys like escape and tab + NSString *characters = [event characters]; + if (characters.length == 0) { + return @""; + } + + if ([characters isEqualToString:@"\r"]) { + return @"enter"; + } + if ([characters isEqualToString:@"\b"]) { + return @"backspace"; + } + if ([characters isEqualToString:@"\e"]) { + return @"escape"; + } + // page down + if ([characters isEqualToString:@"\x0B"]) { + return @"page down"; + } + // page up + if ([characters isEqualToString:@"\x0E"]) { + return @"page up"; + } + // home + if ([characters isEqualToString:@"\x01"]) { + return @"home"; + } + // end + if ([characters isEqualToString:@"\x04"]) { + return @"end"; + } + // clear + if ([characters isEqualToString:@"\x0C"]) { + return @"clear"; + } + // default + return @""; +} +@end \ No newline at end of file diff --git a/v3/pkg/application/webview_window.go b/v3/pkg/application/webview_window.go index 5aed72475a1..355c000e825 100644 --- a/v3/pkg/application/webview_window.go +++ b/v3/pkg/application/webview_window.go @@ -223,6 +223,8 @@ func (w *WebviewWindow) setupEventMapping() { // NewWindow creates a new window with the given options func NewWindow(options WebviewWindowOptions) *WebviewWindow { + id := getWindowID() + if options.Width == 0 { options.Width = 800 } @@ -234,11 +236,11 @@ func NewWindow(options WebviewWindowOptions) *WebviewWindow { } if options.Name == "" { - options.Name = fmt.Sprintf("window-%d", getWindowID()) + options.Name = fmt.Sprintf("Window %d", id) } result := &WebviewWindow{ - id: getWindowID(), + id: id, options: options, eventListeners: make(map[uint][]*WindowEventListener), contextMenus: make(map[string]*Menu), @@ -262,13 +264,13 @@ func NewWindow(options WebviewWindowOptions) *WebviewWindow { // Process keybindings if result.options.KeyBindings != nil { - result.keyBindings = processKeyBindingOptions(result.options.KeyBindings) + result.keyBindings = windowProcessKeyBindingOptions(result.options.KeyBindings) } return result } -func processKeyBindingOptions(keyBindings map[string]func(window *WebviewWindow)) map[string]func(window *WebviewWindow) { +func windowProcessKeyBindingOptions(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 @@ -1279,7 +1281,7 @@ func (w *WebviewWindow) processKeyBinding(acceleratorString string) bool { } func (w *WebviewWindow) HandleKeyEvent(acceleratorString string) { - if w.impl == nil && !w.isDestroyed() { + if w.impl == nil || w.isDestroyed() { return } InvokeSync(func() { diff --git a/v3/pkg/application/webview_window_bindings_darwin.h b/v3/pkg/application/webview_window_bindings_darwin.h new file mode 100644 index 00000000000..529ca7eca75 --- /dev/null +++ b/v3/pkg/application/webview_window_bindings_darwin.h @@ -0,0 +1,108 @@ +#ifndef WEBVIEW_WINDOW_BINDINGS_DARWIN +#define WEBVIEW_WINDOW_BINDINGS_DARWIN + +#include "application_darwin.h" +#include "webview_window_darwin.h" +#include +#include "Cocoa/Cocoa.h" +#import +#import +#import "webview_window_darwin_drag.h" + +struct WebviewPreferences { + bool *TabFocusesLinks; + bool *TextInteractionEnabled; + bool *FullscreenEnabled; +}; + +typedef enum { + WindowTypeWindow, + WindowTypePanel +} WindowType; + +extern void registerListener(unsigned int event); + +void *createWindow(WindowType windowType, unsigned int id, int width, int height, bool fraudulentWebsiteWarningEnabled, bool frameless, bool enableDragAndDrop, struct WebviewPreferences preferences); +void printWindowStyle(void *window); +void setInvisibleTitleBarHeight(void *window, unsigned int height); +void windowSetTransparent(void *window); +void windowSetInvisibleTitleBar(void *window, unsigned int height); +void windowSetTitle(void *window, char *title); +void windowSetSize(void *window, int width, int height); +void windowSetAlwaysOnTop(void *window, bool alwaysOnTop); +void setNormalWindowLevel(void *window); +void setFloatingWindowLevel(void *window); +void setPopUpMenuWindowLevel(void *window); +void setMainMenuWindowLevel(void *window); +void setStatusWindowLevel(void *window); +void setModalPanelWindowLevel(void *window); +void setScreenSaverWindowLevel(void *window); +void setTornOffMenuWindowLevel(void *window); +void navigationLoadURL(void *window, char *url); +void windowSetResizable(void *window, bool resizable); +void windowSetMinSize(void *window, int width, int height); +void windowSetMaxSize(void *window, int width, int height); +void windowZoomReset(void *window); +void windowZoomSet(void *window, double zoom); +float windowZoomGet(void *window); +void windowZoomIn(void *window); +void windowZoomOut(void *window); +void windowSetRelativePosition(void *window, int x, int y); +void windowExecJS(void *window, const char *js); +void windowSetTranslucent(void *window); +void webviewSetTransparent(void *window); +void webviewSetBackgroundColour(void *window, int r, int g, int b, int alpha); +void windowSetBackgroundColour(void *window, int r, int g, int b, int alpha); +bool windowIsMaximised(void *window); +bool windowIsFullScreen (void *window); +bool windowIsMinimised(void *window); +bool windowIsFocused(void *window); +void windowFullscreen(void *window); +void windowUnFullscreen(void *window); +void windowRestore(void *window); +void setFullscreenButtonEnabled(void *window, bool enabled); +void windowSetTitleBarAppearsTransparent(void *window, bool transparent); +void windowSetFullSizeContent(void *window, bool fullSize); +void windowSetHideTitleBar(void *window, bool hideTitlebar); +void windowSetHideTitle(void *window, bool hideTitle); +void windowSetUseToolbar(void *window, bool useToolbar); +void windowSetToolbarStyle(void *window, int style); +void windowSetHideToolbarSeparator(void *window, bool hideSeparator); +void windowSetShowToolbarWhenFullscreen(void *window, bool setting); +void windowSetAppearanceTypeByName(void *window, const char *appearanceName); +void windowCenter(void *window); +void windowGetSize(void *window, int *width, int *height); +int windowGetWidth(void *window); +int windowGetHeight(void *window); +void windowGetRelativePosition(void *window, int *x, int *y); +void windowGetPosition(void *window, int *x, int *y); +void windowSetPosition(void *window, int x, int y); +void windowDestroy(void *window); +void windowSetShadow(void *window, bool hasShadow); +void windowClose(void *window); +void windowZoom(void *window); +void windowRenderHTML(void *window, const char *html); +void windowInjectCSS(void *window, const char *css); +void windowMinimise(void *window); +void windowMaximise(void *window); +bool windowIsVisible(void *window); +void windowSetFullScreen(void *window, bool fullscreen); +void windowUnminimise(void *window); +void windowUnmaximise(void *window); +void windowDisableSizeConstraints(void *window); +void windowShow(void *window); +void windowHide(void *window); +void setButtonState(void *button, int state); +void setMinimiseButtonState(void *window, int state); +void setMaximiseButtonState(void *window, int state); +void setCloseButtonState(void *window, int state); +void windowShowMenu(void *window, void *menu, int x, int y); +void windowSetFrameless(void *window, bool frameless); +void startDrag(void *window); +void windowPrint(void *window); +void windowSetEnabled(void *window, bool enabled); +void windowFocus(void *window); +bool isIgnoreMouseEvents(void *window); +void setIgnoreMouseEvents(void *window, bool ignore); + +#endif // WEBVIEW_WINDOW_BINDINGS_DARWIN \ No newline at end of file diff --git a/v3/pkg/application/webview_window_bindings_darwin.m b/v3/pkg/application/webview_window_bindings_darwin.m new file mode 100644 index 00000000000..7cfc4579d44 --- /dev/null +++ b/v3/pkg/application/webview_window_bindings_darwin.m @@ -0,0 +1,763 @@ +#include "webview_window_bindings_darwin.h" + +void *createWindow(WindowType windowType, unsigned int id, int width, int height, bool fraudulentWebsiteWarningEnabled, bool frameless, bool enableDragAndDrop, struct WebviewPreferences preferences) { + NSWindowStyleMask styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; + if( frameless ) { + styleMask = NSWindowStyleMaskBorderless | NSWindowStyleMaskResizable; + } + + WebviewWindow* webviewWindow; + switch( windowType ) { + case WindowTypeWindow: + webviewWindow = [[WebviewWindow alloc] initAsWindow:NSMakeRect(0, 0, width-1, height-1) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + break; + case WindowTypePanel: + webviewWindow = [[WebviewWindow alloc] initAsPanel:NSMakeRect(0, 0, width-1, height-1) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + break; + default: + NSLog(@"Invalid WindowType"); + return nil; + } + + NSWindow *window = webviewWindow.w; + + // Create delegate + WebviewWindowDelegate* delegate = [[WebviewWindowDelegate alloc] init]; + [delegate autorelease]; + + // Set delegate + [window setDelegate:delegate]; + delegate.windowId = id; + + // Add NSView to window + NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; + [view autorelease]; + + [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + if( frameless ) { + [view setWantsLayer:YES]; + view.layer.cornerRadius = 8.0; + } + [window setContentView:view]; + + // Embed wkwebview in window + NSRect frame = NSMakeRect(0, 0, width, height); + WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init]; + [config autorelease]; + + // Set preferences + if( preferences.TabFocusesLinks != NULL ) { + config.preferences.tabFocusesLinks = *preferences.TabFocusesLinks; + } + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110300 + if( @available(macOS 11.3, *) ) { + if( preferences.TextInteractionEnabled != NULL ) { + config.preferences.textInteractionEnabled = *preferences.TextInteractionEnabled; + } + } +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 120300 + if( @available(macOS 12.3, *) ) { + if( preferences.FullscreenEnabled != NULL ) { + config.preferences.elementFullscreenEnabled = *preferences.FullscreenEnabled; + } + } +#endif + + config.suppressesIncrementalRendering = true; + config.applicationNameForUserAgent = @"wails.io"; + [config setURLSchemeHandler:delegate forURLScheme:@"wails"]; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 + if( @available(macOS 10.15, *) ) { + config.preferences.fraudulentWebsiteWarningEnabled = fraudulentWebsiteWarningEnabled; + } +#endif + + // Setup user content controller + WKUserContentController* userContentController = [WKUserContentController new]; + [userContentController autorelease]; + + [userContentController addScriptMessageHandler:delegate name:@"external"]; + config.userContentController = userContentController; + + WKWebView* webView = [[WKWebView alloc] initWithFrame:frame configuration:config]; + [webView autorelease]; + + [view addSubview:webView]; + + // support webview events + [webView setNavigationDelegate:delegate]; + + // Ensure webview resizes with the window + [webView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + if( enableDragAndDrop ) { + WebviewDrag* dragView = [[WebviewDrag alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; + [dragView autorelease]; + + [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [view addSubview:dragView]; + dragView.windowId = id; + } + + webviewWindow.webView = webView; + return webviewWindow; +} + +void printWindowStyle(void *window) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + NSWindowStyleMask styleMask = [nsWindow styleMask]; + // Get delegate + WebviewWindowDelegate* windowDelegate = (WebviewWindowDelegate*)[nsWindow delegate]; + + printf("Window %d style mask: ", windowDelegate.windowId); + + if( styleMask & NSWindowStyleMaskTitled) { + printf("NSWindowStyleMaskTitled "); + } + + if( styleMask & NSWindowStyleMaskClosable) { + printf("NSWindowStyleMaskClosable "); + } + + if( styleMask & NSWindowStyleMaskMiniaturizable) { + printf("NSWindowStyleMaskMiniaturizable "); + } + + if( styleMask & NSWindowStyleMaskResizable) { + printf("NSWindowStyleMaskResizable "); + } + + if( styleMask & NSWindowStyleMaskFullSizeContentView) { + printf("NSWindowStyleMaskFullSizeContentView "); + } + + if( styleMask & NSWindowStyleMaskNonactivatingPanel) { + printf("NSWindowStyleMaskNonactivatingPanel "); + } + + if( styleMask & NSWindowStyleMaskFullScreen) { + printf("NSWindowStyleMaskFullScreen "); + } + + if( styleMask & NSWindowStyleMaskBorderless) { + printf("MSWindowStyleMaskBorderless "); + } + + printf("\n"); +} + + +// setInvisibleTitleBarHeight sets the invisible title bar height +void setInvisibleTitleBarHeight(void* window, unsigned int height) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + // Get delegate + WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[nsWindow delegate]; + // Set height + delegate.invisibleTitleBarHeight = height; +} + +// Make NSWindow transparent +void windowSetTransparent(void* window) { + [((WebviewWindow*)window).w setOpaque:NO]; + [((WebviewWindow*)window).w setBackgroundColor:[NSColor clearColor]]; +} + +void windowSetInvisibleTitleBar(void* window, unsigned int height) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[nsWindow delegate]; + delegate.invisibleTitleBarHeight = height; +} + +// Set the title of the NSWindow +void windowSetTitle(void* window, char* title) { + NSString* nsTitle = [NSString stringWithUTF8String:title]; + [((WebviewWindow*)window).w setTitle:nsTitle]; + free(title); +} + +// Set the size of the NSWindow +void windowSetSize(void* window, int width, int height) { + // Set window size on main thread + NSWindow* nsWindow = ((WebviewWindow*)window).w; + NSSize contentSize = [nsWindow contentRectForFrameRect:NSMakeRect(0, 0, width, height)].size; + [nsWindow setContentSize:contentSize]; + [nsWindow setFrame:NSMakeRect(nsWindow.frame.origin.x, nsWindow.frame.origin.y, width, height) display:YES animate:YES]; +} + +// Set NSWindow always on top +void windowSetAlwaysOnTop(void* window, bool alwaysOnTop) { + // Set window always on top on main thread + [((WebviewWindow*)window).w setLevel:alwaysOnTop ? NSFloatingWindowLevel : NSNormalWindowLevel]; +} + +void setNormalWindowLevel(void* window) { [((WebviewWindow*)window).w setLevel:NSNormalWindowLevel]; } +void setFloatingWindowLevel(void* window) { [((WebviewWindow*)window).w setLevel:NSFloatingWindowLevel]; } +void setPopUpMenuWindowLevel(void* window) { [((WebviewWindow*)window).w setLevel:NSPopUpMenuWindowLevel]; } +void setMainMenuWindowLevel(void* window) { [((WebviewWindow*)window).w setLevel:NSMainMenuWindowLevel]; } +void setStatusWindowLevel(void* window) { [((WebviewWindow*)window).w setLevel:NSStatusWindowLevel]; } +void setModalPanelWindowLevel(void* window) { [((WebviewWindow*)window).w setLevel:NSModalPanelWindowLevel]; } +void setScreenSaverWindowLevel(void* window) { [((WebviewWindow*)window).w setLevel:NSScreenSaverWindowLevel]; } +void setTornOffMenuWindowLevel(void* window) { [((WebviewWindow*)window).w setLevel:NSTornOffMenuWindowLevel]; } + +// Load URL in NSWindow +void navigationLoadURL(void* window, char* url) { + // Load URL on main thread + NSURL* nsURL = [NSURL URLWithString:[NSString stringWithUTF8String:url]]; + NSURLRequest* request = [NSURLRequest requestWithURL:nsURL]; + WebviewWindow *webviewWindow = (WebviewWindow*)window; + [webviewWindow.webView loadRequest:request]; + free(url); +} + +// Set NSWindow resizable +void windowSetResizable(void* window, bool resizable) { + // Set window resizable on main thread + NSWindow* nsWindow = ((WebviewWindow*)window).w; + if( resizable ) { + NSWindowStyleMask styleMask = [nsWindow styleMask] | NSWindowStyleMaskResizable; + [nsWindow setStyleMask:styleMask]; + } else { + NSWindowStyleMask styleMask = [nsWindow styleMask] & ~NSWindowStyleMaskResizable; + [nsWindow setStyleMask:styleMask]; + } +} + +// Set NSWindow min size +void windowSetMinSize(void* window, int width, int height) { + // Set window min size on main thread + NSWindow* nsWindow = ((WebviewWindow*)window).w; + NSSize contentSize = [nsWindow contentRectForFrameRect:NSMakeRect(0, 0, width, height)].size; + [nsWindow setContentMinSize:contentSize]; + NSSize size = { width, height }; + [nsWindow setMinSize:size]; +} + +// Set NSWindow max size +void windowSetMaxSize(void* window, int width, int height) { + // Set window max size on main thread + NSSize size = { FLT_MAX, FLT_MAX }; + size.width = width > 0 ? width : FLT_MAX; + size.height = height > 0 ? height : FLT_MAX; + NSWindow* nsWindow = ((WebviewWindow*)window).w; + NSSize contentSize = [nsWindow contentRectForFrameRect:NSMakeRect(0, 0, size.width, size.height)].size; + [nsWindow setContentMaxSize:contentSize]; + [nsWindow setMaxSize:size]; +} + +// windowZoomReset +void windowZoomReset(void* window) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + [webviewWindow.webView setMagnification:1.0]; +} + +// windowZoomSet +void windowZoomSet(void* window, double zoom) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + // Reset zoom + [webviewWindow.webView setMagnification:zoom]; +} + +// windowZoomGet +float windowZoomGet(void* window) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + // Get zoom + return [webviewWindow.webView magnification]; +} + +// windowZoomIn +void windowZoomIn(void* window) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + // Zoom in + [webviewWindow.webView setMagnification:webviewWindow.webView.magnification + 0.05]; +} + +// windowZoomOut +void windowZoomOut(void* window) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + // Zoom out + if( webviewWindow.webView.magnification > 1.05 ) { + [webviewWindow.webView setMagnification:webviewWindow.webView.magnification - 0.05]; + } else { + [webviewWindow.webView setMagnification:1.0]; + } +} + +// set the window position relative to the screen +void windowSetRelativePosition(void* window, int x, int y) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + NSScreen* screen = [nsWindow screen]; + if( screen == NULL ) { + screen = [NSScreen mainScreen]; + } + NSRect windowFrame = [nsWindow frame]; + NSRect screenFrame = [screen frame]; + windowFrame.origin.x = screenFrame.origin.x + (float)x; + windowFrame.origin.y = (screenFrame.origin.y + screenFrame.size.height) - windowFrame.size.height - (float)y; + + [nsWindow setFrame:windowFrame display:TRUE animate:FALSE]; +} + +// Execute JS in NSWindow +void windowExecJS(void* window, const char* js) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + [webviewWindow.webView evaluateJavaScript:[NSString stringWithUTF8String:js] completionHandler:nil]; + free((void*)js); +} + +// Make NSWindow backdrop translucent +void windowSetTranslucent(void* window) { + // Get window + NSWindow* nsWindow = ((WebviewWindow*)window).w; + + id contentView = [nsWindow contentView]; + NSVisualEffectView *effectView = [NSVisualEffectView alloc]; + NSRect bounds = [contentView bounds]; + [effectView initWithFrame:bounds]; + [effectView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; + [effectView setState:NSVisualEffectStateActive]; + [contentView addSubview:effectView positioned:NSWindowBelow relativeTo:nil]; +} + +// Make webview background transparent +void webviewSetTransparent(void* window) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + // Set webview background transparent + [webviewWindow.webView setValue:@NO forKey:@"drawsBackground"]; +} + +// Set webview background colour +void webviewSetBackgroundColour(void* window, int r, int g, int b, int alpha) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + // Set webview background color + [webviewWindow.webView setValue:[NSColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:alpha/255.0] forKey:@"backgroundColor"]; +} + +// Set the window background colour +void windowSetBackgroundColour(void* window, int r, int g, int b, int alpha) { + [((WebviewWindow*)window).w setBackgroundColor:[NSColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:alpha/255.0]]; +} + +bool windowIsMaximised(void* window) { + return [((WebviewWindow*)window).w isZoomed]; +} + +bool windowIsFullScreen (void* window) { + return [((WebviewWindow*)window).w styleMask] & NSWindowStyleMaskFullScreen; +} + +bool windowIsMinimised(void* window) { + return [((WebviewWindow*)window).w isMiniaturized]; +} + +bool windowIsFocused(void* window) { + return [((WebviewWindow*)window).w isKeyWindow]; +} + +// Set Window fullscreen +void windowFullscreen(void* window) { + if( windowIsFullScreen(window) ) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [((WebviewWindow*)window).w toggleFullScreen:nil]; + }); +} + +void windowUnFullscreen(void* window) { + if( !windowIsFullScreen(window) ) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [((WebviewWindow*)window).w toggleFullScreen:nil]; + }); +} + +// restore window to normal size +void windowRestore(void* window) { + // If window is fullscreen + if([((WebviewWindow*)window).w styleMask] & NSWindowStyleMaskFullScreen) { + [((WebviewWindow*)window).w toggleFullScreen:nil]; + } + // If window is maximised + if([((WebviewWindow*)window).w isZoomed]) { + [((WebviewWindow*)window).w zoom:nil]; + } + // If window in minimised + if([((WebviewWindow*)window).w isMiniaturized]) { + [((WebviewWindow*)window).w deminiaturize:nil]; + } +} + +// disable window fullscreen button +void setFullscreenButtonEnabled(void* window, bool enabled) { + NSButton *fullscreenButton = [((WebviewWindow*)window).w standardWindowButton:NSWindowZoomButton]; + fullscreenButton.enabled = enabled; +} + +// Set the titlebar style +void windowSetTitleBarAppearsTransparent(void* window, bool transparent) { + if( transparent ) { + [((WebviewWindow*)window).w setTitlebarAppearsTransparent:true]; + } else { + [((WebviewWindow*)window).w setTitlebarAppearsTransparent:false]; + } +} + +// Set window fullsize content view +void windowSetFullSizeContent(void* window, bool fullSize) { + NSWindow *nsWindow = ((WebviewWindow*)window).w; + if( fullSize ) { + [nsWindow setStyleMask:[nsWindow styleMask] | NSWindowStyleMaskFullSizeContentView]; + } else { + [nsWindow setStyleMask:[nsWindow styleMask] & ~NSWindowStyleMaskFullSizeContentView]; + } +} + +// Set Hide Titlebar +void windowSetHideTitleBar(void* window, bool hideTitlebar) { + NSWindow *nsWindow = ((WebviewWindow*)window).w; + if( hideTitlebar ) { + [nsWindow setStyleMask:[nsWindow styleMask] & ~NSWindowStyleMaskTitled]; + } else { + [nsWindow setStyleMask:[nsWindow styleMask] | NSWindowStyleMaskTitled]; + } +} + +// Set Hide Title in Titlebar +void windowSetHideTitle(void* window, bool hideTitle) { + NSWindow *nsWindow = ((WebviewWindow*)window).w; + if( hideTitle ) { + [nsWindow setTitleVisibility:NSWindowTitleHidden]; + } else { + [nsWindow setTitleVisibility:NSWindowTitleVisible]; + } +} + +// Set Window use toolbar +void windowSetUseToolbar(void* window, bool useToolbar) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + if( useToolbar ) { + NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"wails.toolbar"]; + [toolbar autorelease]; + [nsWindow setToolbar:toolbar]; + } else { + [nsWindow setToolbar:nil]; + } +} + +// Set window toolbar style +void windowSetToolbarStyle(void* window, int style) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 + if( @available(macOS 11.0, *) ) { + NSToolbar* toolbar = [nsWindow toolbar]; + if( toolbar == nil ) { + return; + } + [nsWindow setToolbarStyle:style]; + } +#endif + +} +// Set Hide Toolbar Separator +void windowSetHideToolbarSeparator(void* window, bool hideSeparator) { + NSToolbar* toolbar = [((WebviewWindow*)window).w toolbar]; + if( toolbar == nil ) { + return; + } + [toolbar setShowsBaselineSeparator:!hideSeparator]; +} + +// Configure the toolbar auto-hide feature +void windowSetShowToolbarWhenFullscreen(void* window, bool setting) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + // Get delegate + WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[nsWindow delegate]; + // Set height + delegate.showToolbarWhenFullscreen = setting; +} + +// Set Window appearance type +void windowSetAppearanceTypeByName(void* window, const char *appearanceName) { + // set window appearance type by name + // Convert appearance name to NSString + NSString* appearanceNameString = [NSString stringWithUTF8String:appearanceName]; + // Set appearance + [((WebviewWindow*)window).w setAppearance:[NSAppearance appearanceNamed:appearanceNameString]]; + + free((void*)appearanceName); +} + +// Center window on current monitor +void windowCenter(void* window) { + [((WebviewWindow*)window).w center]; +} + +// Get the current size of the window +void windowGetSize(void* window, int* width, int* height) { + NSRect frame = [((WebviewWindow*)window).w frame]; + *width = frame.size.width; + *height = frame.size.height; +} + +// Get window width +int windowGetWidth(void* window) { + return [((WebviewWindow*)window).w frame].size.width; +} + +// Get window height +int windowGetHeight(void* window) { + return [((WebviewWindow*)window).w frame].size.height; +} + +// Get window position +void windowGetRelativePosition(void* window, int* x, int* y) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + NSRect frame = [nsWindow frame]; + *x = frame.origin.x; + + // Translate to screen coordinates so Y=0 is the top of the screen + NSScreen* screen = [nsWindow screen]; + if( screen == NULL ) { + screen = [NSScreen mainScreen]; + } + NSRect screenFrame = [screen frame]; + *y = screenFrame.size.height - frame.origin.y - frame.size.height; +} + +// Get absolute window position +void windowGetPosition(void* window, int* x, int* y) { + NSRect frame = [((WebviewWindow*)window).w frame]; + *x = frame.origin.x; + *y = frame.origin.y; +} + +void windowSetPosition(void* window, int x, int y) { + NSWindow *nsWindow = ((WebviewWindow*)window).w; + + NSRect frame = [nsWindow frame]; + frame.origin.x = x; + frame.origin.y = y; + [nsWindow setFrame:frame display:YES]; +} + +// Destroy window +void windowDestroy(void* window) { + [((WebviewWindow*)window).w close]; +} + +// Remove drop shadow from window +void windowSetShadow(void* window, bool hasShadow) { + [((WebviewWindow*)window).w setHasShadow:hasShadow]; +} + +// windowClose closes the current window +void windowClose(void *window) { + [((WebviewWindow*)window).w close]; +} + +// windowZoom +void windowZoom(void *window) { + [((WebviewWindow*)window).w zoom:nil]; +} + +// webviewRenderHTML renders the given HTML +void windowRenderHTML(void *window, const char *html) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + // get window delegate + WebviewWindowDelegate* windowDelegate = (WebviewWindowDelegate*)[webviewWindow.w delegate]; + // render html + [webviewWindow.webView loadHTMLString:[NSString stringWithUTF8String:html] baseURL:nil]; +} + +void windowInjectCSS(void *window, const char *css) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + // inject css + [webviewWindow.webView evaluateJavaScript:[NSString stringWithFormat:@"(function() { var style = document.createElement('style'); style.appendChild(document.createTextNode('%@')); document.head.appendChild(style); })();", [NSString stringWithUTF8String:css]] completionHandler:nil]; + free((void*)css); +} + +void windowMinimise(void *window) { + [((WebviewWindow*)window).w miniaturize:nil]; +} + +// zoom maximizes the window to the screen dimensions +void windowMaximise(void *window) { + [((WebviewWindow*)window).w zoom:nil]; +} + +bool windowIsVisible(void *window) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + return (nsWindow.occlusionState & NSWindowOcclusionStateVisible) == NSWindowOcclusionStateVisible; +} + +// windowSetFullScreen +void windowSetFullScreen(void *window, bool fullscreen) { + if( windowIsFullScreen (window) ) { + return; + } + NSWindow* nsWindow = ((WebviewWindow*)window).w; + windowSetMaxSize(nsWindow, 0, 0); + windowSetMinSize(nsWindow, 0, 0); + [nsWindow toggleFullScreen:nil]; +} + +// windowUnminimise +void windowUnminimise(void *window) { + [((WebviewWindow*)window).w deminiaturize:nil]; +} + +// windowUnmaximise +void windowUnmaximise(void *window) { + [((WebviewWindow*)window).w zoom:nil]; +} + +void windowDisableSizeConstraints(void *window) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + // disable size constraints + [nsWindow setContentMinSize:CGSizeZero]; + [nsWindow setContentMaxSize:CGSizeZero]; +} + +void windowShow(void *window) { + [((WebviewWindow*)window).w makeKeyAndOrderFront:nil]; +} + +void windowHide(void *window) { + [((WebviewWindow*)window).w orderOut:nil]; +} + +// setButtonState sets the state of the given button +// 0 = enabled +// 1 = disabled +// 2 = hidden +void setButtonState(void *button, int state) { + if( button == nil ) { + return; + } + NSButton *nsbutton = (NSButton*)button; + nsbutton.hidden = state == 2; + nsbutton.enabled = state != 1; +} + +// setMinimiseButtonState sets the minimise button state +void setMinimiseButtonState(void *window, int state) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + NSButton *minimiseButton = [nsWindow standardWindowButton:NSWindowMiniaturizeButton]; + setButtonState(minimiseButton, state); +} + +// setMaximiseButtonState sets the maximise button state +void setMaximiseButtonState(void *window, int state) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + NSButton *maximiseButton = [nsWindow standardWindowButton:NSWindowZoomButton]; + setButtonState(maximiseButton, state); +} + +// setCloseButtonState sets the close button state +void setCloseButtonState(void *window, int state) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + NSButton *closeButton = [nsWindow standardWindowButton:NSWindowCloseButton]; + setButtonState(closeButton, state); +} + +// windowShowMenu opens an NSMenu at the given coordinates +void windowShowMenu(void *window, void *menu, int x, int y) { + NSMenu* nsMenu = (NSMenu*)menu; + WKWebView* webView = ((WebviewWindow*)window).webView; + NSPoint point = NSMakePoint(x, y); + [nsMenu popUpMenuPositioningItem:nil atLocation:point inView:webView]; +} + +// Make the given window frameless +void windowSetFrameless(void *window, bool frameless) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + // set the window style to be frameless + if( frameless ) { + [nsWindow setStyleMask:([nsWindow styleMask] | NSWindowStyleMaskFullSizeContentView)]; + } else { + [nsWindow setStyleMask:([nsWindow styleMask] & ~NSWindowStyleMaskFullSizeContentView)]; + } +} + +void startDrag(void *window) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + // Get delegate + WebviewWindowDelegate* windowDelegate = (WebviewWindowDelegate*)[webviewWindow.w delegate]; + // start drag + [windowDelegate startDrag:webviewWindow]; +} + +// Credit: https://stackoverflow.com/q/33319295 +void windowPrint(void *window) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 + // Check if macOS 11.0 or newer + if( @available(macOS 11.0, *) ) { + WebviewWindow *webviewWindow = (WebviewWindow*)window; + NSWindow* nsWindow = webviewWindow.w; + + WebviewWindowDelegate* windowDelegate = (WebviewWindowDelegate*)[nsWindow delegate]; + WKWebView* webView = webviewWindow.webView; + + // TODO: Think about whether to expose this as config + NSPrintInfo *pInfo = [NSPrintInfo sharedPrintInfo]; + pInfo.horizontalPagination = NSPrintingPaginationModeAutomatic; + pInfo.verticalPagination = NSPrintingPaginationModeAutomatic; + pInfo.verticallyCentered = YES; + pInfo.horizontallyCentered = YES; + pInfo.orientation = NSPaperOrientationLandscape; + pInfo.leftMargin = 30; + pInfo.rightMargin = 30; + pInfo.topMargin = 30; + pInfo.bottomMargin = 30; + + NSPrintOperation *po = [webView printOperationWithPrintInfo:pInfo]; + po.showsPrintPanel = YES; + po.showsProgressPanel = YES; + + // Without the next line you get an exception. Also it seems to + // completely ignore the values in the rect. I tried changing them + // in both x and y direction to include content scrolled off screen. + // It had no effect whatsoever in either direction. + po.view.frame = webView.bounds; + + // [printOperation runOperation] DOES NOT WORK WITH WKWEBVIEW, use + [po runOperationModalForWindow:window delegate:windowDelegate didRunSelector:nil contextInfo:nil]; + } +#endif +} + +void windowSetEnabled(void *window, bool enabled) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + [nsWindow setIgnoresMouseEvents:!enabled]; +} + +void windowFocus(void *window) { + NSWindow* nsWindow = ((WebviewWindow*)window).w; + // If the current application is not active, activate it + if( ![[NSApplication sharedApplication] isActive] ) { + [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; + } + [nsWindow makeKeyAndOrderFront:nil]; + [nsWindow makeKeyWindow]; +} + +bool isIgnoreMouseEvents(void *window) { + return [((WebviewWindow*)window).w ignoresMouseEvents]; +} + +void setIgnoreMouseEvents(void *window, bool ignore) { + [((WebviewWindow*)window).w setIgnoresMouseEvents:ignore]; +} diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go index 27e97aecc1d..31cee2e3982 100644 --- a/v3/pkg/application/webview_window_darwin.go +++ b/v3/pkg/application/webview_window_darwin.go @@ -6,804 +6,11 @@ package application #cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c #cgo LDFLAGS: -framework Cocoa -framework WebKit -#include "application_darwin.h" -#include "webview_window_darwin.h" -#include -#include "Cocoa/Cocoa.h" -#import -#import -#import "webview_window_darwin_drag.h" - -struct WebviewPreferences { - bool *TabFocusesLinks; - bool *TextInteractionEnabled; - bool *FullscreenEnabled; -}; - -extern void registerListener(unsigned int event); - -// Create a new Window -void* windowNew(unsigned int id, int width, int height, bool fraudulentWebsiteWarningEnabled, bool frameless, bool enableDragAndDrop, struct WebviewPreferences preferences) { - NSWindowStyleMask styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; - if (frameless) { - styleMask = NSWindowStyleMaskBorderless | NSWindowStyleMaskResizable; - } - WebviewWindow* window = [[WebviewWindow alloc] initWithContentRect:NSMakeRect(0, 0, width-1, height-1) - styleMask:styleMask - backing:NSBackingStoreBuffered - defer:NO]; - - // Create delegate - WebviewWindowDelegate* delegate = [[WebviewWindowDelegate alloc] init]; - [delegate autorelease]; - - // Set delegate - [window setDelegate:delegate]; - delegate.windowId = id; - - // Add NSView to window - NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; - [view autorelease]; - - [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - if( frameless ) { - [view setWantsLayer:YES]; - view.layer.cornerRadius = 8.0; - } - [window setContentView:view]; - - // Embed wkwebview in window - NSRect frame = NSMakeRect(0, 0, width, height); - WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init]; - [config autorelease]; - - // Set preferences - if (preferences.TabFocusesLinks != NULL) { - config.preferences.tabFocusesLinks = *preferences.TabFocusesLinks; - } - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110300 - if (@available(macOS 11.3, *)) { - if (preferences.TextInteractionEnabled != NULL) { - config.preferences.textInteractionEnabled = *preferences.TextInteractionEnabled; - } - } -#endif - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 120300 - if (@available(macOS 12.3, *)) { - if (preferences.FullscreenEnabled != NULL) { - config.preferences.elementFullscreenEnabled = *preferences.FullscreenEnabled; - } - } -#endif - - config.suppressesIncrementalRendering = true; - config.applicationNameForUserAgent = @"wails.io"; - [config setURLSchemeHandler:delegate forURLScheme:@"wails"]; - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 - if (@available(macOS 10.15, *)) { - config.preferences.fraudulentWebsiteWarningEnabled = fraudulentWebsiteWarningEnabled; - } -#endif - - // Setup user content controller - WKUserContentController* userContentController = [WKUserContentController new]; - [userContentController autorelease]; - - [userContentController addScriptMessageHandler:delegate name:@"external"]; - config.userContentController = userContentController; - - WKWebView* webView = [[WKWebView alloc] initWithFrame:frame configuration:config]; - [webView autorelease]; - - [view addSubview:webView]; - - // support webview events - [webView setNavigationDelegate:delegate]; - - // Ensure webview resizes with the window - [webView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - if( enableDragAndDrop ) { - WebviewDrag* dragView = [[WebviewDrag alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; - [dragView autorelease]; - - [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [view addSubview:dragView]; - dragView.windowId = id; - } - - window.webView = webView; - return window; -} - - -void printWindowStyle(void *window) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - NSWindowStyleMask styleMask = [nsWindow styleMask]; - // Get delegate - WebviewWindowDelegate* windowDelegate = (WebviewWindowDelegate*)[nsWindow delegate]; - - printf("Window %d style mask: ", windowDelegate.windowId); - - if (styleMask & NSWindowStyleMaskTitled) - { - printf("NSWindowStyleMaskTitled "); - } - - if (styleMask & NSWindowStyleMaskClosable) - { - printf("NSWindowStyleMaskClosable "); - } - - if (styleMask & NSWindowStyleMaskMiniaturizable) - { - printf("NSWindowStyleMaskMiniaturizable "); - } - - if (styleMask & NSWindowStyleMaskResizable) - { - printf("NSWindowStyleMaskResizable "); - } - - if (styleMask & NSWindowStyleMaskFullSizeContentView) - { - printf("NSWindowStyleMaskFullSizeContentView "); - } - - if (styleMask & NSWindowStyleMaskNonactivatingPanel) - { - printf("NSWindowStyleMaskNonactivatingPanel "); - } - - if (styleMask & NSWindowStyleMaskFullScreen) - { - printf("NSWindowStyleMaskFullScreen "); - } - - if (styleMask & NSWindowStyleMaskBorderless) - { - printf("MSWindowStyleMaskBorderless "); - } - - printf("\n"); -} - - -// setInvisibleTitleBarHeight sets the invisible title bar height -void setInvisibleTitleBarHeight(void* window, unsigned int height) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - // Get delegate - WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[nsWindow delegate]; - // Set height - delegate.invisibleTitleBarHeight = height; -} - -// Make NSWindow transparent -void windowSetTransparent(void* nsWindow) { - [(WebviewWindow*)nsWindow setOpaque:NO]; - [(WebviewWindow*)nsWindow setBackgroundColor:[NSColor clearColor]]; -} - -void windowSetInvisibleTitleBar(void* nsWindow, unsigned int height) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[window delegate]; - delegate.invisibleTitleBarHeight = height; -} - - -// Set the title of the NSWindow -void windowSetTitle(void* nsWindow, char* title) { - NSString* nsTitle = [NSString stringWithUTF8String:title]; - [(WebviewWindow*)nsWindow setTitle:nsTitle]; - free(title); -} - -// Set the size of the NSWindow -void windowSetSize(void* nsWindow, int width, int height) { - // Set window size on main thread - WebviewWindow* window = (WebviewWindow*)nsWindow; - NSSize contentSize = [window contentRectForFrameRect:NSMakeRect(0, 0, width, height)].size; - [window setContentSize:contentSize]; - [window setFrame:NSMakeRect(window.frame.origin.x, window.frame.origin.y, width, height) display:YES animate:YES]; -} - -// Set NSWindow always on top -void windowSetAlwaysOnTop(void* nsWindow, bool alwaysOnTop) { - // Set window always on top on main thread - [(WebviewWindow*)nsWindow setLevel:alwaysOnTop ? NSFloatingWindowLevel : NSNormalWindowLevel]; -} - -void setNormalWindowLevel(void* nsWindow) { [(WebviewWindow*)nsWindow setLevel:NSNormalWindowLevel]; } -void setFloatingWindowLevel(void* nsWindow) { [(WebviewWindow*)nsWindow setLevel:NSFloatingWindowLevel];} -void setPopUpMenuWindowLevel(void* nsWindow) { [(WebviewWindow*)nsWindow setLevel:NSPopUpMenuWindowLevel]; } -void setMainMenuWindowLevel(void* nsWindow) { [(WebviewWindow*)nsWindow setLevel:NSMainMenuWindowLevel]; } -void setStatusWindowLevel(void* nsWindow) { [(WebviewWindow*)nsWindow setLevel:NSStatusWindowLevel]; } -void setModalPanelWindowLevel(void* nsWindow) { [(WebviewWindow*)nsWindow setLevel:NSModalPanelWindowLevel]; } -void setScreenSaverWindowLevel(void* nsWindow) { [(WebviewWindow*)nsWindow setLevel:NSScreenSaverWindowLevel]; } -void setTornOffMenuWindowLevel(void* nsWindow) { [(WebviewWindow*)nsWindow setLevel:NSTornOffMenuWindowLevel]; } - -// Load URL in NSWindow -void navigationLoadURL(void* nsWindow, char* url) { - // Load URL on main thread - NSURL* nsURL = [NSURL URLWithString:[NSString stringWithUTF8String:url]]; - NSURLRequest* request = [NSURLRequest requestWithURL:nsURL]; - WebviewWindow* window = (WebviewWindow*)nsWindow; - [window.webView loadRequest:request]; - free(url); -} - -// Set NSWindow resizable -void windowSetResizable(void* nsWindow, bool resizable) { - // Set window resizable on main thread - WebviewWindow* window = (WebviewWindow*)nsWindow; - if (resizable) { - NSWindowStyleMask styleMask = [window styleMask] | NSWindowStyleMaskResizable; - [window setStyleMask:styleMask]; - } else { - NSWindowStyleMask styleMask = [window styleMask] & ~NSWindowStyleMaskResizable; - [window setStyleMask:styleMask]; - } -} - -// Set NSWindow min size -void windowSetMinSize(void* nsWindow, int width, int height) { - // Set window min size on main thread - WebviewWindow* window = (WebviewWindow*)nsWindow; - NSSize contentSize = [window contentRectForFrameRect:NSMakeRect(0, 0, width, height)].size; - [window setContentMinSize:contentSize]; - NSSize size = { width, height }; - [window setMinSize:size]; -} - -// Set NSWindow max size -void windowSetMaxSize(void* nsWindow, int width, int height) { - // Set window max size on main thread - NSSize size = { FLT_MAX, FLT_MAX }; - size.width = width > 0 ? width : FLT_MAX; - size.height = height > 0 ? height : FLT_MAX; - WebviewWindow* window = (WebviewWindow*)nsWindow; - NSSize contentSize = [window contentRectForFrameRect:NSMakeRect(0, 0, size.width, size.height)].size; - [window setContentMaxSize:contentSize]; - [window setMaxSize:size]; -} - -// windowZoomReset -void windowZoomReset(void* nsWindow) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - [window.webView setMagnification:1.0]; -} - -// windowZoomSet -void windowZoomSet(void* nsWindow, double zoom) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - // Reset zoom - [window.webView setMagnification:zoom]; -} - -// windowZoomGet -float windowZoomGet(void* nsWindow) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - // Get zoom - return [window.webView magnification]; -} - -// windowZoomIn -void windowZoomIn(void* nsWindow) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - // Zoom in - [window.webView setMagnification:window.webView.magnification + 0.05]; -} - -// windowZoomOut -void windowZoomOut(void* nsWindow) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - // Zoom out - if( window.webView.magnification > 1.05 ) { - [window.webView setMagnification:window.webView.magnification - 0.05]; - } else { - [window.webView setMagnification:1.0]; - } -} - -// set the window position relative to the screen -void windowSetRelativePosition(void* nsWindow, int x, int y) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - NSScreen* screen = [window screen]; - if( screen == NULL ) { - screen = [NSScreen mainScreen]; - } - NSRect windowFrame = [window frame]; - NSRect screenFrame = [screen frame]; - windowFrame.origin.x = screenFrame.origin.x + (float)x; - windowFrame.origin.y = (screenFrame.origin.y + screenFrame.size.height) - windowFrame.size.height - (float)y; - - [window setFrame:windowFrame display:TRUE animate:FALSE]; -} - -// Execute JS in NSWindow -void windowExecJS(void* nsWindow, const char* js) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - [window.webView evaluateJavaScript:[NSString stringWithUTF8String:js] completionHandler:nil]; - free((void*)js); -} - -// Make NSWindow backdrop translucent -void windowSetTranslucent(void* nsWindow) { - // Get window - WebviewWindow* window = (WebviewWindow*)nsWindow; - - id contentView = [window contentView]; - NSVisualEffectView *effectView = [NSVisualEffectView alloc]; - NSRect bounds = [contentView bounds]; - [effectView initWithFrame:bounds]; - [effectView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; - [effectView setState:NSVisualEffectStateActive]; - [contentView addSubview:effectView positioned:NSWindowBelow relativeTo:nil]; -} - -// Make webview background transparent -void webviewSetTransparent(void* nsWindow) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - // Set webview background transparent - [window.webView setValue:@NO forKey:@"drawsBackground"]; -} - -// Set webview background colour -void webviewSetBackgroundColour(void* nsWindow, int r, int g, int b, int alpha) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - // Set webview background color - [window.webView setValue:[NSColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:alpha/255.0] forKey:@"backgroundColor"]; -} - -// Set the window background colour -void windowSetBackgroundColour(void* nsWindow, int r, int g, int b, int alpha) { - [(WebviewWindow*)nsWindow setBackgroundColor:[NSColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:alpha/255.0]]; -} - -bool windowIsMaximised(void* nsWindow) { - return [(WebviewWindow*)nsWindow isZoomed]; -} - -bool windowIsFullscreen(void* nsWindow) { - return [(WebviewWindow*)nsWindow styleMask] & NSWindowStyleMaskFullScreen; -} - -bool windowIsMinimised(void* nsWindow) { - return [(WebviewWindow*)nsWindow isMiniaturized]; -} +#include "webview_window_bindings_darwin.h" -bool windowIsFocused(void* nsWindow) { - return [(WebviewWindow*)nsWindow isKeyWindow]; +void *windowNew(unsigned int id, int width, int height, bool fraudulentWebsiteWarningEnabled, bool frameless, bool enableDragAndDrop, struct WebviewPreferences preferences) { + return createWindow(WindowTypeWindow, id, width, height, fraudulentWebsiteWarningEnabled, frameless, enableDragAndDrop, preferences); } - -// Set Window fullscreen -void windowFullscreen(void* nsWindow) { - if( windowIsFullscreen(nsWindow) ) { - return; - } - dispatch_async(dispatch_get_main_queue(), ^{ - [(WebviewWindow*)nsWindow toggleFullScreen:nil]; - });} - -void windowUnFullscreen(void* nsWindow) { - if( !windowIsFullscreen(nsWindow) ) { - return; - } - dispatch_async(dispatch_get_main_queue(), ^{ - [(WebviewWindow*)nsWindow toggleFullScreen:nil]; - }); -} - -// restore window to normal size -void windowRestore(void* nsWindow) { - // If window is fullscreen - if([(WebviewWindow*)nsWindow styleMask] & NSWindowStyleMaskFullScreen) { - [(WebviewWindow*)nsWindow toggleFullScreen:nil]; - } - // If window is maximised - if([(WebviewWindow*)nsWindow isZoomed]) { - [(WebviewWindow*)nsWindow zoom:nil]; - } - // If window in minimised - if([(WebviewWindow*)nsWindow isMiniaturized]) { - [(WebviewWindow*)nsWindow deminiaturize:nil]; - } -} - -// disable window fullscreen button -void setFullscreenButtonEnabled(void* nsWindow, bool enabled) { - NSButton *fullscreenButton = [(WebviewWindow*)nsWindow standardWindowButton:NSWindowZoomButton]; - fullscreenButton.enabled = enabled; -} - -// Set the titlebar style -void windowSetTitleBarAppearsTransparent(void* nsWindow, bool transparent) { - if( transparent ) { - [(WebviewWindow*)nsWindow setTitlebarAppearsTransparent:true]; - } else { - [(WebviewWindow*)nsWindow setTitlebarAppearsTransparent:false]; - } -} - -// Set window fullsize content view -void windowSetFullSizeContent(void* nsWindow, bool fullSize) { - if( fullSize ) { - [(WebviewWindow*)nsWindow setStyleMask:[(WebviewWindow*)nsWindow styleMask] | NSWindowStyleMaskFullSizeContentView]; - } else { - [(WebviewWindow*)nsWindow setStyleMask:[(WebviewWindow*)nsWindow styleMask] & ~NSWindowStyleMaskFullSizeContentView]; - } -} - -// Set Hide Titlebar -void windowSetHideTitleBar(void* nsWindow, bool hideTitlebar) { - if( hideTitlebar ) { - [(WebviewWindow*)nsWindow setStyleMask:[(WebviewWindow*)nsWindow styleMask] & ~NSWindowStyleMaskTitled]; - } else { - [(WebviewWindow*)nsWindow setStyleMask:[(WebviewWindow*)nsWindow styleMask] | NSWindowStyleMaskTitled]; - } -} - -// Set Hide Title in Titlebar -void windowSetHideTitle(void* nsWindow, bool hideTitle) { - if( hideTitle ) { - [(WebviewWindow*)nsWindow setTitleVisibility:NSWindowTitleHidden]; - } else { - [(WebviewWindow*)nsWindow setTitleVisibility:NSWindowTitleVisible]; - } -} - -// Set Window use toolbar -void windowSetUseToolbar(void* nsWindow, bool useToolbar) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - if( useToolbar ) { - NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"wails.toolbar"]; - [toolbar autorelease]; - [window setToolbar:toolbar]; - } else { - [window setToolbar:nil]; - } -} - -// Set window toolbar style -void windowSetToolbarStyle(void* nsWindow, int style) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 - if (@available(macOS 11.0, *)) { - NSToolbar* toolbar = [window toolbar]; - if ( toolbar == nil ) { - return; - } - [window setToolbarStyle:style]; - } -#endif - -} -// Set Hide Toolbar Separator -void windowSetHideToolbarSeparator(void* nsWindow, bool hideSeparator) { - NSToolbar* toolbar = [(WebviewWindow*)nsWindow toolbar]; - if( toolbar == nil ) { - return; - } - [toolbar setShowsBaselineSeparator:!hideSeparator]; -} - -// Configure the toolbar auto-hide feature -void windowSetShowToolbarWhenFullscreen(void* window, bool setting) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - // Get delegate - WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[nsWindow delegate]; - // Set height - delegate.showToolbarWhenFullscreen = setting; -} - -// Set Window appearance type -void windowSetAppearanceTypeByName(void* nsWindow, const char *appearanceName) { - // set window appearance type by name - // Convert appearance name to NSString - NSString* appearanceNameString = [NSString stringWithUTF8String:appearanceName]; - // Set appearance - [(WebviewWindow*)nsWindow setAppearance:[NSAppearance appearanceNamed:appearanceNameString]]; - - free((void*)appearanceName); -} - -// Center window on current monitor -void windowCenter(void* nsWindow) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - NSScreen* screen = [window screen]; - if (screen == NULL) { - screen = [NSScreen mainScreen]; - } - - NSRect screenFrame = [screen frame]; - NSRect windowFrame = [window frame]; - - CGFloat x = screenFrame.origin.x + (screenFrame.size.width - windowFrame.size.width) / 2; - CGFloat y = screenFrame.origin.y + (screenFrame.size.height - windowFrame.size.height) / 2; - - [window setFrame:NSMakeRect(x, y, windowFrame.size.width, windowFrame.size.height) display:YES]; -} - - -// Get the current size of the window -void windowGetSize(void* nsWindow, int* width, int* height) { - NSRect frame = [(WebviewWindow*)nsWindow frame]; - *width = frame.size.width; - *height = frame.size.height; -} - -// Get window width -int windowGetWidth(void* nsWindow) { - return [(WebviewWindow*)nsWindow frame].size.width; -} - -// Get window height -int windowGetHeight(void* nsWindow) { - return [(WebviewWindow*)nsWindow frame].size.height; -} - -// Get window position -void windowGetRelativePosition(void* nsWindow, int* x, int* y) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - NSRect frame = [window frame]; - *x = frame.origin.x; - - // Translate to screen coordinates so Y=0 is the top of the screen - NSScreen* screen = [window screen]; - if( screen == NULL ) { - screen = [NSScreen mainScreen]; - } - NSRect screenFrame = [screen frame]; - *y = screenFrame.size.height - frame.origin.y - frame.size.height; -} - -// Get absolute window position -void windowGetPosition(void* nsWindow, int* x, int* y) { - NSRect frame = [(WebviewWindow*)nsWindow frame]; - *x = frame.origin.x; - *y = frame.origin.y; -} - -void windowSetPosition(void* nsWindow, int x, int y) { - WebviewWindow* window = (WebviewWindow*)nsWindow; - NSScreen* screen = [window screen]; - if (screen == NULL) { - screen = [NSScreen mainScreen]; - } - NSRect frame = [window frame]; - frame.origin.x = x; - frame.origin.y = (screen.frame.size.height - frame.size.height) - y; - [window setFrame:frame display:YES]; -} - - -// Destroy window -void windowDestroy(void* nsWindow) { - [(WebviewWindow*)nsWindow close]; -} - -// Remove drop shadow from window -void windowSetShadow(void* nsWindow, bool hasShadow) { - [(WebviewWindow*)nsWindow setHasShadow:hasShadow]; -} - - -// windowClose closes the current window -static void windowClose(void *window) { - [(WebviewWindow*)window close]; -} - -// windowZoom -static void windowZoom(void *window) { - [(WebviewWindow*)window zoom:nil]; -} - -// webviewRenderHTML renders the given HTML -static void windowRenderHTML(void *window, const char *html) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - // get window delegate - WebviewWindowDelegate* windowDelegate = (WebviewWindowDelegate*)[nsWindow delegate]; - // render html - [nsWindow.webView loadHTMLString:[NSString stringWithUTF8String:html] baseURL:nil]; -} - -static void windowInjectCSS(void *window, const char *css) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - // inject css - [nsWindow.webView evaluateJavaScript:[NSString stringWithFormat:@"(function() { var style = document.createElement('style'); style.appendChild(document.createTextNode('%@')); document.head.appendChild(style); })();", [NSString stringWithUTF8String:css]] completionHandler:nil]; - free((void*)css); -} - -static void windowMinimise(void *window) { - [(WebviewWindow*)window miniaturize:nil]; -} - -// zoom maximizes the window to the screen dimensions -static void windowMaximise(void *window) { - [(WebviewWindow*)window zoom:nil]; -} - -static bool isFullScreen(void *window) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - long mask = [nsWindow styleMask]; - return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; -} - -static bool isVisible(void *window) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - return (nsWindow.occlusionState & NSWindowOcclusionStateVisible) == NSWindowOcclusionStateVisible; -} - -// windowSetFullScreen -static void windowSetFullScreen(void *window, bool fullscreen) { - if (isFullScreen(window)) { - return; - } - WebviewWindow* nsWindow = (WebviewWindow*)window; - windowSetMaxSize(nsWindow, 0, 0); - windowSetMinSize(nsWindow, 0, 0); - [nsWindow toggleFullScreen:nil]; -} - -// windowUnminimise -static void windowUnminimise(void *window) { - [(WebviewWindow*)window deminiaturize:nil]; -} - -// windowUnmaximise -static void windowUnmaximise(void *window) { - [(WebviewWindow*)window zoom:nil]; -} - -static void windowDisableSizeConstraints(void *window) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - // disable size constraints - [nsWindow setContentMinSize:CGSizeZero]; - [nsWindow setContentMaxSize:CGSizeZero]; -} - -static void windowShow(void *window) { - [(WebviewWindow*)window makeKeyAndOrderFront:nil]; -} - -static void windowHide(void *window) { - [(WebviewWindow*)window orderOut:nil]; -} - -// setButtonState sets the state of the given button -// 0 = enabled -// 1 = disabled -// 2 = hidden -static void setButtonState(void *button, int state) { - if (button == nil) { - return; - } - NSButton *nsbutton = (NSButton*)button; - nsbutton.hidden = state == 2; - nsbutton.enabled = state != 1; -} - -// setMinimiseButtonState sets the minimise button state -static void setMinimiseButtonState(void *window, int state) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - NSButton *minimiseButton = [nsWindow standardWindowButton:NSWindowMiniaturizeButton]; - setButtonState(minimiseButton, state); -} - -// setMaximiseButtonState sets the maximise button state -static void setMaximiseButtonState(void *window, int state) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - NSButton *maximiseButton = [nsWindow standardWindowButton:NSWindowZoomButton]; - setButtonState(maximiseButton, state); -} - -// setCloseButtonState sets the close button state -static void setCloseButtonState(void *window, int state) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - NSButton *closeButton = [nsWindow standardWindowButton:NSWindowCloseButton]; - setButtonState(closeButton, state); -} - -// windowShowMenu opens an NSMenu at the given coordinates -static void windowShowMenu(void *window, void *menu, int x, int y) { - NSMenu* nsMenu = (NSMenu*)menu; - WKWebView* webView = ((WebviewWindow*)window).webView; - NSPoint point = NSMakePoint(x, y); - [nsMenu popUpMenuPositioningItem:nil atLocation:point inView:webView]; -} - -// Make the given window frameless -static void windowSetFrameless(void *window, bool frameless) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - // set the window style to be frameless - if (frameless) { - [nsWindow setStyleMask:([nsWindow styleMask] | NSWindowStyleMaskFullSizeContentView)]; - } else { - [nsWindow setStyleMask:([nsWindow styleMask] & ~NSWindowStyleMaskFullSizeContentView)]; - } -} - -static void startDrag(void *window) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - - // Get delegate - WebviewWindowDelegate* windowDelegate = (WebviewWindowDelegate*)[nsWindow delegate]; - - // start drag - [windowDelegate startDrag:nsWindow]; -} - -// Credit: https://stackoverflow.com/q/33319295 -static void windowPrint(void *window) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 - // Check if macOS 11.0 or newer - if (@available(macOS 11.0, *)) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - WebviewWindowDelegate* windowDelegate = (WebviewWindowDelegate*)[nsWindow delegate]; - WKWebView* webView = nsWindow.webView; - - // TODO: Think about whether to expose this as config - NSPrintInfo *pInfo = [NSPrintInfo sharedPrintInfo]; - pInfo.horizontalPagination = NSPrintingPaginationModeAutomatic; - pInfo.verticalPagination = NSPrintingPaginationModeAutomatic; - pInfo.verticallyCentered = YES; - pInfo.horizontallyCentered = YES; - pInfo.orientation = NSPaperOrientationLandscape; - pInfo.leftMargin = 30; - pInfo.rightMargin = 30; - pInfo.topMargin = 30; - pInfo.bottomMargin = 30; - - NSPrintOperation *po = [webView printOperationWithPrintInfo:pInfo]; - po.showsPrintPanel = YES; - po.showsProgressPanel = YES; - - // Without the next line you get an exception. Also it seems to - // completely ignore the values in the rect. I tried changing them - // in both x and y direction to include content scrolled off screen. - // It had no effect whatsoever in either direction. - po.view.frame = webView.bounds; - - // [printOperation runOperation] DOES NOT WORK WITH WKWEBVIEW, use - [po runOperationModalForWindow:window delegate:windowDelegate didRunSelector:nil contextInfo:nil]; - } -#endif -} - -void setWindowEnabled(void *window, bool enabled) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - [nsWindow setIgnoresMouseEvents:!enabled]; -} - -void windowSetEnabled(void *window, bool enabled) { - // TODO: Implement -} - -void windowFocus(void *window) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - // If the current application is not active, activate it - if (![[NSApplication sharedApplication] isActive]) { - [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; - } - [nsWindow makeKeyAndOrderFront:nil]; - [nsWindow makeKeyWindow]; -} - -static bool isIgnoreMouseEvents(void *nsWindow) { - NSWindow *window = (__bridge NSWindow *)nsWindow; - return [window ignoresMouseEvents]; -} - -static void setIgnoreMouseEvents(void *nsWindow, bool ignore) { - NSWindow *window = (__bridge NSWindow *)nsWindow; - [window setIgnoresMouseEvents:ignore]; -} - */ import "C" import ( @@ -991,7 +198,7 @@ func (w *macosWebviewWindow) isMaximised() bool { func (w *macosWebviewWindow) isFullscreen() bool { return w.syncMainThreadReturningBool(func() bool { - return bool(C.windowIsFullscreen(w.nsWindow)) + return bool(C.windowIsFullScreen(w.nsWindow)) }) } @@ -1000,7 +207,7 @@ func (w *macosWebviewWindow) isNormal() bool { } func (w *macosWebviewWindow) isVisible() bool { - return bool(C.isVisible(w.nsWindow)) + return bool(C.windowIsVisible(w.nsWindow)) } func (w *macosWebviewWindow) syncMainThreadReturningBool(fn func() bool) bool { @@ -1036,12 +243,16 @@ func (w *macosWebviewWindow) execJS(js string) { if w.nsWindow == nil { return } - C.windowExecJS(w.nsWindow, C.CString(js)) + cJS := C.CString(js) + defer C.free(unsafe.Pointer(cJS)) + C.windowExecJS(w.nsWindow, cJS) }) } func (w *macosWebviewWindow) setURL(uri string) { - C.navigationLoadURL(w.nsWindow, C.CString(uri)) + cURI := C.CString(uri) + defer C.free(unsafe.Pointer(cURI)) + C.navigationLoadURL(w.nsWindow, cURI) } func (w *macosWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) { @@ -1061,6 +272,7 @@ func newWindowImpl(parent *WebviewWindow) *macosWebviewWindow { func (w *macosWebviewWindow) setTitle(title string) { if !w.parent.options.Frameless { cTitle := C.CString(title) + defer C.free(unsafe.Pointer(cTitle)) C.windowSetTitle(w.nsWindow, cTitle) } } @@ -1183,130 +395,139 @@ func (w *macosWebviewWindow) run() { C.bool(options.EnableDragAndDrop), w.getWebviewPreferences(), ) - w.setTitle(options.Title) - w.setResizable(!options.DisableResize) - if options.MinWidth != 0 || options.MinHeight != 0 { - w.setMinSize(options.MinWidth, options.MinHeight) - } - if options.MaxWidth != 0 || options.MaxHeight != 0 { - w.setMaxSize(options.MaxWidth, options.MaxHeight) - } - //w.setZoom(options.Zoom) - w.enableDevTools() - - w.setBackgroundColour(options.BackgroundColour) - - switch macOptions.Backdrop { - case MacBackdropTransparent: - C.windowSetTransparent(w.nsWindow) - C.webviewSetTransparent(w.nsWindow) - case MacBackdropTranslucent: - C.windowSetTranslucent(w.nsWindow) - C.webviewSetTransparent(w.nsWindow) - case MacBackdropNormal: - } - if macOptions.WindowLevel == "" { - macOptions.WindowLevel = MacWindowLevelNormal - } - w.setWindowLevel(macOptions.WindowLevel) - - // Initialise the window buttons - w.setMinimiseButtonState(options.MinimiseButtonState) - w.setMaximiseButtonState(options.MaximiseButtonState) - w.setCloseButtonState(options.CloseButtonState) - - // Ignore mouse events if requested - w.setIgnoreMouseEvents(options.IgnoreMouseEvents) - - titleBarOptions := macOptions.TitleBar - if !w.parent.options.Frameless { - C.windowSetTitleBarAppearsTransparent(w.nsWindow, C.bool(titleBarOptions.AppearsTransparent)) - C.windowSetHideTitleBar(w.nsWindow, C.bool(titleBarOptions.Hide)) - C.windowSetHideTitle(w.nsWindow, C.bool(titleBarOptions.HideTitle)) - C.windowSetFullSizeContent(w.nsWindow, C.bool(titleBarOptions.FullSizeContent)) - C.windowSetUseToolbar(w.nsWindow, C.bool(titleBarOptions.UseToolbar)) - C.windowSetToolbarStyle(w.nsWindow, C.int(titleBarOptions.ToolbarStyle)) - C.windowSetShowToolbarWhenFullscreen(w.nsWindow, C.bool(titleBarOptions.ShowToolbarWhenFullscreen)) - C.windowSetHideToolbarSeparator(w.nsWindow, C.bool(titleBarOptions.HideToolbarSeparator)) - } + w.setup(&options, &macOptions) + }) +} - if macOptions.Appearance != "" { - C.windowSetAppearanceTypeByName(w.nsWindow, C.CString(string(macOptions.Appearance))) - } +func (w *macosWebviewWindow) setup(options *WebviewWindowOptions, macOptions *MacWindow) { + w.setTitle(options.Title) - if macOptions.InvisibleTitleBarHeight != 0 { - C.windowSetInvisibleTitleBar(w.nsWindow, C.uint(macOptions.InvisibleTitleBarHeight)) - } + w.setResizable(!options.DisableResize) + if options.MinWidth != 0 || options.MinHeight != 0 { + w.setMinSize(options.MinWidth, options.MinHeight) + } + if options.MaxWidth != 0 || options.MaxHeight != 0 { + w.setMaxSize(options.MaxWidth, options.MaxHeight) + } + //w.setZoom(options.Zoom) + w.enableDevTools() - switch w.parent.options.StartState { - case WindowStateMaximised: - w.maximise() - case WindowStateMinimised: - w.minimise() - case WindowStateFullscreen: - w.fullscreen() - case WindowStateNormal: - } - if w.parent.options.InitialPosition == WindowCentered { - C.windowCenter(w.nsWindow) - } else { - w.setPosition(options.X, options.Y) - } + w.setBackgroundColour(options.BackgroundColour) - startURL, err := assetserver.GetStartURL(options.URL) - if err != nil { - globalApplication.fatal(err.Error()) - } + switch macOptions.Backdrop { + case MacBackdropTransparent: + C.windowSetTransparent(w.nsWindow) + C.webviewSetTransparent(w.nsWindow) + case MacBackdropTranslucent: + C.windowSetTranslucent(w.nsWindow) + C.webviewSetTransparent(w.nsWindow) + case MacBackdropNormal: + } - w.setURL(startURL) - - // We need to wait for the HTML to load before we can execute the javascript - w.parent.OnWindowEvent(events.Mac.WebViewDidFinishNavigation, func(_ *WindowEvent) { - InvokeAsync(func() { - if options.JS != "" { - w.execJS(options.JS) - } - if options.CSS != "" { - C.windowInjectCSS(w.nsWindow, C.CString(options.CSS)) - } - if !options.Hidden { - w.parent.Show() - w.setHasShadow(!options.Mac.DisableShadow) - w.setAlwaysOnTop(options.AlwaysOnTop) - } else { - // We have to wait until the window is shown before we can remove the shadow - var cancel func() - cancel = w.parent.OnWindowEvent(events.Mac.WindowDidBecomeKey, func(_ *WindowEvent) { - w.setHasShadow(!options.Mac.DisableShadow) - w.setAlwaysOnTop(options.AlwaysOnTop) - cancel() - }) - } - }) - }) + if macOptions.WindowLevel == "" { + macOptions.WindowLevel = MacWindowLevelNormal + } + w.setWindowLevel(macOptions.WindowLevel) - // Translate ShouldClose to common WindowClosing event - w.parent.OnWindowEvent(events.Mac.WindowShouldClose, func(_ *WindowEvent) { - w.parent.emit(events.Common.WindowClosing) - }) + // Initialise the window buttons + w.setMinimiseButtonState(options.MinimiseButtonState) + w.setMaximiseButtonState(options.MaximiseButtonState) + w.setCloseButtonState(options.CloseButtonState) - // Translate WindowDidResignKey to common WindowLostFocus event - w.parent.OnWindowEvent(events.Mac.WindowDidResignKey, func(_ *WindowEvent) { - w.parent.emit(events.Common.WindowLostFocus) - }) - w.parent.OnWindowEvent(events.Mac.WindowDidResignMain, func(_ *WindowEvent) { - w.parent.emit(events.Common.WindowLostFocus) - }) - w.parent.OnWindowEvent(events.Mac.WindowDidResize, func(_ *WindowEvent) { - w.parent.emit(events.Common.WindowDidResize) + // Ignore mouse events if requested + w.setIgnoreMouseEvents(options.IgnoreMouseEvents) + + titleBarOptions := macOptions.TitleBar + if !w.parent.options.Frameless { + C.windowSetTitleBarAppearsTransparent(w.nsWindow, C.bool(titleBarOptions.AppearsTransparent)) + C.windowSetHideTitleBar(w.nsWindow, C.bool(titleBarOptions.Hide)) + C.windowSetHideTitle(w.nsWindow, C.bool(titleBarOptions.HideTitle)) + C.windowSetFullSizeContent(w.nsWindow, C.bool(titleBarOptions.FullSizeContent)) + C.windowSetUseToolbar(w.nsWindow, C.bool(titleBarOptions.UseToolbar)) + C.windowSetToolbarStyle(w.nsWindow, C.int(titleBarOptions.ToolbarStyle)) + C.windowSetShowToolbarWhenFullscreen(w.nsWindow, C.bool(titleBarOptions.ShowToolbarWhenFullscreen)) + C.windowSetHideToolbarSeparator(w.nsWindow, C.bool(titleBarOptions.HideToolbarSeparator)) + } + + if macOptions.Appearance != "" { + cAppearance := C.CString(string(macOptions.Appearance)) + defer C.free(unsafe.Pointer(cAppearance)) + C.windowSetAppearanceTypeByName(w.nsWindow, cAppearance) + } + + if macOptions.InvisibleTitleBarHeight != 0 { + C.windowSetInvisibleTitleBar(w.nsWindow, C.uint(macOptions.InvisibleTitleBarHeight)) + } + + switch w.parent.options.StartState { + case WindowStateMaximised: + w.maximise() + case WindowStateMinimised: + w.minimise() + case WindowStateFullscreen: + w.fullscreen() + case WindowStateNormal: + } + if w.parent.options.InitialPosition == WindowCentered { + C.windowCenter(w.nsWindow) + } else { + w.setPosition(options.X, options.Y) + } + + startURL, err := assetserver.GetStartURL(options.URL) + if err != nil { + globalApplication.fatal(err.Error()) + } + + w.setURL(startURL) + + // We need to wait for the HTML to load before we can execute the javascript + w.parent.OnWindowEvent(events.Mac.WebViewDidFinishNavigation, func(_ *WindowEvent) { + InvokeAsync(func() { + if options.JS != "" { + w.execJS(options.JS) + } + if options.CSS != "" { + cCSS := C.CString(options.CSS) + C.windowInjectCSS(w.nsWindow, cCSS) + C.free(unsafe.Pointer(cCSS)) + } + if !options.Hidden { + w.parent.Show() + w.setHasShadow(!options.Mac.DisableShadow) + w.setAlwaysOnTop(options.AlwaysOnTop) + } else { + // We have to wait until the window is shown before we can remove the shadow + var cancel func() + cancel = w.parent.OnWindowEvent(events.Mac.WindowDidBecomeKey, func(_ *WindowEvent) { + w.setHasShadow(!options.Mac.DisableShadow) + w.setAlwaysOnTop(options.AlwaysOnTop) + cancel() + }) + } }) + }) - if options.HTML != "" { - w.setHTML(options.HTML) - } + // Translate ShouldClose to common WindowClosing event + w.parent.OnWindowEvent(events.Mac.WindowShouldClose, func(_ *WindowEvent) { + w.parent.emit(events.Common.WindowClosing) + }) + // Translate WindowDidResignKey to common WindowLostFocus event + w.parent.OnWindowEvent(events.Mac.WindowDidResignKey, func(_ *WindowEvent) { + w.parent.emit(events.Common.WindowLostFocus) }) + w.parent.OnWindowEvent(events.Mac.WindowDidResignMain, func(_ *WindowEvent) { + w.parent.emit(events.Common.WindowLostFocus) + }) + w.parent.OnWindowEvent(events.Mac.WindowDidResize, func(_ *WindowEvent) { + w.parent.emit(events.Common.WindowDidResize) + }) + + if options.HTML != "" { + w.setHTML(options.HTML) + } } func (w *macosWebviewWindow) nativeWindowHandle() uintptr { @@ -1376,6 +597,7 @@ func (w *macosWebviewWindow) destroy() { func (w *macosWebviewWindow) setHTML(html string) { // Convert HTML to C string cHTML := C.CString(html) + defer C.free(unsafe.Pointer(cHTML)) // Render HTML C.windowRenderHTML(w.nsWindow, cHTML) } diff --git a/v3/pkg/application/webview_window_darwin.h b/v3/pkg/application/webview_window_darwin.h index 1dfab739594..9474fbdae4b 100644 --- a/v3/pkg/application/webview_window_darwin.h +++ b/v3/pkg/application/webview_window_darwin.h @@ -1,20 +1,18 @@ -//go:build darwin - -#ifndef WebviewWindowDelegate_h -#define WebviewWindowDelegate_h +#ifndef WEBVIEW_WINDOW_DARWIN +#define WEBVIEW_WINDOW_DARWIN #import #import +#include "webview_responder_darwin.h" + +@interface WebviewWindow : NSObject -@interface WebviewWindow : NSWindow -- (BOOL) canBecomeKeyWindow; -- (BOOL) canBecomeMainWindow; -- (BOOL) acceptsFirstResponder; -- (BOOL) becomeFirstResponder; -- (BOOL) resignFirstResponder; -- (WebviewWindow*) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; +@property(assign) NSWindow *w; +@property(assign) WKWebView *webView; // We already retain WKWebView since it's part of the Window. +@property(assign) WebviewResponder *responder; -@property (assign) WKWebView* webView; // We already retain WKWebView since it's part of the Window. +- (WebviewWindow *) initAsWindow:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; +- (WebviewWindow *) initAsPanel:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; @end @@ -32,5 +30,4 @@ @end - -#endif /* WebviewWindowDelegate_h */ +#endif /* WEBVIEW_WINDOW_DARWIN */ diff --git a/v3/pkg/application/webview_window_darwin.m b/v3/pkg/application/webview_window_darwin.m index 00f1eeb0be2..01edcdfc7c3 100644 --- a/v3/pkg/application/webview_window_darwin.m +++ b/v3/pkg/application/webview_window_darwin.m @@ -3,197 +3,44 @@ #import #import "webview_window_darwin.h" #import "../events/events_darwin.h" + extern void processMessage(unsigned int, const char*); extern void processURLRequest(unsigned int, void *); -extern void processWindowKeyDownEvent(unsigned int, const char*); extern bool hasListeners(unsigned int); + @implementation WebviewWindow -- (WebviewWindow*) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; -{ - self = [super initWithContentRect:contentRect styleMask:windowStyle backing:bufferingType defer:deferCreation]; - [self setAlphaValue:1.0]; - [self setBackgroundColor:[NSColor clearColor]]; - [self setOpaque:NO]; - [self setMovableByWindowBackground:YES]; +- (WebviewWindow *) initAsWindow:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation { + self = [super init]; + + self.w = [[NSWindow alloc] initWithContentRect:contentRect styleMask:windowStyle backing:bufferingType defer:deferCreation]; + [self commonInitialization]; + + return self; +} +- (WebviewWindow *) initAsPanel:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation { + self = [super init]; + + self.w = (NSWindow *) [[NSPanel alloc] initWithContentRect:contentRect styleMask:windowStyle backing:bufferingType defer:deferCreation]; + [self commonInitialization]; + return self; } -- (void)keyDown:(NSEvent *)event { - NSUInteger modifierFlags = event.modifierFlags; - // Create an array to hold the modifier strings - NSMutableArray *modifierStrings = [NSMutableArray array]; - // Check for modifier flags and add corresponding strings to the array - if (modifierFlags & NSEventModifierFlagShift) { - [modifierStrings addObject:@"shift"]; - } - if (modifierFlags & NSEventModifierFlagControl) { - [modifierStrings addObject:@"ctrl"]; - } - if (modifierFlags & NSEventModifierFlagOption) { - [modifierStrings addObject:@"option"]; - } - if (modifierFlags & NSEventModifierFlagCommand) { - [modifierStrings addObject:@"cmd"]; - } - NSString *keyString = [self keyStringFromEvent:event]; - if (keyString.length > 0) { - [modifierStrings addObject:keyString]; - } - // Combine the modifier strings with the key character - NSString *keyEventString = [modifierStrings componentsJoinedByString:@"+"]; - const char* utf8String = [keyEventString UTF8String]; - WebviewWindowDelegate *delegate = (WebviewWindowDelegate*)self.delegate; - processWindowKeyDownEvent(delegate.windowId, utf8String); -} -- (NSString *)keyStringFromEvent:(NSEvent *)event { - // Get the pressed key - // Check for special keys like escape and tab - NSString *characters = [event characters]; - if (characters.length == 0) { - return @""; - } - if ([characters isEqualToString:@"\r"]) { - return @"enter"; - } - if ([characters isEqualToString:@"\b"]) { - return @"backspace"; - } - if ([characters isEqualToString:@"\e"]) { - return @"escape"; - } - // page down - if ([characters isEqualToString:@"\x0B"]) { - return @"page down"; - } - // page up - if ([characters isEqualToString:@"\x0E"]) { - return @"page up"; - } - // home - if ([characters isEqualToString:@"\x01"]) { - return @"home"; - } - // end - if ([characters isEqualToString:@"\x04"]) { - return @"end"; - } - // clear - if ([characters isEqualToString:@"\x0C"]) { - return @"clear"; - } - switch ([event keyCode]) { - // Function keys - case 122: return @"f1"; - case 120: return @"f2"; - case 99: return @"f3"; - case 118: return @"f4"; - case 96: return @"f5"; - case 97: return @"f6"; - case 98: return @"f7"; - case 100: return @"f8"; - case 101: return @"f9"; - case 109: return @"f10"; - case 103: return @"f11"; - case 111: return @"f12"; - case 105: return @"f13"; - case 107: return @"f14"; - case 113: return @"f15"; - case 106: return @"f16"; - case 64: return @"f17"; - case 79: return @"f18"; - case 80: return @"f19"; - case 90: return @"f20"; - // Letter keys - case 0: return @"a"; - case 11: return @"b"; - case 8: return @"c"; - case 2: return @"d"; - case 14: return @"e"; - case 3: return @"f"; - case 5: return @"g"; - case 4: return @"h"; - case 34: return @"i"; - case 38: return @"j"; - case 40: return @"k"; - case 37: return @"l"; - case 46: return @"m"; - case 45: return @"n"; - case 31: return @"o"; - case 35: return @"p"; - case 12: return @"q"; - case 15: return @"r"; - case 1: return @"s"; - case 17: return @"t"; - case 32: return @"u"; - case 9: return @"v"; - case 13: return @"w"; - case 7: return @"x"; - case 16: return @"y"; - case 6: return @"z"; - // Number keys - case 29: return @"0"; - case 18: return @"1"; - case 19: return @"2"; - case 20: return @"3"; - case 21: return @"4"; - case 23: return @"5"; - case 22: return @"6"; - case 26: return @"7"; - case 28: return @"8"; - case 25: return @"9"; - // Other special keys - case 51: return @"delete"; - case 117: return @"forward delete"; - case 123: return @"left"; - case 124: return @"right"; - case 126: return @"up"; - case 125: return @"down"; - case 48: return @"tab"; - case 53: return @"escape"; - case 49: return @"space"; - // Punctuation and other keys (for a standard US layout) - case 33: return @"["; - case 30: return @"]"; - case 43: return @","; - case 27: return @"-"; - case 39: return @"'"; - case 44: return @"/"; - case 47: return @"."; - case 41: return @";"; - case 24: return @"="; - case 50: return @"`"; - case 42: return @"\\"; - default: return @""; - } -} -- (BOOL)canBecomeKeyWindow { - return YES; -} -- (BOOL) canBecomeMainWindow { - return YES; -} -- (BOOL) acceptsFirstResponder { - return YES; -} -- (BOOL) becomeFirstResponder { - return YES; -} -- (BOOL) resignFirstResponder { - return YES; -} -- (void) setDelegate:(id) delegate { - [delegate retain]; - [super setDelegate: delegate]; +- (void) commonInitialization { + [self.w setAlphaValue:1.0]; + [self.w setBackgroundColor:[NSColor clearColor]]; + [self.w setOpaque:NO]; + [self.w setMovableByWindowBackground:YES]; + + self.responder = [[WebviewResponder alloc] initAttachToWindow:self.w]; } - (void) dealloc { // Remove the script handler, otherwise WebviewWindowDelegate won't get deallocated // See: https://stackoverflow.com/questions/26383031/wkwebview-causes-my-view-controller-to-leak [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"external"]; - if (self.delegate) { - [self.delegate release]; - } [super dealloc]; } @end + @implementation WebviewWindowDelegate - (BOOL)windowShouldClose:(NSWindow *)sender { processWindowEvent(self.windowId, EventWindowShouldClose); @@ -205,7 +52,7 @@ - (void) dealloc { [super dealloc]; } - (void) startDrag:(WebviewWindow*)window { - [window performWindowDragWithEvent:self.leftMouseEvent]; + [window.w performWindowDragWithEvent:self.leftMouseEvent]; } // Handle script messages from the external bridge - (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message { diff --git a/v3/pkg/application/webview_window_darwin_dev.go b/v3/pkg/application/webview_window_darwin_dev.go index 6ae5fa9981a..6ceb716c90d 100644 --- a/v3/pkg/application/webview_window_darwin_dev.go +++ b/v3/pkg/application/webview_window_darwin_dev.go @@ -55,3 +55,11 @@ func (w *macosWebviewWindow) openDevTools() { func (w *macosWebviewWindow) enableDevTools() { C.windowEnableDevTools(w.nsWindow) } + +func (w *macosWebviewPanel) openDevTools() { + C.openDevTools(w.nsPanel) +} + +func (w *macosWebviewPanel) enableDevTools() { + C.windowEnableDevTools(w.nsPanel) +} diff --git a/v3/pkg/application/window.go b/v3/pkg/application/window.go index 797380e0c74..6604a373aea 100644 --- a/v3/pkg/application/window.go +++ b/v3/pkg/application/window.go @@ -12,6 +12,8 @@ type Callback interface { } type Window interface { + Bounds() Rect + SetBounds(bounds Rect) Callback Center() Close() @@ -22,6 +24,7 @@ type Window interface { EnableSizeConstraints() Error(message string, args ...any) ExecJS(js string) + Flash(enabled bool) Focus() ForceReload() Fullscreen() Window @@ -47,12 +50,14 @@ type Window interface { OnWindowEvent(eventType events.WindowEventType, callback func(event *WindowEvent)) func() OpenContextMenu(data *ContextMenuData) Position() (int, int) + Print() error RegisterContextMenu(name string, menu *Menu) RelativePosition() (int, int) Reload() Resizable() bool Restore() Run() + SetEnabled(enabled bool) SetPosition(x, y int) SetAlwaysOnTop(b bool) Window SetBackgroundColour(colour RGBA) Window