mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-19 02:19:31 +08:00
Merge branch 'v3-alpha-linux' into v3-alpha
This commit is contained in:
commit
eb18f02fd2
@ -4,7 +4,6 @@
|
|||||||
package webview
|
package webview
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@ -76,7 +75,6 @@ func (r *request) Body() (io.ReadCloser, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *request) Response() ResponseWriter {
|
func (r *request) Response() ResponseWriter {
|
||||||
fmt.Println("r.Response()")
|
|
||||||
if r.rw != nil {
|
if r.rw != nil {
|
||||||
return r.rw
|
return r.rw
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,6 @@ func (rw *responseWriter) WriteHeader(code int) {
|
|||||||
contentLength = pLen
|
contentLength = pLen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("content_length", contentLength)
|
|
||||||
// We can't use os.Pipe here, because that returns files with a finalizer for closing the FD. But the control over the
|
// We can't use os.Pipe here, because that returns files with a finalizer for closing the FD. But the control over the
|
||||||
// read FD is given to the InputStream and will be closed there.
|
// read FD is given to the InputStream and will be closed there.
|
||||||
// Furthermore we especially don't want to have the FD_CLOEXEC
|
// Furthermore we especially don't want to have the FD_CLOEXEC
|
||||||
|
132
v3/STATUS.md
132
v3/STATUS.md
@ -14,21 +14,21 @@ Status of features in v3. Incomplete - please add as you see fit.
|
|||||||
|
|
||||||
Application interface methods
|
Application interface methods
|
||||||
|
|
||||||
| Method | Windows | Linux | Mac | Notes |
|
| Method | Windows | Linux | Mac | Notes |
|
||||||
|---------------------------------------------------------------|---------|-------|-----|------------------------------|
|
|---------------------------------------------------------------|---------|-------|-----|-------|
|
||||||
| run() error | Y | Y | Y | |
|
| run() error | Y | Y | Y | |
|
||||||
| destroy() | | Y | Y | |
|
| destroy() | | Y | Y | |
|
||||||
| setApplicationMenu(menu *Menu) | Y | | Y | |
|
| setApplicationMenu(menu *Menu) | Y | Y | Y | |
|
||||||
| name() string | | | Y | |
|
| name() string | | Y | Y | |
|
||||||
| getCurrentWindowID() uint | Y | Y | Y | |
|
| getCurrentWindowID() uint | Y | Y | Y | |
|
||||||
| showAboutDialog(name string, description string, icon []byte) | | Y | Y | [linux] No icon possible yet |
|
| showAboutDialog(name string, description string, icon []byte) | | Y | Y | |
|
||||||
| setIcon(icon []byte) | - | | Y | |
|
| setIcon(icon []byte) | - | Y | Y | |
|
||||||
| on(id uint) | | | Y | |
|
| on(id uint) | | | Y | |
|
||||||
| dispatchOnMainThread(fn func()) | Y | Y | Y | |
|
| dispatchOnMainThread(fn func()) | Y | Y | Y | |
|
||||||
| hide() | Y | | Y | |
|
| hide() | Y | Y | Y | |
|
||||||
| show() | Y | | Y | |
|
| show() | Y | Y | Y | |
|
||||||
| getPrimaryScreen() (*Screen, error) | | Y | Y | |
|
| getPrimaryScreen() (*Screen, error) | | Y | Y | |
|
||||||
| getScreens() ([]*Screen, error) | | Y | Y | |
|
| getScreens() ([]*Screen, error) | | Y | Y | |
|
||||||
|
|
||||||
## Webview Window
|
## Webview Window
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ Webview Window Interface Methods
|
|||||||
|
|
||||||
| Feature | Windows | Linux | Mac | Notes |
|
| Feature | Windows | Linux | Mac | Notes |
|
||||||
|---------|---------|-------|-----|-------|
|
|---------|---------|-------|-----|-------|
|
||||||
| Quit | Y | Y | Y | |
|
| Quit | Y | Y | Y | |
|
||||||
| Hide | Y | | Y | |
|
| Hide | Y | | Y | |
|
||||||
| Show | Y | | Y | |
|
| Show | Y | | Y | |
|
||||||
|
|
||||||
@ -98,10 +98,10 @@ Webview Window Interface Methods
|
|||||||
|
|
||||||
| Feature | Windows | Linux | Mac | Notes |
|
| Feature | Windows | Linux | Mac | Notes |
|
||||||
|----------|---------|-------|-----|-------|
|
|----------|---------|-------|-----|-------|
|
||||||
| Info | Y | | Y | |
|
| Info | Y | Y | Y | |
|
||||||
| Warning | Y | | Y | |
|
| Warning | Y | Y | Y | |
|
||||||
| Error | Y | | Y | |
|
| Error | Y | Y | Y | |
|
||||||
| Question | Y | | Y | |
|
| Question | Y | Y | Y | |
|
||||||
| OpenFile | Y | | Y | |
|
| OpenFile | Y | | Y | |
|
||||||
| SaveFile | Y | | Y | |
|
| SaveFile | Y | | Y | |
|
||||||
|
|
||||||
@ -180,37 +180,40 @@ U = Untested
|
|||||||
A 'Y' in the table below indicates that the option has been tested and is applied when the window is created.
|
A 'Y' in the table below indicates that the option has been tested and is applied when the window is created.
|
||||||
An 'X' indicates that the option is not supported by the platform.
|
An 'X' indicates that the option is not supported by the platform.
|
||||||
|
|
||||||
| Feature | Windows | Linux | Mac | Notes |
|
| Feature | Windows | Linux | Mac | Notes |
|
||||||
|---------------------------------|---------|-------|-----|---------------------------------------------------|
|
|---------------------------------|---------|-------|-----|--------------------------------------------|
|
||||||
| Name | | | | |
|
| AlwaysOnTop | Y | | | |
|
||||||
| Title | Y | | | |
|
| BackgroundColour | Y | Y | | |
|
||||||
| Width | Y | Y | | |
|
| BackgroundType | | | | Acrylic seems to work but the others don't |
|
||||||
| Height | Y | Y | | |
|
| CSS | Y | Y | | |
|
||||||
| AlwaysOnTop | Y | Y | | |
|
| DevToolsEnabled | Y | Y | Y | |
|
||||||
| URL | Y | | | |
|
| DisableResize | Y | Y | | |
|
||||||
| DisableResize | Y | Y | | |
|
| EnableDragAndDrop | | Y | | |
|
||||||
| Frameless | Y | Y | | |
|
| EnableFraudulentWebsiteWarnings | | | | |
|
||||||
| MinWidth | Y | Y | | |
|
| Focused | Y | Y | | |
|
||||||
| MinHeight | Y | Y | | |
|
| Frameless | Y | Y | | |
|
||||||
| MaxWidth | Y | Y | | |
|
| FullscreenButtonEnabled | Y | | | |
|
||||||
| MaxHeight | Y | Y | | |
|
| Height | Y | Y | | |
|
||||||
| StartState | Y | | | |
|
| Hidden | Y | Y | | |
|
||||||
| Mac | - | - | | |
|
| HTML | Y | Y | | |
|
||||||
| BackgroundType | | | | Acrylic seems to work but the others don't |
|
| JS | Y | Y | | |
|
||||||
| BackgroundColour | Y | Y | | |
|
| Mac | - | - | | |
|
||||||
| HTML | Y | Y | | |
|
| MaxHeight | Y | Y | | |
|
||||||
| JS | Y | Y | | |
|
| MaxWidth | Y | Y | | |
|
||||||
| CSS | Y | Y | | |
|
| MinHeight | Y | Y | | |
|
||||||
| X | Y | Y | | |
|
| MinWidth | Y | Y | | |
|
||||||
| Y | Y | Y | | |
|
| Name | Y | Y | | |
|
||||||
| HideOnClose | Y | Y | | |
|
| OpenInspectorOnStartup | | | | |
|
||||||
| FullscreenButtonEnabled | | ? | | [linux] How is this different from DisableResize? |
|
| StartState | Y | | | |
|
||||||
| Hidden | Y | | | |
|
| Title | Y | Y | | |
|
||||||
| EnableFraudulentWebsiteWarnings | | | | |
|
| URL | Y | Y | | |
|
||||||
| Zoom | | Y | | |
|
| Width | Y | Y | | |
|
||||||
| EnableDragAndDrop | Y | Y | | |
|
| Windows | Y | - | - | |
|
||||||
| Windows | Y | - | - | |
|
| X | Y | Y | | |
|
||||||
| Focused | Y | | | |
|
| Y | Y | Y | | |
|
||||||
|
| Zoom | | | | |
|
||||||
|
| ZoomControlEnabled | | | | |
|
||||||
|
|
||||||
|
|
||||||
### Log
|
### Log
|
||||||
|
|
||||||
@ -220,7 +223,7 @@ To log or not to log? System logger vs custom logger.
|
|||||||
|
|
||||||
| Event | Windows | Linux | Mac | Notes |
|
| Event | Windows | Linux | Mac | Notes |
|
||||||
|--------------------------|---------|-------|-----|-------|
|
|--------------------------|---------|-------|-----|-------|
|
||||||
| Default Application Menu | Y | Y | Y | |
|
| Default Application Menu | Y | Y | Y | |
|
||||||
|
|
||||||
## Tray Menus
|
## Tray Menus
|
||||||
|
|
||||||
@ -300,6 +303,8 @@ Built-in plugin support:
|
|||||||
| Start at login | | | Y | |
|
| Start at login | | | Y | |
|
||||||
| Server | | | | |
|
| Server | | | | |
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
|
||||||
- Ensure each plugin has a JS wrapper that can be injected into the window.
|
- Ensure each plugin has a JS wrapper that can be injected into the window.
|
||||||
|
|
||||||
## Packaging
|
## Packaging
|
||||||
@ -318,7 +323,8 @@ Built-in plugin support:
|
|||||||
| Feature | Windows | Linux | Mac | Notes |
|
| Feature | Windows | Linux | Mac | Notes |
|
||||||
|---------|---------|-------|-----|-----------------------------------------------|
|
|---------|---------|-------|-----|-----------------------------------------------|
|
||||||
| Resize | | | | |
|
| Resize | | | | |
|
||||||
| Drag | | Y | | Linux - can always drag with `Alt`+left mouse |
|
| Drag | | Y | | Linux - can always drag with `Meta`+left mouse |
|
||||||
|
|
||||||
|
|
||||||
## Mac Specific
|
## Mac Specific
|
||||||
|
|
||||||
@ -353,6 +359,24 @@ Built-in plugin support:
|
|||||||
|
|
||||||
## Linux Specific
|
## Linux Specific
|
||||||
|
|
||||||
|
|
||||||
|
Implementation details for the functions utilized by the `*_linux.go` files are located in the following files:
|
||||||
|
|
||||||
|
- linux_cgo.go: CGo implementation
|
||||||
|
- linux_purego.go: PureGo implementation
|
||||||
|
|
||||||
|
### CGO
|
||||||
|
|
||||||
|
By default CGO is utilized to compile the Linux port. This prevents easy cross-compilation and so the PureGo implementation is also being simultaneously developed.
|
||||||
|
|
||||||
|
### Purego
|
||||||
|
|
||||||
|
The examples can be compiled using the following command:
|
||||||
|
|
||||||
|
CGO_ENABLED=0 go build -tags purego
|
||||||
|
|
||||||
|
Note: things are currently not working after the refactor
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
| Example | Windows | Linux | Mac |
|
| Example | Windows | Linux | Mac |
|
||||||
@ -381,4 +405,4 @@ Built-in plugin support:
|
|||||||
|
|
||||||
# Beta Release TODO
|
# Beta Release TODO
|
||||||
|
|
||||||
- [ ] Make better looking examples
|
- [ ] Make better looking examples
|
||||||
|
@ -6,6 +6,7 @@ require github.com/wailsapp/wails/v3 v3.0.0-alpha.0
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bep/debounce v1.2.1 // indirect
|
github.com/bep/debounce v1.2.1 // indirect
|
||||||
|
github.com/ebitengine/purego v0.4.0-alpha.4 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
|
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
|
||||||
@ -16,7 +17,7 @@ require (
|
|||||||
github.com/samber/lo v1.37.0 // indirect
|
github.com/samber/lo v1.37.0 // indirect
|
||||||
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b // indirect
|
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b // indirect
|
||||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||||
github.com/wailsapp/wails/v2 v2.3.2-0.20230117193915-45c3a501d9e6 // indirect
|
github.com/wailsapp/wails/v2 v2.5.1 // indirect
|
||||||
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // indirect
|
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // indirect
|
||||||
golang.org/x/net v0.7.0 // indirect
|
golang.org/x/net v0.7.0 // indirect
|
||||||
golang.org/x/sys v0.8.0 // indirect
|
golang.org/x/sys v0.8.0 // indirect
|
||||||
@ -25,3 +26,5 @@ require (
|
|||||||
replace github.com/wailsapp/wails/v3 => ../..
|
replace github.com/wailsapp/wails/v3 => ../..
|
||||||
|
|
||||||
replace github.com/wailsapp/wails/v2 => ../../../v2
|
replace github.com/wailsapp/wails/v2 => ../../../v2
|
||||||
|
|
||||||
|
replace github.com/ebitengine/purego v0.4.0-alpha.4 => github.com/tmclane/purego v0.0.0-20230601213035-1f25e70d7b01
|
||||||
|
@ -26,6 +26,8 @@ github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpo
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
github.com/tmclane/purego v0.0.0-20230601213035-1f25e70d7b01 h1:oQwu3iNDywGp1Hry+PDvz+grwbCGpzY+ckSnWKCnX5Y=
|
||||||
|
github.com/tmclane/purego v0.0.0-20230601213035-1f25e70d7b01/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||||
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b h1:cztK9x+ikg6nFscy5c8NgtfIXv/d0ESdENy9+JkE8i4=
|
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b h1:cztK9x+ikg6nFscy5c8NgtfIXv/d0ESdENy9+JkE8i4=
|
||||||
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
|
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
|
||||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||||
|
@ -3,8 +3,8 @@ module github.com/wailsapp/wails/v3
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ebitengine/purego v0.3.2
|
|
||||||
github.com/bep/debounce v1.2.1
|
github.com/bep/debounce v1.2.1
|
||||||
|
github.com/ebitengine/purego v0.4.0-alpha.4
|
||||||
github.com/go-ole/go-ole v1.2.6
|
github.com/go-ole/go-ole v1.2.6
|
||||||
github.com/go-task/task/v3 v3.20.0
|
github.com/go-task/task/v3 v3.20.0
|
||||||
github.com/google/go-cmp v0.5.9
|
github.com/google/go-cmp v0.5.9
|
||||||
@ -77,4 +77,4 @@ require (
|
|||||||
|
|
||||||
replace github.com/wailsapp/wails/v2 => ../v2
|
replace github.com/wailsapp/wails/v2 => ../v2
|
||||||
|
|
||||||
replace github.com/ebitengine/purego v0.3.2 => github.com/TotallyGamerJet/purego v0.2.0-alpha.0.20230404174033-5655abccca7e
|
replace github.com/ebitengine/purego v0.4.0-alpha.4 => github.com/tmclane/purego v0.0.0-20230601213035-1f25e70d7b01
|
||||||
|
@ -11,8 +11,6 @@ github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzX
|
|||||||
github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c=
|
github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c=
|
||||||
github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
|
github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
|
||||||
github.com/MarvinJWendt/testza v0.5.1 h1:a9Fqx6vQrHQ4CyiaLhktfTTelwGotmFWy8MNhyaohw8=
|
github.com/MarvinJWendt/testza v0.5.1 h1:a9Fqx6vQrHQ4CyiaLhktfTTelwGotmFWy8MNhyaohw8=
|
||||||
github.com/TotallyGamerJet/purego v0.2.0-alpha.0.20230404174033-5655abccca7e h1:wQ7ot+e0mwJYkbomtIX9tU0dOV9lFTmwAgUGqvQTIUg=
|
|
||||||
github.com/TotallyGamerJet/purego v0.2.0-alpha.0.20230404174033-5655abccca7e/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
|
||||||
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
|
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
|
||||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||||
@ -145,6 +143,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/tc-hib/winres v0.1.6 h1:qgsYHze+BxQPEYilxIz/KCQGaClvI2+yLBAZs+3+0B8=
|
github.com/tc-hib/winres v0.1.6 h1:qgsYHze+BxQPEYilxIz/KCQGaClvI2+yLBAZs+3+0B8=
|
||||||
github.com/tc-hib/winres v0.1.6/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A=
|
github.com/tc-hib/winres v0.1.6/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A=
|
||||||
|
github.com/tmclane/purego v0.0.0-20230601213035-1f25e70d7b01 h1:oQwu3iNDywGp1Hry+PDvz+grwbCGpzY+ckSnWKCnX5Y=
|
||||||
|
github.com/tmclane/purego v0.0.0-20230601213035-1f25e70d7b01/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||||
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b h1:cztK9x+ikg6nFscy5c8NgtfIXv/d0ESdENy9+JkE8i4=
|
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b h1:cztK9x+ikg6nFscy5c8NgtfIXv/d0ESdENy9+JkE8i4=
|
||||||
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
|
github.com/wailsapp/go-webview2 v1.0.2-0.20230604075323-d593c659ca7b/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
|
||||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||||
|
@ -2,7 +2,6 @@ package application
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/wailsapp/wails/v3/internal/capabilities"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -10,6 +9,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v3/internal/capabilities"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/icons"
|
"github.com/wailsapp/wails/v3/pkg/icons"
|
||||||
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
@ -333,9 +334,7 @@ func (a *App) error(message string, args ...any) {
|
|||||||
func (a *App) NewWebviewWindowWithOptions(windowOptions WebviewWindowOptions) *WebviewWindow {
|
func (a *App) NewWebviewWindowWithOptions(windowOptions WebviewWindowOptions) *WebviewWindow {
|
||||||
newWindow := NewWindow(windowOptions)
|
newWindow := NewWindow(windowOptions)
|
||||||
id := newWindow.id
|
id := newWindow.id
|
||||||
if a.windows == nil {
|
|
||||||
a.windows = make(map[uint]*WebviewWindow)
|
|
||||||
}
|
|
||||||
a.windowsLock.Lock()
|
a.windowsLock.Lock()
|
||||||
a.windows[id] = newWindow
|
a.windows[id] = newWindow
|
||||||
a.windowsLock.Unlock()
|
a.windowsLock.Unlock()
|
||||||
@ -416,7 +415,7 @@ func (a *App) Run() error {
|
|||||||
a.runLock.Unlock()
|
a.runLock.Unlock()
|
||||||
|
|
||||||
// set the application menu
|
// set the application menu
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" || runtime.GOOS == "linux" {
|
||||||
a.impl.setApplicationMenu(a.ApplicationMenu)
|
a.impl.setApplicationMenu(a.ApplicationMenu)
|
||||||
}
|
}
|
||||||
a.impl.setIcon(a.options.Icon)
|
a.impl.setIcon(a.options.Icon)
|
||||||
|
@ -1,153 +1,69 @@
|
|||||||
//go:build linux && !purego
|
//go:build linux
|
||||||
|
|
||||||
package application
|
package application
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gdk/gdk.h>
|
|
||||||
#include <webkit2/webkit2.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef struct App {
|
|
||||||
void *app;
|
|
||||||
} App;
|
|
||||||
|
|
||||||
extern void processApplicationEvent(uint);
|
|
||||||
|
|
||||||
extern void activateLinux(gpointer data);
|
|
||||||
|
|
||||||
static void activate (GtkApplication* app, gpointer data) {
|
|
||||||
// FIXME: should likely emit a WAILS specific code
|
|
||||||
// events.Mac.EventApplicationDidFinishLaunching == 1032
|
|
||||||
//processApplicationEvent(1032);
|
|
||||||
|
|
||||||
activateLinux(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GtkApplication* init(char* name) {
|
|
||||||
return gtk_application_new(name, G_APPLICATION_DEFAULT_FLAGS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int run(void *app, void *data) {
|
|
||||||
g_signal_connect (app, "activate", G_CALLBACK (activate), data);
|
|
||||||
g_application_hold(app); // allows it to run without a window
|
|
||||||
int status = g_application_run (G_APPLICATION (app), 0, NULL);
|
|
||||||
g_application_release(app);
|
|
||||||
g_object_unref (app);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/events"
|
"github.com/wailsapp/wails/v3/pkg/events"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
// FIXME: This should be handled appropriately in the individual files most likely.
|
||||||
// Set GDK_BACKEND=x11 if currently unset and XDG_SESSION_TYPE is unset, unspecified or x11 to prevent warnings
|
// Set GDK_BACKEND=x11 if currently unset and XDG_SESSION_TYPE is unset, unspecified or x11 to prevent warnings
|
||||||
_ = os.Setenv("GDK_BACKEND", "x11")
|
_ = os.Setenv("GDK_BACKEND", "x11")
|
||||||
}
|
}
|
||||||
|
|
||||||
type linuxApp struct {
|
type linuxApp struct {
|
||||||
application unsafe.Pointer
|
application pointer
|
||||||
applicationMenu unsafe.Pointer
|
applicationMenu pointer
|
||||||
parent *App
|
parent *App
|
||||||
|
|
||||||
startupActions []func()
|
startupActions []func()
|
||||||
|
|
||||||
// Native -> uint
|
// Native -> uint
|
||||||
windows map[*C.GtkWindow]uint
|
windows map[windowPointer]uint
|
||||||
windowsLock sync.Mutex
|
windowsLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *linuxApp) GetFlags(options Options) map[string]any {
|
||||||
|
if options.Flags == nil {
|
||||||
|
options.Flags = make(map[string]any)
|
||||||
|
}
|
||||||
|
return options.Flags
|
||||||
|
}
|
||||||
|
|
||||||
func getNativeApplication() *linuxApp {
|
func getNativeApplication() *linuxApp {
|
||||||
return globalApplication.impl.(*linuxApp)
|
return globalApplication.impl.(*linuxApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxApp) hide() {
|
func (m *linuxApp) hide() {
|
||||||
windows := C.gtk_application_get_windows((*C.GtkApplication)(m.application))
|
hideAllWindows(m.application)
|
||||||
for {
|
|
||||||
fmt.Println("hiding", windows.data)
|
|
||||||
C.gtk_widget_hide((*C.GtkWidget)(windows.data))
|
|
||||||
windows = windows.next
|
|
||||||
if windows == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxApp) show() {
|
func (m *linuxApp) show() {
|
||||||
windows := C.gtk_application_get_windows((*C.GtkApplication)(m.application))
|
showAllWindows(m.application)
|
||||||
for {
|
|
||||||
fmt.Println("hiding", windows.data)
|
|
||||||
C.gtk_widget_show_all((*C.GtkWidget)(windows.data))
|
|
||||||
windows = windows.next
|
|
||||||
if windows == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxApp) on(eventID uint) {
|
func (m *linuxApp) on(eventID uint) {
|
||||||
|
// TODO: What do we need to do here?
|
||||||
log.Println("linuxApp.on()", eventID)
|
log.Println("linuxApp.on()", eventID)
|
||||||
// TODO: Setup signal handling as appropriate
|
|
||||||
// Note: GTK signals seem to be strings!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxApp) setIcon(icon []byte) {
|
func (m *linuxApp) setIcon(icon []byte) {
|
||||||
/* // FIXME: WIP
|
fmt.Println("linuxApp.setIcon", "not implemented")
|
||||||
loader := C.gdk_pixbuf_loader_new()
|
|
||||||
|
|
||||||
if loader == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loaded := C.gdk_pixbuf_loader_write(loader, (*C.guchar)(&icon[0]), (C.gsize)(len(icon)), 0)
|
|
||||||
|
|
||||||
if loaded == C.bool(1) && C.gdk_pixbuf_loader_close(loader, 0) {
|
|
||||||
pixbuf := C.gdk_pixbuf_loader_get_pixbuf(loader)
|
|
||||||
if pixbuf != nil {
|
|
||||||
ww := m.parent.CurrentWindow()
|
|
||||||
window := ww.impl.window
|
|
||||||
C.gtk_window_set_icon(window, pixbuf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
C.g_object_unref(loader)
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxApp) name() string {
|
func (m *linuxApp) name() string {
|
||||||
// appName := C.getAppName()
|
return appName()
|
||||||
// defer C.free(unsafe.Pointer(appName))
|
|
||||||
// return C.GoString(appName)
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxApp) getCurrentWindowID() uint {
|
func (m *linuxApp) getCurrentWindowID() uint {
|
||||||
// TODO: Add extra metadata to window
|
return getCurrentWindowID(m.application, m.windows)
|
||||||
window := (*C.GtkWindow)(C.gtk_application_get_active_window((*C.GtkApplication)(m.application)))
|
|
||||||
if window == nil {
|
|
||||||
return uint(1)
|
|
||||||
}
|
|
||||||
m.windowsLock.Lock()
|
|
||||||
defer m.windowsLock.Unlock()
|
|
||||||
identifier, ok := m.windows[window]
|
|
||||||
if ok {
|
|
||||||
return identifier
|
|
||||||
}
|
|
||||||
return uint(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxApp) setApplicationMenu(menu *Menu) {
|
func (m *linuxApp) setApplicationMenu(menu *Menu) {
|
||||||
@ -172,20 +88,22 @@ func (m *linuxApp) run() error {
|
|||||||
fmt.Println("events.Mac.ApplicationDidFinishLaunching received!")
|
fmt.Println("events.Mac.ApplicationDidFinishLaunching received!")
|
||||||
})
|
})
|
||||||
|
|
||||||
var app C.App
|
return appRun(m.application)
|
||||||
app.app = unsafe.Pointer(m)
|
|
||||||
C.run(m.application, m.application)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxApp) destroy() {
|
func (m *linuxApp) destroy() {
|
||||||
C.g_application_quit((*C.GApplication)(m.application))
|
appDestroy(m.application)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *linuxApp) isOnMainThread() bool {
|
||||||
|
// FIXME: How do we detect this properly?
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// register our window to our parent mapping
|
// register our window to our parent mapping
|
||||||
func (m *linuxApp) registerWindow(window *C.GtkWindow, id uint) {
|
func (m *linuxApp) registerWindow(window pointer, id uint) {
|
||||||
m.windowsLock.Lock()
|
m.windowsLock.Lock()
|
||||||
m.windows[window] = id
|
m.windows[windowPointer(window)] = id
|
||||||
m.windowsLock.Unlock()
|
m.windowsLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,30 +112,15 @@ func newPlatformApp(parent *App) *linuxApp {
|
|||||||
if name == "" {
|
if name == "" {
|
||||||
name = "undefined"
|
name = "undefined"
|
||||||
}
|
}
|
||||||
nameC := C.CString(fmt.Sprintf("org.wails.%s", name))
|
|
||||||
app := &linuxApp{
|
app := &linuxApp{
|
||||||
parent: parent,
|
parent: parent,
|
||||||
application: unsafe.Pointer(C.init(nameC)),
|
application: appNew(name),
|
||||||
// name: fmt.Sprintf("org.wails.%s", name),
|
windows: map[windowPointer]uint{},
|
||||||
windows: map[*C.GtkWindow]uint{},
|
|
||||||
}
|
}
|
||||||
C.free(unsafe.Pointer(nameC))
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
// executeStartupActions is called by `activateLinux` below to execute
|
/*
|
||||||
// code which needs to be run after the 'activate' signal is received
|
|
||||||
func (m *linuxApp) executeStartupActions() {
|
|
||||||
for _, fn := range m.startupActions {
|
|
||||||
fn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//export activateLinux
|
|
||||||
func activateLinux(data unsafe.Pointer) {
|
|
||||||
getNativeApplication().executeStartupActions()
|
|
||||||
}
|
|
||||||
|
|
||||||
//export processApplicationEvent
|
//export processApplicationEvent
|
||||||
func processApplicationEvent(eventID C.uint) {
|
func processApplicationEvent(eventID C.uint) {
|
||||||
// TODO: add translation to Wails events
|
// TODO: add translation to Wails events
|
||||||
@ -256,7 +159,7 @@ func processDragItems(windowID C.uint, arr **C.char, length C.int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//export processMenuItemClick
|
//export processMenuItemClick
|
||||||
func processMenuItemClick(menuID C.uint) {
|
func processMenuItemClick(menuID identifier) {
|
||||||
menuItemClicked <- uint(menuID)
|
menuItemClicked <- uint(menuID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,3 +169,4 @@ func setIcon(icon []byte) {
|
|||||||
}
|
}
|
||||||
//C.setApplicationIcon(unsafe.Pointer(&icon[0]), C.int(len(icon)))
|
//C.setApplicationIcon(unsafe.Pointer(&icon[0]), C.int(len(icon)))
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -1,253 +0,0 @@
|
|||||||
//go:build linux && purego
|
|
||||||
|
|
||||||
package application
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ebitengine/purego"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
gtk3 = "libgtk-3.so"
|
|
||||||
gtk4 = "libgtk-4.so"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
gtk uintptr
|
|
||||||
version int
|
|
||||||
webkit uintptr
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// needed for GTK4 to function
|
|
||||||
_ = os.Setenv("GDK_BACKEND", "x11")
|
|
||||||
var err error
|
|
||||||
/*
|
|
||||||
gtk, err = purego.Dlopen(gtk4, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
|
||||||
if err == nil {
|
|
||||||
version = 4
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Failed to open GTK4: Falling back to GTK3")
|
|
||||||
*/
|
|
||||||
gtk, err = purego.Dlopen(gtk3, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
var webkit4 string = "libwebkit2gtk-4.1.so"
|
|
||||||
webkit, err = purego.Dlopen(webkit4, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type linuxApp struct {
|
|
||||||
appName string
|
|
||||||
application uintptr
|
|
||||||
applicationMenu uintptr
|
|
||||||
parent *App
|
|
||||||
|
|
||||||
// Native -> uint
|
|
||||||
windows map[uintptr]uint
|
|
||||||
windowsLock sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNativeApplication() *linuxApp {
|
|
||||||
return globalApplication.impl.(*linuxApp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) hide() {
|
|
||||||
// C.hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) show() {
|
|
||||||
// C.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) on(eventID uint) {
|
|
||||||
log.Println("linuxApp.on()", eventID)
|
|
||||||
|
|
||||||
// TODO: Setup signal handling as appropriate
|
|
||||||
// Note: GTK signals seem to be strings!
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) setIcon(icon []byte) {
|
|
||||||
// C.setApplicationIcon(unsafe.Pointer(&icon[0]), C.int(len(icon)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) name() string {
|
|
||||||
return m.appName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) getCurrentWindowID() uint {
|
|
||||||
var getCurrentWindow func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getCurrentWindow, gtk, "gtk_application_get_active_window")
|
|
||||||
window := getCurrentWindow(m.application)
|
|
||||||
if window == 0 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
m.windowsLock.Lock()
|
|
||||||
defer m.windowsLock.Unlock()
|
|
||||||
if identifier, ok := m.windows[window]; ok {
|
|
||||||
return identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) setApplicationMenu(menu *Menu) {
|
|
||||||
if menu == nil {
|
|
||||||
// Create a default menu
|
|
||||||
menu = defaultApplicationMenu()
|
|
||||||
}
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
menu.Update()
|
|
||||||
m.applicationMenu = (menu.impl).(*linuxMenu).native
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) activate() {
|
|
||||||
fmt.Println("linuxApp.activated!", m.application)
|
|
||||||
var hold func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&hold, gtk, "g_application_hold")
|
|
||||||
|
|
||||||
hold(m.application)
|
|
||||||
|
|
||||||
// time.Sleep(50 * time.Millisecond)
|
|
||||||
// m.parent.activate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) run() error {
|
|
||||||
// Add a hook to the ApplicationDidFinishLaunching event
|
|
||||||
// FIXME: add Wails specific events - i.e. Shouldn't platform specific ones be translated to Wails events?
|
|
||||||
/* m.parent.On(events.Mac.ApplicationDidFinishLaunching, func() {
|
|
||||||
// Do we need to do anything now?
|
|
||||||
fmt.Println("ApplicationDidFinishLaunching!")
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
m.parent.OnWindowCreation(func(window *WebviewWindow) {
|
|
||||||
fmt.Println("OnWindowCreation: ", window)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
var g_signal_connect func(uintptr, string, uintptr, uintptr, bool, int) int
|
|
||||||
purego.RegisterLibFunc(&g_signal_connect, gtk, "g_signal_connect_data")
|
|
||||||
g_signal_connect(m.application, "activate", purego.NewCallback(m.activate), m.application, false, 0)
|
|
||||||
|
|
||||||
var run func(uintptr, int, []string) int
|
|
||||||
purego.RegisterLibFunc(&run, gtk, "g_application_run")
|
|
||||||
|
|
||||||
// FIXME: Convert status to 'error' if needed
|
|
||||||
status := run(m.application, 0, []string{})
|
|
||||||
fmt.Println("status", status)
|
|
||||||
|
|
||||||
var release func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&release, gtk, "g_application_release")
|
|
||||||
release(m.application)
|
|
||||||
|
|
||||||
purego.RegisterLibFunc(&release, gtk, "g_object_unref")
|
|
||||||
release(m.application)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) destroy() {
|
|
||||||
var quit func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&quit, gtk, "g_application_quit")
|
|
||||||
quit(m.application)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) registerWindow(address uintptr, window uint) {
|
|
||||||
m.windowsLock.Lock()
|
|
||||||
m.windows[address] = window
|
|
||||||
m.windowsLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPlatformApp(parent *App) *linuxApp {
|
|
||||||
name := strings.ToLower(parent.options.Name)
|
|
||||||
if name == "" {
|
|
||||||
name = "undefined"
|
|
||||||
}
|
|
||||||
identifier := fmt.Sprintf("org.wails.%s", strings.Replace(name, " ", "-", -1))
|
|
||||||
|
|
||||||
var gtkNew func(string, uint) uintptr
|
|
||||||
purego.RegisterLibFunc(>kNew, gtk, "gtk_application_new")
|
|
||||||
app := &linuxApp{
|
|
||||||
appName: identifier,
|
|
||||||
parent: parent,
|
|
||||||
application: gtkNew(identifier, 0),
|
|
||||||
windows: map[uintptr]uint{},
|
|
||||||
}
|
|
||||||
return app
|
|
||||||
}
|
|
||||||
|
|
||||||
func processApplicationEvent(eventID uint) {
|
|
||||||
// TODO: add translation to Wails events
|
|
||||||
// currently reusing Mac specific values
|
|
||||||
applicationEvents <- eventID
|
|
||||||
}
|
|
||||||
|
|
||||||
func processWindowEvent(windowID uint, eventID uint) {
|
|
||||||
windowEvents <- &WindowEvent{
|
|
||||||
WindowID: windowID,
|
|
||||||
EventID: eventID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func processMessage(windowID uint, message string) {
|
|
||||||
windowMessageBuffer <- &windowMessage{
|
|
||||||
windowId: windowID,
|
|
||||||
message: message,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func processURLRequest(windowID uint, wkUrlSchemeTask uintptr) {
|
|
||||||
fmt.Println("processURLRequest", windowID, wkUrlSchemeTask)
|
|
||||||
webviewRequests <- &webViewAssetRequest{
|
|
||||||
Request: webview.NewRequest(wkUrlSchemeTask),
|
|
||||||
windowId: windowID,
|
|
||||||
windowName: globalApplication.getWindowForID(windowID).Name(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func processDragItems(windowID uint, arr []string, length int) {
|
|
||||||
windowDragAndDropBuffer <- &dragAndDropMessage{
|
|
||||||
windowId: windowID,
|
|
||||||
filenames: arr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func processMenuItemClick(menuID uint) {
|
|
||||||
menuItemClicked <- menuID
|
|
||||||
}
|
|
||||||
|
|
||||||
func setIcon(icon []byte) {
|
|
||||||
if icon == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("setIcon")
|
|
||||||
/*
|
|
||||||
GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
|
|
||||||
if (!loader)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (gdk_pixbuf_loader_write(loader, buf, len, NULL) && gdk_pixbuf_loader_close(loader, NULL))
|
|
||||||
{
|
|
||||||
GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
|
|
||||||
if (pixbuf)
|
|
||||||
{
|
|
||||||
gtk_window_set_icon(window, pixbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_object_unref(loader);*/
|
|
||||||
}
|
|
@ -20,12 +20,12 @@ func (m linuxClipboard) setText(text string) bool {
|
|||||||
return bool(success)
|
return bool(success)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m linuxClipboard) text() string {
|
func (m linuxClipboard) text() (string, bool) {
|
||||||
clipboardLock.RLock()
|
clipboardLock.RLock()
|
||||||
defer clipboardLock.RUnlock()
|
defer clipboardLock.RUnlock()
|
||||||
// clipboardText := C.getClipboardText()
|
// clipboardText := C.getClipboardText()
|
||||||
// result := C.GoString(clipboardText)
|
// result := C.GoString(clipboardText)
|
||||||
return ""
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClipboardImpl() *linuxClipboard {
|
func newClipboardImpl() *linuxClipboard {
|
||||||
|
@ -1,154 +1,40 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package application
|
package application
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gdk/gdk.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
static GtkWidget* new_about_dialog(GtkWindow *parent, const gchar *msg) {
|
|
||||||
// gtk_message_dialog_new is variadic! Can't call from cgo
|
|
||||||
GtkWidget *dialog;
|
|
||||||
dialog = gtk_message_dialog_new(
|
|
||||||
parent,
|
|
||||||
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
||||||
GTK_MESSAGE_INFO,
|
|
||||||
GTK_BUTTONS_CLOSE,
|
|
||||||
msg);
|
|
||||||
|
|
||||||
g_signal_connect_swapped (dialog,
|
|
||||||
"response",
|
|
||||||
G_CALLBACK (gtk_widget_destroy),
|
|
||||||
dialog);
|
|
||||||
return dialog;
|
|
||||||
};
|
|
||||||
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const AlertStyleWarning = C.int(0)
|
|
||||||
const AlertStyleInformational = C.int(1)
|
|
||||||
const AlertStyleCritical = C.int(2)
|
|
||||||
|
|
||||||
var alertTypeMap = map[DialogType]C.int{
|
|
||||||
WarningDialog: AlertStyleWarning,
|
|
||||||
InfoDialog: AlertStyleInformational,
|
|
||||||
ErrorDialog: AlertStyleCritical,
|
|
||||||
QuestionDialog: AlertStyleInformational,
|
|
||||||
}
|
|
||||||
|
|
||||||
func setWindowIcon(window *C.GtkWindow, icon []byte) {
|
|
||||||
fmt.Println("setWindowIcon", len(icon))
|
|
||||||
loader := C.gdk_pixbuf_loader_new()
|
|
||||||
if loader == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
written := C.gdk_pixbuf_loader_write(
|
|
||||||
loader,
|
|
||||||
(*C.uchar)(&icon[0]),
|
|
||||||
C.ulong(len(icon)),
|
|
||||||
nil)
|
|
||||||
if written == 0 {
|
|
||||||
fmt.Println("failed to write icon")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
C.gdk_pixbuf_loader_close(loader, nil)
|
|
||||||
pixbuf := C.gdk_pixbuf_loader_get_pixbuf(loader)
|
|
||||||
if pixbuf != nil {
|
|
||||||
fmt.Println("gtk_window_set_icon", window)
|
|
||||||
C.gtk_window_set_icon((*C.GtkWindow)(window), pixbuf)
|
|
||||||
}
|
|
||||||
C.g_object_unref(C.gpointer(loader))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) showAboutDialog(title string, message string, icon []byte) {
|
func (m *linuxApp) showAboutDialog(title string, message string, icon []byte) {
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
window := globalApplication.getWindowForID(m.getCurrentWindowID())
|
||||||
parent := C.gtk_application_get_active_window((*C.GtkApplication)(m.application))
|
var parent pointer
|
||||||
cMsg := C.CString(message)
|
if window != nil {
|
||||||
cTitle := C.CString(title)
|
parent = window.impl.(*linuxWebviewWindow).window
|
||||||
defer C.free(unsafe.Pointer(cMsg))
|
}
|
||||||
defer C.free(unsafe.Pointer(cTitle))
|
about := newMessageDialog(InfoDialog)
|
||||||
dialog := C.new_about_dialog(parent, cMsg)
|
about.SetTitle(title).
|
||||||
C.gtk_window_set_title(
|
SetMessage(message).
|
||||||
(*C.GtkWindow)(unsafe.Pointer(dialog)),
|
SetIcon(icon)
|
||||||
cTitle)
|
runQuestionDialog(
|
||||||
// setWindowIcon((*C.GtkWindow)(dialog), icon)
|
parent,
|
||||||
C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(dialog)))
|
about,
|
||||||
|
)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type linuxDialog struct {
|
type linuxDialog struct {
|
||||||
dialog *MessageDialog
|
dialog *MessageDialog
|
||||||
|
|
||||||
//nsDialog unsafe.Pointer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxDialog) show() {
|
func (m *linuxDialog) show() {
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
windowId := getNativeApplication().getCurrentWindowID()
|
||||||
|
window := globalApplication.getWindowForID(windowId)
|
||||||
|
var parent pointer
|
||||||
|
if window != nil {
|
||||||
|
parent = window.impl.(*linuxWebviewWindow).window
|
||||||
|
}
|
||||||
|
|
||||||
// Mac can only have 4 Buttons on a dialog
|
response := runQuestionDialog(parent, m.dialog)
|
||||||
if len(m.dialog.Buttons) > 4 {
|
if response >= 0 && response < len(m.dialog.Buttons) {
|
||||||
m.dialog.Buttons = m.dialog.Buttons[:4]
|
button := m.dialog.Buttons[response]
|
||||||
|
if button.Callback != nil {
|
||||||
|
go button.Callback()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// if m.nsDialog != nil {
|
|
||||||
// //C.releaseDialog(m.nsDialog)
|
|
||||||
// }
|
|
||||||
// var title *C.char
|
|
||||||
// if m.dialog.Title != "" {
|
|
||||||
// title = C.CString(m.dialog.Title)
|
|
||||||
// }
|
|
||||||
// var message *C.char
|
|
||||||
// if m.dialog.Message != "" {
|
|
||||||
// message = C.CString(m.dialog.Message)
|
|
||||||
// }
|
|
||||||
// var iconData unsafe.Pointer
|
|
||||||
// var iconLength C.int
|
|
||||||
// if m.dialog.Icon != nil {
|
|
||||||
// iconData = unsafe.Pointer(&m.dialog.Icon[0])
|
|
||||||
// iconLength = C.int(len(m.dialog.Icon))
|
|
||||||
// } else {
|
|
||||||
// // if it's an error, use the application Icon
|
|
||||||
// if m.dialog.DialogType == ErrorDialog {
|
|
||||||
// iconData = unsafe.Pointer(&globalApplication.options.Icon[0])
|
|
||||||
// iconLength = C.int(len(globalApplication.options.Icon))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// alertType, ok := alertTypeMap[m.dialog.DialogType]
|
|
||||||
// if !ok {
|
|
||||||
// alertType = AlertStyleInformational
|
|
||||||
// }
|
|
||||||
|
|
||||||
// m.nsDialog = C.createAlert(alertType, title, message, iconData, iconLength)
|
|
||||||
|
|
||||||
// Reverse the Buttons so that the default is on the right
|
|
||||||
reversedButtons := make([]*Button, len(m.dialog.Buttons))
|
|
||||||
var count = 0
|
|
||||||
for i := len(m.dialog.Buttons) - 1; i >= 0; i-- {
|
|
||||||
//button := m.dialog.Buttons[i]
|
|
||||||
//C.alertAddButton(m.nsDialog, C.CString(button.Label), C.bool(button.IsDefault), C.bool(button.IsCancel))
|
|
||||||
reversedButtons[count] = m.dialog.Buttons[i]
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonPressed := int(0) //C.dialogRunModal(m.nsDialog))
|
|
||||||
if len(m.dialog.Buttons) > buttonPressed {
|
|
||||||
button := reversedButtons[buttonPressed]
|
|
||||||
if button.callback != nil {
|
|
||||||
button.callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDialogImpl(d *MessageDialog) *linuxDialog {
|
func newDialogImpl(d *MessageDialog) *linuxDialog {
|
||||||
@ -167,85 +53,8 @@ func newOpenFileDialogImpl(d *OpenFileDialog) *linuxOpenFileDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toCString(s string) *C.char {
|
|
||||||
if s == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return C.CString(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxOpenFileDialog) show() ([]string, error) {
|
func (m *linuxOpenFileDialog) show() ([]string, error) {
|
||||||
openFileResponses[m.dialog.id] = make(chan string)
|
return runOpenFileDialog(m.dialog)
|
||||||
// nsWindow := unsafe.Pointer(nil)
|
|
||||||
if m.dialog.window != nil {
|
|
||||||
// get NSWindow from window
|
|
||||||
//nsWindow = m.dialog.window.impl.(*macosWebviewWindow).nsWindow
|
|
||||||
}
|
|
||||||
|
|
||||||
// Massage filter patterns into macOS format
|
|
||||||
// We iterate all filter patterns, tidy them up and then join them with a semicolon
|
|
||||||
// This should produce a single string of extensions like "png;jpg;gif"
|
|
||||||
// var filterPatterns string
|
|
||||||
// if len(m.dialog.filters) > 0 {
|
|
||||||
// var allPatterns []string
|
|
||||||
// for _, filter := range m.dialog.filters {
|
|
||||||
// patternComponents := strings.Split(filter.Pattern, ";")
|
|
||||||
// for i, component := range patternComponents {
|
|
||||||
// filterPattern := strings.TrimSpace(component)
|
|
||||||
// filterPattern = strings.TrimPrefix(filterPattern, "*.")
|
|
||||||
// patternComponents[i] = filterPattern
|
|
||||||
// }
|
|
||||||
// allPatterns = append(allPatterns, strings.Join(patternComponents, ";"))
|
|
||||||
// }
|
|
||||||
// filterPatterns = strings.Join(allPatterns, ";")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// C.showOpenFileDialog(C.uint(m.dialog.id),
|
|
||||||
// C.bool(m.dialog.canChooseFiles),
|
|
||||||
// C.bool(m.dialog.canChooseDirectories),
|
|
||||||
// C.bool(m.dialog.canCreateDirectories),
|
|
||||||
// C.bool(m.dialog.showHiddenFiles),
|
|
||||||
// C.bool(m.dialog.allowsMultipleSelection),
|
|
||||||
// C.bool(m.dialog.resolvesAliases),
|
|
||||||
// C.bool(m.dialog.hideExtension),
|
|
||||||
// C.bool(m.dialog.treatsFilePackagesAsDirectories),
|
|
||||||
// C.bool(m.dialog.allowsOtherFileTypes),
|
|
||||||
// toCString(filterPatterns),
|
|
||||||
// C.uint(len(filterPatterns)),
|
|
||||||
// toCString(m.dialog.message),
|
|
||||||
// toCString(m.dialog.directory),
|
|
||||||
// toCString(m.dialog.buttonText),
|
|
||||||
// nsWindow)
|
|
||||||
var result []string
|
|
||||||
for filename := range openFileResponses[m.dialog.id] {
|
|
||||||
result = append(result, filename)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//export openFileDialogCallback
|
|
||||||
func openFileDialogCallback(cid C.uint, cpath *C.char) {
|
|
||||||
path := C.GoString(cpath)
|
|
||||||
id := uint(cid)
|
|
||||||
channel, ok := openFileResponses[id]
|
|
||||||
if ok {
|
|
||||||
channel <- path
|
|
||||||
} else {
|
|
||||||
panic("No channel found for open file dialog")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//export openFileDialogCallbackEnd
|
|
||||||
func openFileDialogCallbackEnd(cid C.uint) {
|
|
||||||
id := uint(cid)
|
|
||||||
channel, ok := openFileResponses[id]
|
|
||||||
if ok {
|
|
||||||
close(channel)
|
|
||||||
delete(openFileResponses, id)
|
|
||||||
freeDialogID(id)
|
|
||||||
} else {
|
|
||||||
panic("No channel found for open file dialog")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type linuxSaveFileDialog struct {
|
type linuxSaveFileDialog struct {
|
||||||
@ -259,42 +68,5 @@ func newSaveFileDialogImpl(d *SaveFileDialog) *linuxSaveFileDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxSaveFileDialog) show() (string, error) {
|
func (m *linuxSaveFileDialog) show() (string, error) {
|
||||||
saveFileResponses[m.dialog.id] = make(chan string)
|
return runSaveFileDialog(m.dialog)
|
||||||
// nsWindow := unsafe.Pointer(nil)
|
|
||||||
if m.dialog.window != nil {
|
|
||||||
// get NSWindow from window
|
|
||||||
// nsWindow = m.dialog.window.impl.(*linuxWebviewWindow).nsWindow
|
|
||||||
}
|
|
||||||
|
|
||||||
// C.showSaveFileDialog(C.uint(m.dialog.id),
|
|
||||||
// C.bool(m.dialog.canCreateDirectories),
|
|
||||||
// C.bool(m.dialog.showHiddenFiles),
|
|
||||||
// C.bool(m.dialog.canSelectHiddenExtension),
|
|
||||||
// C.bool(m.dialog.hideExtension),
|
|
||||||
// C.bool(m.dialog.treatsFilePackagesAsDirectories),
|
|
||||||
// C.bool(m.dialog.allowOtherFileTypes),
|
|
||||||
// toCString(m.dialog.message),
|
|
||||||
// toCString(m.dialog.directory),
|
|
||||||
// toCString(m.dialog.buttonText),
|
|
||||||
// toCString(m.dialog.filename),
|
|
||||||
// nsWindow)
|
|
||||||
return <-saveFileResponses[m.dialog.id], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//export saveFileDialogCallback
|
|
||||||
func saveFileDialogCallback(cid C.uint, cpath *C.char) {
|
|
||||||
// Covert the path to a string
|
|
||||||
path := C.GoString(cpath)
|
|
||||||
id := uint(cid)
|
|
||||||
// put response on channel
|
|
||||||
channel, ok := saveFileResponses[id]
|
|
||||||
if ok {
|
|
||||||
channel <- path
|
|
||||||
close(channel)
|
|
||||||
delete(saveFileResponses, id)
|
|
||||||
freeDialogID(id)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
panic("No channel found for save file dialog")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,220 +0,0 @@
|
|||||||
//go:build linux && purego
|
|
||||||
|
|
||||||
package application
|
|
||||||
|
|
||||||
const AlertStyleWarning = 0
|
|
||||||
const AlertStyleInformational = 1
|
|
||||||
const AlertStyleCritical = 2
|
|
||||||
|
|
||||||
var alertTypeMap = map[DialogType]int{
|
|
||||||
WarningDialog: AlertStyleWarning,
|
|
||||||
InfoDialog: AlertStyleInformational,
|
|
||||||
ErrorDialog: AlertStyleCritical,
|
|
||||||
QuestionDialog: AlertStyleInformational,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) showAboutDialog(title string, message string, icon []byte) {
|
|
||||||
// var iconData unsafe.Pointer
|
|
||||||
// if icon != nil {
|
|
||||||
// iconData = unsafe.Pointer(&icon[0])
|
|
||||||
// }
|
|
||||||
//C.showAboutBox(C.CString(title), C.CString(message), iconData, C.int(len(icon)))
|
|
||||||
}
|
|
||||||
|
|
||||||
type linuxDialog struct {
|
|
||||||
dialog *MessageDialog
|
|
||||||
|
|
||||||
//nsDialog unsafe.Pointer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxDialog) show() {
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
|
|
||||||
// Mac can only have 4 Buttons on a dialog
|
|
||||||
if len(m.dialog.Buttons) > 4 {
|
|
||||||
m.dialog.Buttons = m.dialog.Buttons[:4]
|
|
||||||
}
|
|
||||||
|
|
||||||
// if m.nsDialog != nil {
|
|
||||||
// //C.releaseDialog(m.nsDialog)
|
|
||||||
// }
|
|
||||||
// var title *C.char
|
|
||||||
// if m.dialog.Title != "" {
|
|
||||||
// title = C.CString(m.dialog.Title)
|
|
||||||
// }
|
|
||||||
// var message *C.char
|
|
||||||
// if m.dialog.Message != "" {
|
|
||||||
// message = C.CString(m.dialog.Message)
|
|
||||||
// }
|
|
||||||
// var iconData unsafe.Pointer
|
|
||||||
// var iconLength C.int
|
|
||||||
// if m.dialog.Icon != nil {
|
|
||||||
// iconData = unsafe.Pointer(&m.dialog.Icon[0])
|
|
||||||
// iconLength = C.int(len(m.dialog.Icon))
|
|
||||||
// } else {
|
|
||||||
// // if it's an error, use the application Icon
|
|
||||||
// if m.dialog.DialogType == ErrorDialog {
|
|
||||||
// iconData = unsafe.Pointer(&globalApplication.options.Icon[0])
|
|
||||||
// iconLength = C.int(len(globalApplication.options.Icon))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// alertType, ok := alertTypeMap[m.dialog.DialogType]
|
|
||||||
// if !ok {
|
|
||||||
// alertType = AlertStyleInformational
|
|
||||||
// }
|
|
||||||
|
|
||||||
// m.nsDialog = C.createAlert(alertType, title, message, iconData, iconLength)
|
|
||||||
|
|
||||||
// Reverse the Buttons so that the default is on the right
|
|
||||||
reversedButtons := make([]*Button, len(m.dialog.Buttons))
|
|
||||||
var count = 0
|
|
||||||
for i := len(m.dialog.Buttons) - 1; i >= 0; i-- {
|
|
||||||
//button := m.dialog.Buttons[i]
|
|
||||||
//C.alertAddButton(m.nsDialog, C.CString(button.Label), C.bool(button.IsDefault), C.bool(button.IsCancel))
|
|
||||||
reversedButtons[count] = m.dialog.Buttons[i]
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonPressed := int(0) //C.dialogRunModal(m.nsDialog))
|
|
||||||
if len(m.dialog.Buttons) > buttonPressed {
|
|
||||||
button := reversedButtons[buttonPressed]
|
|
||||||
if button.callback != nil {
|
|
||||||
button.callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDialogImpl(d *MessageDialog) *linuxDialog {
|
|
||||||
return &linuxDialog{
|
|
||||||
dialog: d,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type linuxOpenFileDialog struct {
|
|
||||||
dialog *OpenFileDialog
|
|
||||||
}
|
|
||||||
|
|
||||||
func newOpenFileDialogImpl(d *OpenFileDialog) *linuxOpenFileDialog {
|
|
||||||
return &linuxOpenFileDialog{
|
|
||||||
dialog: d,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxOpenFileDialog) show() ([]string, error) {
|
|
||||||
openFileResponses[m.dialog.id] = make(chan string)
|
|
||||||
// nsWindow := unsafe.Pointer(nil)
|
|
||||||
if m.dialog.window != nil {
|
|
||||||
// get NSWindow from window
|
|
||||||
//nsWindow = m.dialog.window.impl.(*macosWebviewWindow).nsWindow
|
|
||||||
}
|
|
||||||
|
|
||||||
// Massage filter patterns into macOS format
|
|
||||||
// We iterate all filter patterns, tidy them up and then join them with a semicolon
|
|
||||||
// This should produce a single string of extensions like "png;jpg;gif"
|
|
||||||
// var filterPatterns string
|
|
||||||
// if len(m.dialog.filters) > 0 {
|
|
||||||
// var allPatterns []string
|
|
||||||
// for _, filter := range m.dialog.filters {
|
|
||||||
// patternComponents := strings.Split(filter.Pattern, ";")
|
|
||||||
// for i, component := range patternComponents {
|
|
||||||
// filterPattern := strings.TrimSpace(component)
|
|
||||||
// filterPattern = strings.TrimPrefix(filterPattern, "*.")
|
|
||||||
// patternComponents[i] = filterPattern
|
|
||||||
// }
|
|
||||||
// allPatterns = append(allPatterns, strings.Join(patternComponents, ";"))
|
|
||||||
// }
|
|
||||||
// filterPatterns = strings.Join(allPatterns, ";")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// C.showOpenFileDialog(C.uint(m.dialog.id),
|
|
||||||
// C.bool(m.dialog.canChooseFiles),
|
|
||||||
// C.bool(m.dialog.canChooseDirectories),
|
|
||||||
// C.bool(m.dialog.canCreateDirectories),
|
|
||||||
// C.bool(m.dialog.showHiddenFiles),
|
|
||||||
// C.bool(m.dialog.allowsMultipleSelection),
|
|
||||||
// C.bool(m.dialog.resolvesAliases),
|
|
||||||
// C.bool(m.dialog.hideExtension),
|
|
||||||
// C.bool(m.dialog.treatsFilePackagesAsDirectories),
|
|
||||||
// C.bool(m.dialog.allowsOtherFileTypes),
|
|
||||||
// toCString(filterPatterns),
|
|
||||||
// C.uint(len(filterPatterns)),
|
|
||||||
// toCString(m.dialog.message),
|
|
||||||
// toCString(m.dialog.directory),
|
|
||||||
// toCString(m.dialog.buttonText),
|
|
||||||
// nsWindow)
|
|
||||||
var result []string
|
|
||||||
for filename := range openFileResponses[m.dialog.id] {
|
|
||||||
result = append(result, filename)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func openFileDialogCallback(id uint, path string) {
|
|
||||||
channel, ok := openFileResponses[id]
|
|
||||||
if ok {
|
|
||||||
channel <- path
|
|
||||||
} else {
|
|
||||||
panic("No channel found for open file dialog")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func openFileDialogCallbackEnd(id uint) {
|
|
||||||
channel, ok := openFileResponses[id]
|
|
||||||
if ok {
|
|
||||||
close(channel)
|
|
||||||
delete(openFileResponses, id)
|
|
||||||
freeDialogID(id)
|
|
||||||
} else {
|
|
||||||
panic("No channel found for open file dialog")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type linuxSaveFileDialog struct {
|
|
||||||
dialog *SaveFileDialog
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSaveFileDialogImpl(d *SaveFileDialog) *linuxSaveFileDialog {
|
|
||||||
return &linuxSaveFileDialog{
|
|
||||||
dialog: d,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxSaveFileDialog) show() (string, error) {
|
|
||||||
saveFileResponses[m.dialog.id] = make(chan string)
|
|
||||||
// nsWindow := unsafe.Pointer(nil)
|
|
||||||
if m.dialog.window != nil {
|
|
||||||
// get NSWindow from window
|
|
||||||
// nsWindow = m.dialog.window.impl.(*linuxWebviewWindow).nsWindow
|
|
||||||
}
|
|
||||||
|
|
||||||
// C.showSaveFileDialog(C.uint(m.dialog.id),
|
|
||||||
// C.bool(m.dialog.canCreateDirectories),
|
|
||||||
// C.bool(m.dialog.showHiddenFiles),
|
|
||||||
// C.bool(m.dialog.canSelectHiddenExtension),
|
|
||||||
// C.bool(m.dialog.hideExtension),
|
|
||||||
// C.bool(m.dialog.treatsFilePackagesAsDirectories),
|
|
||||||
// C.bool(m.dialog.allowOtherFileTypes),
|
|
||||||
// toCString(m.dialog.message),
|
|
||||||
// toCString(m.dialog.directory),
|
|
||||||
// toCString(m.dialog.buttonText),
|
|
||||||
// toCString(m.dialog.filename),
|
|
||||||
// nsWindow)
|
|
||||||
return <-saveFileResponses[m.dialog.id], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveFileDialogCallback(cid uint, path string) {
|
|
||||||
// put response on channel
|
|
||||||
channel, ok := saveFileResponses[cid]
|
|
||||||
if ok {
|
|
||||||
channel <- path
|
|
||||||
close(channel)
|
|
||||||
delete(saveFileResponses, cid)
|
|
||||||
freeDialogID(cid)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
panic("No channel found for save file dialog")
|
|
||||||
}
|
|
||||||
}
|
|
997
v3/pkg/application/linux_cgo.go
Normal file
997
v3/pkg/application/linux_cgo.go
Normal file
@ -0,0 +1,997 @@
|
|||||||
|
//go:build linux && cgo
|
||||||
|
|
||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/events"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
#include <webkit2/webkit2.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct CallbackID
|
||||||
|
{
|
||||||
|
unsigned int value;
|
||||||
|
} CallbackID;
|
||||||
|
|
||||||
|
extern void dispatchOnMainThreadCallback(unsigned int);
|
||||||
|
|
||||||
|
static gboolean dispatchCallback(gpointer data) {
|
||||||
|
struct CallbackID *args = data;
|
||||||
|
unsigned int cid = args->value;
|
||||||
|
dispatchOnMainThreadCallback(cid);
|
||||||
|
free(args);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dispatchOnMainThread(unsigned int id) {
|
||||||
|
CallbackID *args = malloc(sizeof(CallbackID));
|
||||||
|
args->value = id;
|
||||||
|
g_idle_add((GSourceFunc)dispatchCallback, (gpointer)args);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct WindowEvent {
|
||||||
|
uint id;
|
||||||
|
uint event;
|
||||||
|
} WindowEvent;
|
||||||
|
|
||||||
|
// exported below
|
||||||
|
void activateLinux(gpointer data);
|
||||||
|
extern void emit(WindowEvent* data);
|
||||||
|
void handleClick(void*);
|
||||||
|
extern gboolean onButtonEvent(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
|
||||||
|
extern void onDragNDrop(
|
||||||
|
void *target,
|
||||||
|
GdkDragContext* context,
|
||||||
|
gint x,
|
||||||
|
gint y,
|
||||||
|
gpointer seldata,
|
||||||
|
guint info,
|
||||||
|
guint time,
|
||||||
|
gpointer data);
|
||||||
|
extern void onProcessRequest(void *request, gpointer user_data);
|
||||||
|
// exported below (end)
|
||||||
|
|
||||||
|
static void signal_connect(GtkWidget *widget, char *event, void *cb, void* data) {
|
||||||
|
// g_signal_connect is a macro and can't be called directly
|
||||||
|
g_signal_connect(widget, event, cb, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* new_message_dialog(GtkWindow *parent, const gchar *msg, int dialogType, bool hasButtons) {
|
||||||
|
// gtk_message_dialog_new is variadic! Can't call from cgo directly
|
||||||
|
GtkWidget *dialog;
|
||||||
|
int buttonMask;
|
||||||
|
|
||||||
|
// buttons will be added after creation
|
||||||
|
buttonMask = GTK_BUTTONS_OK;
|
||||||
|
if (hasButtons) {
|
||||||
|
buttonMask = GTK_BUTTONS_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog = gtk_message_dialog_new(
|
||||||
|
parent,
|
||||||
|
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
dialogType,
|
||||||
|
buttonMask,
|
||||||
|
msg);
|
||||||
|
|
||||||
|
// g_signal_connect_swapped (dialog,
|
||||||
|
// "response",
|
||||||
|
// G_CALLBACK (callback),
|
||||||
|
// dialog);
|
||||||
|
return dialog;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void messageDialogCB(gint button);
|
||||||
|
|
||||||
|
typedef struct Screen {
|
||||||
|
const char* id;
|
||||||
|
const char* name;
|
||||||
|
int p_width;
|
||||||
|
int p_height;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int w_width;
|
||||||
|
int w_height;
|
||||||
|
int w_x;
|
||||||
|
int w_y;
|
||||||
|
float scale;
|
||||||
|
double rotation;
|
||||||
|
bool isPrimary;
|
||||||
|
} Screen;
|
||||||
|
|
||||||
|
|
||||||
|
static int GetNumScreens(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type windowPointer *C.GtkWindow
|
||||||
|
type identifier C.uint
|
||||||
|
type pointer unsafe.Pointer
|
||||||
|
type GSList C.GSList
|
||||||
|
type GSListPointer *GSList
|
||||||
|
|
||||||
|
var (
|
||||||
|
nilRadioGroup GSListPointer = nil
|
||||||
|
gtkSignalHandlers map[*C.GtkWidget]C.gulong
|
||||||
|
gtkSignalToMenuItem map[*C.GtkWidget]*MenuItem
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
fmt.Println("linux_cgo")
|
||||||
|
|
||||||
|
gtkSignalHandlers = map[*C.GtkWidget]C.gulong{}
|
||||||
|
gtkSignalToMenuItem = map[*C.GtkWidget]*MenuItem{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mainthread stuff
|
||||||
|
func dispatchOnMainThread(id uint) {
|
||||||
|
C.dispatchOnMainThread(C.uint(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export dispatchOnMainThreadCallback
|
||||||
|
func dispatchOnMainThreadCallback(callbackID C.uint) {
|
||||||
|
executeOnMainThread(uint(callbackID))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export activateLinux
|
||||||
|
func activateLinux(data pointer) {
|
||||||
|
// NOOP: Callback for now
|
||||||
|
}
|
||||||
|
|
||||||
|
// implementation below
|
||||||
|
func appName() string {
|
||||||
|
name := C.g_get_application_name()
|
||||||
|
defer C.free(unsafe.Pointer(name))
|
||||||
|
return C.GoString(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appNew(name string) pointer {
|
||||||
|
nameC := C.CString(fmt.Sprintf("org.wails.%s", name))
|
||||||
|
defer C.free(unsafe.Pointer(nameC))
|
||||||
|
return pointer(C.gtk_application_new(nameC, C.G_APPLICATION_DEFAULT_FLAGS))
|
||||||
|
}
|
||||||
|
|
||||||
|
func appRun(app pointer) error {
|
||||||
|
application := (*C.GApplication)(app)
|
||||||
|
C.g_application_hold(application) // allows it to run without a window
|
||||||
|
signal := C.CString("activate")
|
||||||
|
C.g_signal_connect_data(C.gpointer(application), signal, C.GCallback(C.activateLinux), nil, nil, 0)
|
||||||
|
status := C.g_application_run(application, 0, nil)
|
||||||
|
C.g_application_release(application)
|
||||||
|
C.g_object_unref(C.gpointer(app))
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if status != 0 {
|
||||||
|
err = fmt.Errorf("exit code: %d", status)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func appDestroy(application pointer) {
|
||||||
|
C.g_application_quit((*C.GApplication)(application))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentWindowID(application pointer, windows map[windowPointer]uint) uint {
|
||||||
|
// TODO: Add extra metadata to window and use it!
|
||||||
|
window := (*C.GtkWindow)(C.gtk_application_get_active_window((*C.GtkApplication)(application)))
|
||||||
|
if window == nil {
|
||||||
|
return uint(1)
|
||||||
|
}
|
||||||
|
identifier, ok := windows[window]
|
||||||
|
if ok {
|
||||||
|
return identifier
|
||||||
|
}
|
||||||
|
// FIXME: Should we panic here if not found?
|
||||||
|
return uint(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWindows(application pointer) []pointer {
|
||||||
|
result := []pointer{}
|
||||||
|
windows := C.gtk_application_get_windows((*C.GtkApplication)(application))
|
||||||
|
for {
|
||||||
|
result = append(result, pointer(windows.data))
|
||||||
|
windows = windows.next
|
||||||
|
if windows == nil {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hideAllWindows(application pointer) {
|
||||||
|
for _, window := range getWindows(application) {
|
||||||
|
C.gtk_widget_hide((*C.GtkWidget)(window))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func showAllWindows(application pointer) {
|
||||||
|
for _, window := range getWindows(application) {
|
||||||
|
C.gtk_widget_show_all((*C.GtkWidget)(window))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Menu
|
||||||
|
func menuAddSeparator(menu *Menu) {
|
||||||
|
C.gtk_menu_shell_append(
|
||||||
|
(*C.GtkMenuShell)((menu.impl).(*linuxMenu).native),
|
||||||
|
C.gtk_separator_menu_item_new())
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuAppend(parent *Menu, menu *MenuItem) {
|
||||||
|
C.gtk_menu_shell_append(
|
||||||
|
(*C.GtkMenuShell)((parent.impl).(*linuxMenu).native),
|
||||||
|
(*C.GtkWidget)((menu.impl).(*linuxMenuItem).native),
|
||||||
|
)
|
||||||
|
/* gtk4
|
||||||
|
C.gtk_menu_item_set_submenu(
|
||||||
|
(*C.struct__GtkMenuItem)((menu.impl).(*linuxMenuItem).native),
|
||||||
|
(*C.struct__GtkWidget)((parent.impl).(*linuxMenu).native),
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuBarNew() pointer {
|
||||||
|
return pointer(C.gtk_menu_bar_new())
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuNew() pointer {
|
||||||
|
return pointer(C.gtk_menu_new())
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuSetSubmenu(item *MenuItem, menu *Menu) {
|
||||||
|
C.gtk_menu_item_set_submenu(
|
||||||
|
(*C.GtkMenuItem)((item.impl).(*linuxMenuItem).native),
|
||||||
|
(*C.GtkWidget)((menu.impl).(*linuxMenu).native))
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuGetRadioGroup(item *linuxMenuItem) *GSList {
|
||||||
|
return (*GSList)(C.gtk_radio_menu_item_get_group((*C.GtkRadioMenuItem)(item.native)))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export handleClick
|
||||||
|
func handleClick(idPtr unsafe.Pointer) {
|
||||||
|
id := (*C.GtkWidget)(idPtr)
|
||||||
|
item, ok := gtkSignalToMenuItem[id]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch item.itemType {
|
||||||
|
case text, checkbox:
|
||||||
|
menuItemClicked <- item.id
|
||||||
|
case radio:
|
||||||
|
menuItem := (item.impl).(*linuxMenuItem)
|
||||||
|
if menuItem.isChecked() {
|
||||||
|
menuItemClicked <- item.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func attachMenuHandler(item *MenuItem) {
|
||||||
|
signal := C.CString("activate")
|
||||||
|
defer C.free(unsafe.Pointer(signal))
|
||||||
|
|
||||||
|
impl := (item.impl).(*linuxMenuItem)
|
||||||
|
widget := impl.native
|
||||||
|
flags := C.GConnectFlags(0)
|
||||||
|
handlerId := C.g_signal_connect_object(
|
||||||
|
C.gpointer(widget),
|
||||||
|
signal,
|
||||||
|
C.GCallback(C.handleClick),
|
||||||
|
C.gpointer(widget),
|
||||||
|
flags)
|
||||||
|
|
||||||
|
id := (*C.GtkWidget)(widget)
|
||||||
|
gtkSignalToMenuItem[id] = item
|
||||||
|
gtkSignalHandlers[id] = handlerId
|
||||||
|
impl.handlerId = uint(handlerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// menuItem
|
||||||
|
func menuItemChecked(widget pointer) bool {
|
||||||
|
if C.gtk_check_menu_item_get_active((*C.GtkCheckMenuItem)(widget)) == C.int(1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuItemNew(label string) pointer {
|
||||||
|
cLabel := C.CString(label)
|
||||||
|
defer C.free(unsafe.Pointer(cLabel))
|
||||||
|
return pointer(C.gtk_menu_item_new_with_label(cLabel))
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuCheckItemNew(label string) pointer {
|
||||||
|
cLabel := C.CString(label)
|
||||||
|
defer C.free(unsafe.Pointer(cLabel))
|
||||||
|
return pointer(C.gtk_check_menu_item_new_with_label(cLabel))
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuItemSetChecked(widget pointer, checked bool) {
|
||||||
|
value := C.int(0)
|
||||||
|
if checked {
|
||||||
|
value = C.int(1)
|
||||||
|
}
|
||||||
|
C.gtk_check_menu_item_set_active(
|
||||||
|
(*C.GtkCheckMenuItem)(widget),
|
||||||
|
value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuItemSetDisabled(widget pointer, disabled bool) {
|
||||||
|
value := C.int(1)
|
||||||
|
if disabled {
|
||||||
|
value = C.int(0)
|
||||||
|
}
|
||||||
|
C.gtk_widget_set_sensitive(
|
||||||
|
(*C.GtkWidget)(widget),
|
||||||
|
value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuItemSetLabel(widget pointer, label string) {
|
||||||
|
value := C.CString(label)
|
||||||
|
C.gtk_menu_item_set_label(
|
||||||
|
(*C.GtkMenuItem)(widget),
|
||||||
|
value)
|
||||||
|
C.free(unsafe.Pointer(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuItemSetToolTip(widget pointer, tooltip string) {
|
||||||
|
value := C.CString(tooltip)
|
||||||
|
C.gtk_widget_set_tooltip_text(
|
||||||
|
(*C.GtkWidget)(widget),
|
||||||
|
value)
|
||||||
|
C.free(unsafe.Pointer(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuItemSignalBlock(widget pointer, handlerId uint, block bool) {
|
||||||
|
if block {
|
||||||
|
C.g_signal_handler_block(C.gpointer(widget), C.ulong(handlerId))
|
||||||
|
} else {
|
||||||
|
C.g_signal_handler_unblock(C.gpointer(widget), C.ulong(handlerId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func menuRadioItemNew(group *GSList, label string) pointer {
|
||||||
|
cLabel := C.CString(label)
|
||||||
|
defer C.free(unsafe.Pointer(cLabel))
|
||||||
|
return pointer(C.gtk_radio_menu_item_new_with_label((*C.GSList)(group), cLabel))
|
||||||
|
}
|
||||||
|
|
||||||
|
// screen related
|
||||||
|
|
||||||
|
func getScreenByIndex(display *C.struct__GdkDisplay, index int) *Screen {
|
||||||
|
monitor := C.gdk_display_get_monitor(display, C.int(index))
|
||||||
|
// TODO: Do we need to update Screen to contain current info?
|
||||||
|
// currentMonitor := C.gdk_display_get_monitor_at_window(display, window)
|
||||||
|
|
||||||
|
var geometry C.GdkRectangle
|
||||||
|
C.gdk_monitor_get_geometry(monitor, &geometry)
|
||||||
|
primary := false
|
||||||
|
if C.gdk_monitor_is_primary(monitor) == 1 {
|
||||||
|
primary = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Screen{
|
||||||
|
IsPrimary: primary,
|
||||||
|
Scale: 1.0,
|
||||||
|
X: int(geometry.x),
|
||||||
|
Y: int(geometry.y),
|
||||||
|
Size: Size{
|
||||||
|
Height: int(geometry.height),
|
||||||
|
Width: int(geometry.width),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getScreens(app pointer) ([]*Screen, error) {
|
||||||
|
var screens []*Screen
|
||||||
|
window := C.gtk_application_get_active_window((*C.GtkApplication)(app))
|
||||||
|
display := C.gdk_window_get_display((*C.GdkWindow)(unsafe.Pointer(window)))
|
||||||
|
count := C.gdk_display_get_n_monitors(display)
|
||||||
|
for i := 0; i < int(count); i++ {
|
||||||
|
screens = append(screens, getScreenByIndex(display, i))
|
||||||
|
}
|
||||||
|
return screens, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// widgets
|
||||||
|
func widgetSetSensitive(widget pointer, enabled bool) {
|
||||||
|
value := C.int(0)
|
||||||
|
if enabled {
|
||||||
|
value = C.int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
C.gtk_widget_set_sensitive((*C.GtkWidget)(widget), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func widgetSetVisible(widget pointer, hidden bool) {
|
||||||
|
if hidden {
|
||||||
|
C.gtk_widget_hide((*C.GtkWidget)(widget))
|
||||||
|
} else {
|
||||||
|
C.gtk_widget_show((*C.GtkWidget)(widget))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// window related functions
|
||||||
|
func windowClose(window pointer) {
|
||||||
|
C.gtk_window_close((*C.GtkWindow)(window))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowEnableDND(id uint, webview pointer) {
|
||||||
|
dnd := C.CString("text/uri-list")
|
||||||
|
defer C.free(unsafe.Pointer(dnd))
|
||||||
|
targetentry := C.gtk_target_entry_new(dnd, 0, C.guint(id))
|
||||||
|
defer C.gtk_target_entry_free(targetentry)
|
||||||
|
C.gtk_drag_dest_set((*C.GtkWidget)(webview), C.GTK_DEST_DEFAULT_DROP, targetentry, 1, C.GDK_ACTION_COPY)
|
||||||
|
event := C.CString("drag-data-received")
|
||||||
|
defer C.free(unsafe.Pointer(event))
|
||||||
|
windowId := C.uint(id)
|
||||||
|
C.signal_connect((*C.GtkWidget)(unsafe.Pointer(webview)), event, C.onDragNDrop, unsafe.Pointer(C.gpointer(&windowId)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowExecJS(webview pointer, js string) {
|
||||||
|
value := C.CString(js)
|
||||||
|
C.webkit_web_view_evaluate_javascript((*C.WebKitWebView)(webview),
|
||||||
|
value,
|
||||||
|
C.long(len(js)),
|
||||||
|
nil,
|
||||||
|
C.CString(""),
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil)
|
||||||
|
C.free(unsafe.Pointer(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowDestroy(window pointer) {
|
||||||
|
// Should this truly 'destroy' ?
|
||||||
|
C.gtk_window_close((*C.GtkWindow)(window))
|
||||||
|
//C.gtk_widget_destroy((*C.GtkWidget)(window))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowFullscreen(window pointer) {
|
||||||
|
C.gtk_window_fullscreen((*C.GtkWindow)(window))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowGetCurrentMonitor(window pointer) *C.GdkMonitor {
|
||||||
|
// Get the monitor that the window is currently on
|
||||||
|
display := C.gtk_widget_get_display((*C.GtkWidget)(window))
|
||||||
|
gdk_window := C.gtk_widget_get_window((*C.GtkWidget)(window))
|
||||||
|
if gdk_window == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return C.gdk_display_get_monitor_at_window(display, gdk_window)
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowGetCurrentMonitorGeometry(window pointer) (x int, y int, width int, height int, scale int) {
|
||||||
|
monitor := windowGetCurrentMonitor(window)
|
||||||
|
if monitor == nil {
|
||||||
|
return -1, -1, -1, -1, 1
|
||||||
|
}
|
||||||
|
var result C.GdkRectangle
|
||||||
|
C.gdk_monitor_get_geometry(monitor, &result)
|
||||||
|
scale = int(C.gdk_monitor_get_scale_factor(monitor))
|
||||||
|
return int(result.x), int(result.y), int(result.width), int(result.height), scale
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowGetAbsolutePosition(window pointer) (int, int) {
|
||||||
|
var x C.int
|
||||||
|
var y C.int
|
||||||
|
C.gtk_window_get_position((*C.GtkWindow)(window), &x, &y)
|
||||||
|
return int(x), int(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowGetSize(window pointer) (int, int) {
|
||||||
|
var windowWidth C.int
|
||||||
|
var windowHeight C.int
|
||||||
|
C.gtk_window_get_size((*C.GtkWindow)(window), &windowWidth, &windowHeight)
|
||||||
|
return int(windowWidth), int(windowHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowGetRelativePosition(window pointer) (int, int) {
|
||||||
|
x, y := windowGetAbsolutePosition(window)
|
||||||
|
// The position must be relative to the screen it is on
|
||||||
|
// We need to get the screen it is on
|
||||||
|
monitor := windowGetCurrentMonitor(window)
|
||||||
|
geometry := C.GdkRectangle{}
|
||||||
|
C.gdk_monitor_get_geometry(monitor, &geometry)
|
||||||
|
x = x - int(geometry.x)
|
||||||
|
y = y - int(geometry.y)
|
||||||
|
|
||||||
|
// TODO: Scale based on DPI
|
||||||
|
|
||||||
|
return x, y
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowHide(window pointer) {
|
||||||
|
C.gtk_widget_hide((*C.GtkWidget)(window))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowIsFullscreen(window pointer) bool {
|
||||||
|
gdkwindow := C.gtk_widget_get_window((*C.GtkWidget)(window))
|
||||||
|
state := C.gdk_window_get_state(gdkwindow)
|
||||||
|
return state&C.GDK_WINDOW_STATE_FULLSCREEN > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowIsMaximized(window pointer) bool {
|
||||||
|
gdkwindow := C.gtk_widget_get_window((*C.GtkWidget)(window))
|
||||||
|
state := C.gdk_window_get_state(gdkwindow)
|
||||||
|
return state&C.GDK_WINDOW_STATE_MAXIMIZED > 0 && state&C.GDK_WINDOW_STATE_FULLSCREEN == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowIsMinimized(window pointer) bool {
|
||||||
|
gdkwindow := C.gtk_widget_get_window((*C.GtkWidget)(window))
|
||||||
|
state := C.gdk_window_get_state(gdkwindow)
|
||||||
|
return state&C.GDK_WINDOW_STATE_ICONIFIED > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowIsVisible(window pointer) bool {
|
||||||
|
if C.gtk_widget_is_visible((*C.GtkWidget)(window)) == 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowMaximize(window pointer) {
|
||||||
|
C.gtk_window_maximize((*C.GtkWindow)(window))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowMinimize(window pointer) {
|
||||||
|
C.gtk_window_iconify((*C.GtkWindow)(window))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowNew(application pointer, menu pointer, windowId uint, gpuPolicy int) (window pointer, webview pointer) {
|
||||||
|
window = pointer(C.gtk_application_window_new((*C.GtkApplication)(application)))
|
||||||
|
C.g_object_ref_sink(C.gpointer(window))
|
||||||
|
webview = windowNewWebview(windowId, gpuPolicy)
|
||||||
|
vbox := pointer(C.gtk_box_new(C.GTK_ORIENTATION_VERTICAL, 0))
|
||||||
|
C.gtk_container_add((*C.GtkContainer)(window), (*C.GtkWidget)(vbox))
|
||||||
|
|
||||||
|
if menu != nil {
|
||||||
|
C.gtk_box_pack_start((*C.GtkBox)(vbox), (*C.GtkWidget)(menu), 0, 0, 0)
|
||||||
|
}
|
||||||
|
C.gtk_box_pack_start((*C.GtkBox)(unsafe.Pointer(vbox)), (*C.GtkWidget)(webview), 1, 1, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowNewWebview(parentId uint, gpuPolicy int) pointer {
|
||||||
|
manager := C.webkit_user_content_manager_new()
|
||||||
|
external := C.CString("external")
|
||||||
|
C.webkit_user_content_manager_register_script_message_handler(manager, external)
|
||||||
|
C.free(unsafe.Pointer(external))
|
||||||
|
webview := C.webkit_web_view_new_with_user_content_manager(manager)
|
||||||
|
id := C.uint(parentId)
|
||||||
|
if !registered {
|
||||||
|
wails := C.CString("wails")
|
||||||
|
C.webkit_web_context_register_uri_scheme(
|
||||||
|
C.webkit_web_context_get_default(),
|
||||||
|
wails,
|
||||||
|
C.WebKitURISchemeRequestCallback(C.onProcessRequest),
|
||||||
|
C.gpointer(&id),
|
||||||
|
nil)
|
||||||
|
registered = true
|
||||||
|
C.free(unsafe.Pointer(wails))
|
||||||
|
}
|
||||||
|
settings := C.webkit_web_view_get_settings((*C.WebKitWebView)(unsafe.Pointer(webview)))
|
||||||
|
wails_io := C.CString("wails.io")
|
||||||
|
empty := C.CString("")
|
||||||
|
defer C.free(unsafe.Pointer(wails_io))
|
||||||
|
defer C.free(unsafe.Pointer(empty))
|
||||||
|
C.webkit_settings_set_user_agent_with_application_details(settings, wails_io, empty)
|
||||||
|
|
||||||
|
switch gpuPolicy {
|
||||||
|
case 0:
|
||||||
|
C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS)
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND)
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND)
|
||||||
|
}
|
||||||
|
return pointer(webview)
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowPresent(window pointer) {
|
||||||
|
C.gtk_window_present((*C.GtkWindow)(window))
|
||||||
|
// gtk_window_unminimize ((*C.GtkWindow)(w.window)) /// gtk4
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowReload(webview pointer, address string) {
|
||||||
|
uri := C.CString(address)
|
||||||
|
C.webkit_web_view_load_uri((*C.WebKitWebView)(webview), uri)
|
||||||
|
C.free(unsafe.Pointer(uri))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowResize(window pointer, width, height int) {
|
||||||
|
C.gtk_window_resize(
|
||||||
|
(*C.GtkWindow)(window),
|
||||||
|
C.gint(width),
|
||||||
|
C.gint(height))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowShow(window pointer) {
|
||||||
|
C.gtk_widget_show_all((*C.GtkWidget)(window))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowSetBackgroundColour(webview pointer, colour RGBA) {
|
||||||
|
rgba := C.GdkRGBA{C.double(colour.Red) / 255.0, C.double(colour.Green) / 255.0, C.double(colour.Blue) / 255.0, C.double(colour.Alpha) / 255.0}
|
||||||
|
C.webkit_web_view_set_background_color((*C.WebKitWebView)(webview), &rgba)
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowSetGeometryHints(window pointer, minWidth, minHeight, maxWidth, maxHeight int) {
|
||||||
|
size := C.GdkGeometry{
|
||||||
|
min_width: C.int(minWidth),
|
||||||
|
min_height: C.int(minHeight),
|
||||||
|
max_width: C.int(maxWidth),
|
||||||
|
max_height: C.int(maxHeight),
|
||||||
|
}
|
||||||
|
C.gtk_window_set_geometry_hints((*C.GtkWindow)(window), nil, &size, C.GDK_HINT_MAX_SIZE|C.GDK_HINT_MIN_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowSetFrameless(window pointer, frameless bool) {
|
||||||
|
C.gtk_window_set_decorated((*C.GtkWindow)(window), gtkBool(!frameless))
|
||||||
|
// TODO: Deal with transparency for the titlebar if possible when !frameless
|
||||||
|
// Perhaps we just make it undecorated and add a menu bar inside?
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: confirm this is working properly
|
||||||
|
func windowSetHTML(webview pointer, html string) {
|
||||||
|
cHTML := C.CString(html)
|
||||||
|
uri := C.CString("wails://")
|
||||||
|
empty := C.CString("")
|
||||||
|
defer C.free(unsafe.Pointer(cHTML))
|
||||||
|
defer C.free(unsafe.Pointer(uri))
|
||||||
|
defer C.free(unsafe.Pointer(empty))
|
||||||
|
C.webkit_web_view_load_alternate_html(
|
||||||
|
(*C.WebKitWebView)(webview),
|
||||||
|
cHTML,
|
||||||
|
uri,
|
||||||
|
empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowSetKeepAbove(window pointer, alwaysOnTop bool) {
|
||||||
|
C.gtk_window_set_keep_above((*C.GtkWindow)(window), gtkBool(alwaysOnTop))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowSetResizable(window pointer, resizable bool) {
|
||||||
|
C.gtk_window_set_resizable((*C.GtkWindow)(window), gtkBool(resizable))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowSetTitle(window pointer, title string) {
|
||||||
|
cTitle := C.CString(title)
|
||||||
|
C.gtk_window_set_title((*C.GtkWindow)(window), cTitle)
|
||||||
|
C.free(unsafe.Pointer(cTitle))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowSetTransparent(window pointer) {
|
||||||
|
screen := C.gtk_widget_get_screen((*C.GtkWidget)(window))
|
||||||
|
visual := C.gdk_screen_get_rgba_visual(screen)
|
||||||
|
|
||||||
|
if visual != nil && C.gdk_screen_is_composited(screen) == C.int(1) {
|
||||||
|
C.gtk_widget_set_app_paintable((*C.GtkWidget)(window), C.gboolean(1))
|
||||||
|
C.gtk_widget_set_visual((*C.GtkWidget)(window), visual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowSetURL(webview pointer, uri string) {
|
||||||
|
target := C.CString(uri)
|
||||||
|
C.webkit_web_view_load_uri((*C.WebKitWebView)(webview), target)
|
||||||
|
C.free(unsafe.Pointer(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export emit
|
||||||
|
func emit(we *C.WindowEvent) {
|
||||||
|
window := globalApplication.getWindowForID(uint(we.id))
|
||||||
|
if window != nil {
|
||||||
|
window.emit(events.WindowEventType(we.event))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowSetupSignalHandlers(windowId uint, window, webview pointer, emit func(e events.WindowEventType)) {
|
||||||
|
event := C.CString("delete-event")
|
||||||
|
defer C.free(unsafe.Pointer(event))
|
||||||
|
wEvent := C.WindowEvent{
|
||||||
|
id: C.uint(windowId),
|
||||||
|
event: C.uint(events.Common.WindowClosing),
|
||||||
|
}
|
||||||
|
C.signal_connect((*C.GtkWidget)(window), event, C.emit, unsafe.Pointer(&wEvent))
|
||||||
|
|
||||||
|
/*
|
||||||
|
event = C.CString("load-changed")
|
||||||
|
defer C.free(unsafe.Pointer(event))
|
||||||
|
C.signal_connect(webview, event, C.webviewLoadChanged, unsafe.Pointer(&w.parent.id))
|
||||||
|
*/
|
||||||
|
id := C.uint(windowId)
|
||||||
|
event = C.CString("button-press-event")
|
||||||
|
C.signal_connect((*C.GtkWidget)(unsafe.Pointer(webview)), event, C.onButtonEvent, unsafe.Pointer(&id))
|
||||||
|
C.free(unsafe.Pointer(event))
|
||||||
|
event = C.CString("button-release-event")
|
||||||
|
defer C.free(unsafe.Pointer(event))
|
||||||
|
C.signal_connect((*C.GtkWidget)(unsafe.Pointer(webview)), event, C.onButtonEvent, unsafe.Pointer(&id))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowToggleDevTools(webview pointer) {
|
||||||
|
settings := C.webkit_web_view_get_settings((*C.WebKitWebView)(webview))
|
||||||
|
enabled := C.webkit_settings_get_enable_developer_extras(settings)
|
||||||
|
switch enabled {
|
||||||
|
case C.int(0):
|
||||||
|
enabled = C.int(1)
|
||||||
|
case C.int(1):
|
||||||
|
enabled = C.int(0)
|
||||||
|
}
|
||||||
|
C.webkit_settings_set_enable_developer_extras(settings, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowUnfullscreen(window pointer) {
|
||||||
|
C.gtk_window_unfullscreen((*C.GtkWindow)(window))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowUnmaximize(window pointer) {
|
||||||
|
C.gtk_window_unmaximize((*C.GtkWindow)(window))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowZoom(webview pointer) float64 {
|
||||||
|
return float64(C.webkit_web_view_get_zoom_level((*C.WebKitWebView)(webview)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: ZoomIn/Out is assumed to be incorrect!
|
||||||
|
func windowZoomIn(webview pointer) {
|
||||||
|
ZoomInFactor := 1.10
|
||||||
|
windowZoomSet(webview, windowZoom(webview)*ZoomInFactor)
|
||||||
|
}
|
||||||
|
func windowZoomOut(webview pointer) {
|
||||||
|
ZoomOutFactor := -1.10
|
||||||
|
windowZoomSet(webview, windowZoom(webview)*ZoomOutFactor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowZoomSet(webview pointer, zoom float64) {
|
||||||
|
C.webkit_web_view_set_zoom_level((*C.WebKitWebView)(webview), C.double(zoom))
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowMove(window pointer, x, y int) {
|
||||||
|
C.gtk_window_move((*C.GtkWindow)(window), C.int(x), C.int(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export onButtonEvent
|
||||||
|
func onButtonEvent(_ *C.GtkWidget, event *C.GdkEventButton, data unsafe.Pointer) C.gboolean {
|
||||||
|
// Constants (defined here to be easier to use with )
|
||||||
|
GdkButtonPress := C.GDK_BUTTON_PRESS // 4
|
||||||
|
Gdk2ButtonPress := C.GDK_2BUTTON_PRESS // 5 for double-click
|
||||||
|
GdkButtonRelease := C.GDK_BUTTON_RELEASE // 7
|
||||||
|
|
||||||
|
windowId := uint(*((*C.uint)(data)))
|
||||||
|
window := globalApplication.getWindowForID(windowId)
|
||||||
|
if window == nil {
|
||||||
|
return C.gboolean(0)
|
||||||
|
}
|
||||||
|
lw, ok := (window.impl).(*linuxWebviewWindow)
|
||||||
|
if !ok {
|
||||||
|
return C.gboolean(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if event == nil {
|
||||||
|
return C.gboolean(0)
|
||||||
|
}
|
||||||
|
if event.button == 3 {
|
||||||
|
return C.gboolean(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch int(event._type) {
|
||||||
|
case GdkButtonPress:
|
||||||
|
lw.startDrag() //uint(event.button), int(event.x_root), int(event.y_root))
|
||||||
|
case Gdk2ButtonPress:
|
||||||
|
fmt.Printf("%d - button %d - double-clicked\n", windowId, int(event.button))
|
||||||
|
case GdkButtonRelease:
|
||||||
|
lw.endDrag(uint(event.button), int(event.x_root), int(event.y_root))
|
||||||
|
}
|
||||||
|
|
||||||
|
return C.gboolean(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export onDragNDrop
|
||||||
|
func onDragNDrop(target unsafe.Pointer, context *C.GdkDragContext, x C.gint, y C.gint, seldata unsafe.Pointer, info C.guint, time C.guint, data unsafe.Pointer) {
|
||||||
|
var length C.gint
|
||||||
|
selection := unsafe.Pointer(C.gtk_selection_data_get_data_with_length((*C.GtkSelectionData)(seldata), &length))
|
||||||
|
extracted := C.g_uri_list_extract_uris((*C.char)(selection))
|
||||||
|
defer C.g_strfreev(extracted)
|
||||||
|
|
||||||
|
uris := unsafe.Slice(
|
||||||
|
(**C.char)(unsafe.Pointer(extracted)),
|
||||||
|
int(length))
|
||||||
|
|
||||||
|
var filenames []string
|
||||||
|
for _, uri := range uris {
|
||||||
|
if uri == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
filenames = append(filenames, strings.TrimPrefix(C.GoString(uri), "file://"))
|
||||||
|
}
|
||||||
|
windowDragAndDropBuffer <- &dragAndDropMessage{
|
||||||
|
windowId: uint(*((*C.uint)(data))),
|
||||||
|
filenames: filenames,
|
||||||
|
}
|
||||||
|
C.gtk_drag_finish(context, C.true, C.false, time)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export onProcessRequest
|
||||||
|
func onProcessRequest(request unsafe.Pointer, data unsafe.Pointer) {
|
||||||
|
windowId := uint(*((*C.uint)(data)))
|
||||||
|
webviewRequests <- &webViewAssetRequest{
|
||||||
|
Request: webview.NewRequest(request),
|
||||||
|
windowId: windowId,
|
||||||
|
windowName: globalApplication.getWindowForID(windowId).Name(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func gtkBool(input bool) C.gboolean {
|
||||||
|
if input {
|
||||||
|
return C.gboolean(1)
|
||||||
|
}
|
||||||
|
return C.gboolean(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialog related
|
||||||
|
|
||||||
|
func setWindowIcon(window pointer, icon []byte) {
|
||||||
|
fmt.Println("setWindowIcon", len(icon))
|
||||||
|
loader := C.gdk_pixbuf_loader_new()
|
||||||
|
if loader == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
written := C.gdk_pixbuf_loader_write(
|
||||||
|
loader,
|
||||||
|
(*C.uchar)(&icon[0]),
|
||||||
|
C.ulong(len(icon)),
|
||||||
|
nil)
|
||||||
|
if written == 0 {
|
||||||
|
fmt.Println("failed to write icon")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
C.gdk_pixbuf_loader_close(loader, nil)
|
||||||
|
pixbuf := C.gdk_pixbuf_loader_get_pixbuf(loader)
|
||||||
|
if pixbuf != nil {
|
||||||
|
fmt.Println("gtk_window_set_icon", window)
|
||||||
|
C.gtk_window_set_icon((*C.GtkWindow)(window), pixbuf)
|
||||||
|
}
|
||||||
|
C.g_object_unref(C.gpointer(loader))
|
||||||
|
}
|
||||||
|
|
||||||
|
//export messageDialogCB
|
||||||
|
func messageDialogCB(button C.int) {
|
||||||
|
fmt.Println("messageDialogCB", button)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func runOpenFileDialog(dialog *OpenFileDialog) ([]string, error) {
|
||||||
|
return []string{}, fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func runQuestionDialog(parent pointer, options *MessageDialog) int {
|
||||||
|
cMsg := C.CString(options.Message)
|
||||||
|
cTitle := C.CString(options.Title)
|
||||||
|
defer C.free(unsafe.Pointer(cMsg))
|
||||||
|
defer C.free(unsafe.Pointer(cTitle))
|
||||||
|
hasButtons := false
|
||||||
|
if len(options.Buttons) > 0 {
|
||||||
|
hasButtons = true
|
||||||
|
}
|
||||||
|
|
||||||
|
dType, ok := map[DialogType]C.int{
|
||||||
|
InfoDialog: C.GTK_MESSAGE_INFO,
|
||||||
|
QuestionDialog: C.GTK_MESSAGE_QUESTION,
|
||||||
|
WarningDialog: C.GTK_MESSAGE_WARNING,
|
||||||
|
}[options.DialogType]
|
||||||
|
if !ok {
|
||||||
|
// FIXME: Add logging here!
|
||||||
|
dType = C.GTK_MESSAGE_INFO
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog := C.new_message_dialog((*C.GtkWindow)(parent), cMsg, dType, C.bool(hasButtons))
|
||||||
|
if options.Title != "" {
|
||||||
|
C.gtk_window_set_title(
|
||||||
|
(*C.GtkWindow)(unsafe.Pointer(dialog)),
|
||||||
|
cTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if img, err := pngToImage(options.Icon); err == nil {
|
||||||
|
gbytes := C.g_bytes_new_static(
|
||||||
|
C.gconstpointer(unsafe.Pointer(&img.Pix[0])),
|
||||||
|
C.ulong(len(img.Pix)))
|
||||||
|
defer C.g_bytes_unref(gbytes)
|
||||||
|
pixBuf := C.gdk_pixbuf_new_from_bytes(
|
||||||
|
gbytes,
|
||||||
|
C.GDK_COLORSPACE_RGB,
|
||||||
|
1, // has_alpha
|
||||||
|
8,
|
||||||
|
C.int(img.Bounds().Dx()),
|
||||||
|
C.int(img.Bounds().Dy()),
|
||||||
|
C.int(img.Stride),
|
||||||
|
)
|
||||||
|
image := C.gtk_image_new_from_pixbuf(pixBuf)
|
||||||
|
C.gtk_widget_set_visible((*C.GtkWidget)(image), C.gboolean(1))
|
||||||
|
contentArea := C.gtk_dialog_get_content_area((*C.GtkDialog)(dialog))
|
||||||
|
C.gtk_container_add(
|
||||||
|
(*C.GtkContainer)(unsafe.Pointer(contentArea)),
|
||||||
|
(*C.GtkWidget)(image))
|
||||||
|
}
|
||||||
|
for i, button := range options.Buttons {
|
||||||
|
cLabel := C.CString(button.Label)
|
||||||
|
defer C.free(unsafe.Pointer(cLabel))
|
||||||
|
index := C.int(i)
|
||||||
|
C.gtk_dialog_add_button(
|
||||||
|
(*C.GtkDialog)(dialog), cLabel, index)
|
||||||
|
if button.IsDefault {
|
||||||
|
C.gtk_dialog_set_default_response((*C.GtkDialog)(dialog), index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer C.gtk_widget_destroy((*C.GtkWidget)(dialog))
|
||||||
|
return int(C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(dialog))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSaveFileDialog(dialog *SaveFileDialog) (string, error) {
|
||||||
|
return "", fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
//export openFileDialogCallbackEnd
|
||||||
|
func openFileDialogCallbackEnd(cid C.uint) {
|
||||||
|
id := uint(cid)
|
||||||
|
channel, ok := openFileResponses[id]
|
||||||
|
if ok {
|
||||||
|
close(channel)
|
||||||
|
delete(openFileResponses, id)
|
||||||
|
freeDialogID(id)
|
||||||
|
} else {
|
||||||
|
panic("No channel found for open file dialog")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//export openFileDialogCallback
|
||||||
|
func openFileDialogCallback(cid C.uint, cpath *C.char) {
|
||||||
|
path := C.GoString(cpath)
|
||||||
|
id := uint(cid)
|
||||||
|
channel, ok := openFileResponses[id]
|
||||||
|
if ok {
|
||||||
|
channel <- path
|
||||||
|
} else {
|
||||||
|
panic("No channel found for open file dialog")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//export saveFileDialogCallback
|
||||||
|
func saveFileDialogCallback(cid C.uint, cpath *C.char) {
|
||||||
|
// Covert the path to a string
|
||||||
|
path := C.GoString(cpath)
|
||||||
|
id := uint(cid)
|
||||||
|
// put response on channel
|
||||||
|
channel, ok := saveFileResponses[id]
|
||||||
|
if ok {
|
||||||
|
channel <- path
|
||||||
|
close(channel)
|
||||||
|
delete(saveFileResponses, id)
|
||||||
|
freeDialogID(id)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
panic("No channel found for save file dialog")
|
||||||
|
}
|
||||||
|
}
|
1151
v3/pkg/application/linux_purego.go
Normal file
1151
v3/pkg/application/linux_purego.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,50 +2,17 @@
|
|||||||
|
|
||||||
package application
|
package application
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux pkg-config: gtk+-3.0
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "gtk/gtk.h"
|
|
||||||
|
|
||||||
typedef struct CallbackID
|
|
||||||
{
|
|
||||||
unsigned int value;
|
|
||||||
} CallbackID;
|
|
||||||
|
|
||||||
extern void dispatchOnMainThreadCallback(unsigned int);
|
|
||||||
|
|
||||||
static gboolean dispatchCallback(gpointer data) {
|
|
||||||
struct CallbackID *args = data;
|
|
||||||
unsigned int cid = args->value;
|
|
||||||
dispatchOnMainThreadCallback(cid);
|
|
||||||
free(args);
|
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void dispatchOnMainThread(unsigned int id) {
|
|
||||||
CallbackID *args = malloc(sizeof(CallbackID));
|
|
||||||
args->value = id;
|
|
||||||
g_idle_add((GSourceFunc)dispatchCallback, (gpointer)args);
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
func (m *linuxApp) dispatchOnMainThread(id uint) {
|
func (m *linuxApp) dispatchOnMainThread(id uint) {
|
||||||
C.dispatchOnMainThread(C.uint(id))
|
dispatchOnMainThread(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export dispatchOnMainThreadCallback
|
func executeOnMainThread(callbackID uint) {
|
||||||
func dispatchOnMainThreadCallback(callbackID C.uint) {
|
|
||||||
mainThreadFunctionStoreLock.RLock()
|
mainThreadFunctionStoreLock.RLock()
|
||||||
id := uint(callbackID)
|
fn := mainThreadFunctionStore[callbackID]
|
||||||
fn := mainThreadFunctionStore[id]
|
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
Fatal("dispatchCallback called with invalid id: %v", id)
|
Fatal("dispatchCallback called with invalid id: %v", callbackID)
|
||||||
}
|
}
|
||||||
delete(mainThreadFunctionStore, id)
|
delete(mainThreadFunctionStore, callbackID)
|
||||||
mainThreadFunctionStoreLock.RUnlock()
|
mainThreadFunctionStoreLock.RUnlock()
|
||||||
fn()
|
fn()
|
||||||
}
|
}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
//go:build linux && purego
|
|
||||||
|
|
||||||
package application
|
|
||||||
|
|
||||||
import "github.com/ebitengine/purego"
|
|
||||||
|
|
||||||
const (
|
|
||||||
G_SOURCE_REMOVE = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
func (m *linuxApp) dispatchOnMainThread(id uint) {
|
|
||||||
var dispatch func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&dispatch, gtk, "g_idle_add")
|
|
||||||
dispatch(purego.NewCallback(func(uintptr) int {
|
|
||||||
dispatchOnMainThreadCallback(id)
|
|
||||||
return G_SOURCE_REMOVE
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func dispatchOnMainThreadCallback(callbackID uint) {
|
|
||||||
mainThreadFunctionStoreLock.RLock()
|
|
||||||
id := uint(callbackID)
|
|
||||||
fn := mainThreadFunctionStore[id]
|
|
||||||
if fn == nil {
|
|
||||||
Fatal("dispatchCallback called with invalid id: %v", id)
|
|
||||||
}
|
|
||||||
delete(mainThreadFunctionStore, id)
|
|
||||||
mainThreadFunctionStoreLock.RUnlock()
|
|
||||||
fn()
|
|
||||||
}
|
|
@ -2,70 +2,20 @@
|
|||||||
|
|
||||||
package application
|
package application
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gdk/gdk.h>
|
|
||||||
|
|
||||||
void handleClick(void*);
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
gtkSignalHandlers map[*C.GtkWidget]C.gulong
|
|
||||||
gtkSignalToMenuItem map[*C.GtkWidget]*MenuItem
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
gtkSignalHandlers = map[*C.GtkWidget]C.gulong{}
|
|
||||||
gtkSignalToMenuItem = map[*C.GtkWidget]*MenuItem{}
|
|
||||||
}
|
|
||||||
|
|
||||||
//export handleClick
|
|
||||||
func handleClick(idPtr unsafe.Pointer) {
|
|
||||||
id := (*C.GtkWidget)(idPtr)
|
|
||||||
item, ok := gtkSignalToMenuItem[id]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//impl := (item.impl).(*linuxMenuItem)
|
|
||||||
|
|
||||||
switch item.itemType {
|
|
||||||
case text, checkbox:
|
|
||||||
processMenuItemClick(C.uint(item.id))
|
|
||||||
case radio:
|
|
||||||
menuItem := (item.impl).(*linuxMenuItem)
|
|
||||||
if menuItem.isChecked() {
|
|
||||||
processMenuItemClick(C.uint(item.id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type linuxMenu struct {
|
type linuxMenu struct {
|
||||||
menu *Menu
|
menu *Menu
|
||||||
native unsafe.Pointer
|
native pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMenuImpl(menu *Menu) *linuxMenu {
|
func newMenuImpl(menu *Menu) *linuxMenu {
|
||||||
result := &linuxMenu{
|
result := &linuxMenu{
|
||||||
menu: menu,
|
menu: menu,
|
||||||
native: unsafe.Pointer(C.gtk_menu_bar_new()),
|
native: menuBarNew(),
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxMenu) update() {
|
func (m *linuxMenu) update() {
|
||||||
// fmt.Println("linuxMenu.update()")
|
|
||||||
// if m.native != nil {
|
|
||||||
// C.gtk_widget_destroy((*C.GtkWidget)(m.native))
|
|
||||||
// m.native = unsafe.Pointer(C.gtk_menu_new())
|
|
||||||
// }
|
|
||||||
m.processMenu(m.menu)
|
m.processMenu(m.menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,15 +23,15 @@ func (m *linuxMenu) processMenu(menu *Menu) {
|
|||||||
if menu.impl == nil {
|
if menu.impl == nil {
|
||||||
menu.impl = &linuxMenu{
|
menu.impl = &linuxMenu{
|
||||||
menu: menu,
|
menu: menu,
|
||||||
native: unsafe.Pointer(C.gtk_menu_new()),
|
native: menuNew(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var currentRadioGroup *C.GSList
|
var currentRadioGroup GSListPointer
|
||||||
|
|
||||||
for _, item := range menu.items {
|
for _, item := range menu.items {
|
||||||
// drop the group if we have run out of radio items
|
// drop the group if we have run out of radio items
|
||||||
if item.itemType != radio {
|
if item.itemType != radio {
|
||||||
currentRadioGroup = nil
|
currentRadioGroup = nilRadioGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
switch item.itemType {
|
switch item.itemType {
|
||||||
@ -99,7 +49,7 @@ func (m *linuxMenu) processMenu(menu *Menu) {
|
|||||||
menuItem := newRadioItemImpl(item, currentRadioGroup)
|
menuItem := newRadioItemImpl(item, currentRadioGroup)
|
||||||
item.impl = menuItem
|
item.impl = menuItem
|
||||||
m.addMenuItem(menu, item)
|
m.addMenuItem(menu, item)
|
||||||
currentRadioGroup = C.gtk_radio_menu_item_get_group((*C.GtkRadioMenuItem)(menuItem.native))
|
currentRadioGroup = menuGetRadioGroup(menuItem)
|
||||||
case separator:
|
case separator:
|
||||||
m.addMenuSeparator(menu)
|
m.addMenuSeparator(menu)
|
||||||
}
|
}
|
||||||
@ -115,66 +65,30 @@ func (m *linuxMenu) processMenu(menu *Menu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxMenu) attachHandler(item *MenuItem) {
|
func (m *linuxMenu) attachHandler(item *MenuItem) {
|
||||||
signal := C.CString("activate")
|
attachMenuHandler(item)
|
||||||
defer C.free(unsafe.Pointer(signal))
|
|
||||||
|
|
||||||
impl := (item.impl).(*linuxMenuItem)
|
|
||||||
widget := impl.native
|
|
||||||
flags := C.GConnectFlags(0)
|
|
||||||
handlerId := C.g_signal_connect_object(
|
|
||||||
C.gpointer(widget),
|
|
||||||
signal,
|
|
||||||
C.GCallback(C.handleClick),
|
|
||||||
C.gpointer(widget),
|
|
||||||
flags)
|
|
||||||
|
|
||||||
id := (*C.GtkWidget)(widget)
|
|
||||||
gtkSignalToMenuItem[id] = item
|
|
||||||
gtkSignalHandlers[id] = handlerId
|
|
||||||
impl.handlerId = handlerId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxMenu) addSubMenuToItem(menu *Menu, item *MenuItem) {
|
func (m *linuxMenu) addSubMenuToItem(menu *Menu, item *MenuItem) {
|
||||||
if menu.impl == nil {
|
if menu.impl == nil {
|
||||||
menu.impl = &linuxMenu{
|
menu.impl = &linuxMenu{
|
||||||
menu: menu,
|
menu: menu,
|
||||||
native: unsafe.Pointer(C.gtk_menu_new()),
|
native: menuNew(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
menuSetSubmenu(item, menu)
|
||||||
C.gtk_menu_item_set_submenu(
|
|
||||||
(*C.GtkMenuItem)((item.impl).(*linuxMenuItem).native),
|
|
||||||
(*C.GtkWidget)((menu.impl).(*linuxMenu).native))
|
|
||||||
|
|
||||||
if item.role == ServicesMenu {
|
|
||||||
// FIXME: what does this mean?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxMenu) addMenuItem(parent *Menu, menu *MenuItem) {
|
func (m *linuxMenu) addMenuItem(parent *Menu, menu *MenuItem) {
|
||||||
// fmt.Println("addMenuIteam", fmt.Sprintf("%+v", parent), fmt.Sprintf("%+v", menu))
|
menuAppend(parent, menu)
|
||||||
C.gtk_menu_shell_append(
|
|
||||||
(*C.GtkMenuShell)((parent.impl).(*linuxMenu).native),
|
|
||||||
(*C.GtkWidget)((menu.impl).(*linuxMenuItem).native),
|
|
||||||
)
|
|
||||||
/*
|
|
||||||
C.gtk_menu_item_set_submenu(
|
|
||||||
(*C.struct__GtkMenuItem)((menu.impl).(*linuxMenuItem).native),
|
|
||||||
(*C.struct__GtkWidget)((parent.impl).(*linuxMenu).native),
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxMenu) addMenuSeparator(menu *Menu) {
|
func (m *linuxMenu) addMenuSeparator(menu *Menu) {
|
||||||
// fmt.Println("addMenuSeparator", fmt.Sprintf("%+v", menu))
|
menuAddSeparator(menu)
|
||||||
sep := C.gtk_separator_menu_item_new()
|
|
||||||
native := (menu.impl).(*linuxMenu).native
|
|
||||||
C.gtk_menu_shell_append((*C.GtkMenuShell)(native), sep)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxMenu) addServicesMenu(menu *Menu) {
|
func (m *linuxMenu) addServicesMenu(menu *Menu) {
|
||||||
fmt.Println("addServicesMenu - not implemented")
|
// FIXME: Should this be required?
|
||||||
//C.addServicesMenu(unsafe.Pointer(menu.impl.(*linuxMenu).nsMenu))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linuxMenu) createMenu(name string, items []*MenuItem) *Menu {
|
func (l *linuxMenu) createMenu(name string, items []*MenuItem) *Menu {
|
||||||
|
@ -1,179 +0,0 @@
|
|||||||
//go:build linux && purego
|
|
||||||
|
|
||||||
package application
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ebitengine/purego"
|
|
||||||
)
|
|
||||||
|
|
||||||
type linuxMenu struct {
|
|
||||||
menu *Menu
|
|
||||||
native uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMenuImpl(menu *Menu) *linuxMenu {
|
|
||||||
var newMenuBar func() uintptr
|
|
||||||
purego.RegisterLibFunc(&newMenuBar, gtk, "gtk_menu_bar_new")
|
|
||||||
result := &linuxMenu{
|
|
||||||
menu: menu,
|
|
||||||
native: newMenuBar(),
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxMenu) update() {
|
|
||||||
m.processMenu(m.menu)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxMenu) processMenu(menu *Menu) {
|
|
||||||
var newMenu func() uintptr
|
|
||||||
purego.RegisterLibFunc(&newMenu, gtk, "gtk_menu_new")
|
|
||||||
if menu.impl == nil {
|
|
||||||
menu.impl = &linuxMenu{
|
|
||||||
menu: menu,
|
|
||||||
native: newMenu(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var currentRadioGroup uintptr
|
|
||||||
|
|
||||||
for _, item := range menu.items {
|
|
||||||
// drop the group if we have run out of radio items
|
|
||||||
if item.itemType != radio {
|
|
||||||
currentRadioGroup = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
switch item.itemType {
|
|
||||||
case submenu:
|
|
||||||
menuItem := newMenuItemImpl(item)
|
|
||||||
item.impl = menuItem
|
|
||||||
m.processMenu(item.submenu)
|
|
||||||
m.addSubMenuToItem(item.submenu, item)
|
|
||||||
m.addMenuItem(menu, item)
|
|
||||||
case text, checkbox:
|
|
||||||
menuItem := newMenuItemImpl(item)
|
|
||||||
item.impl = menuItem
|
|
||||||
m.addMenuItem(menu, item)
|
|
||||||
case radio:
|
|
||||||
menuItem := newRadioItemImpl(item, currentRadioGroup)
|
|
||||||
item.impl = menuItem
|
|
||||||
m.addMenuItem(menu, item)
|
|
||||||
|
|
||||||
var radioGetGroup func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&radioGetGroup, gtk, "gtk_radio_menu_item_get_group")
|
|
||||||
|
|
||||||
currentRadioGroup = radioGetGroup(menuItem.native)
|
|
||||||
case separator:
|
|
||||||
m.addMenuSeparator(menu)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, item := range menu.items {
|
|
||||||
if item.callback != nil {
|
|
||||||
m.attachHandler(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxMenu) attachHandler(item *MenuItem) {
|
|
||||||
impl := (item.impl).(*linuxMenuItem)
|
|
||||||
widget := impl.native
|
|
||||||
flags := 0
|
|
||||||
|
|
||||||
var handleClick = func() {
|
|
||||||
item := item
|
|
||||||
switch item.itemType {
|
|
||||||
case text, checkbox:
|
|
||||||
processMenuItemClick(item.id)
|
|
||||||
case radio:
|
|
||||||
menuItem := (item.impl).(*linuxMenuItem)
|
|
||||||
if menuItem.isChecked() {
|
|
||||||
processMenuItemClick(item.id)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fmt.Println("handleClick", item.itemType, item.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var signalConnectObject func(uintptr, string, uintptr, uintptr, int) uint
|
|
||||||
purego.RegisterLibFunc(&signalConnectObject, gtk, "g_signal_connect_object")
|
|
||||||
handlerId := signalConnectObject(
|
|
||||||
widget,
|
|
||||||
"activate",
|
|
||||||
purego.NewCallback(handleClick),
|
|
||||||
widget,
|
|
||||||
flags)
|
|
||||||
|
|
||||||
impl.handlerId = handlerId
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxMenu) addSubMenuToItem(menu *Menu, item *MenuItem) {
|
|
||||||
var newMenu func() uintptr
|
|
||||||
purego.RegisterLibFunc(&newMenu, gtk, "gtk_menu_new")
|
|
||||||
if menu.impl == nil {
|
|
||||||
menu.impl = &linuxMenu{
|
|
||||||
menu: menu,
|
|
||||||
native: newMenu(),
|
|
||||||
N }
|
|
||||||
}
|
|
||||||
var itemSetSubmenu func(uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(&itemSetSubmenu, gtk, "gtk_menu_item_set_submenu")
|
|
||||||
|
|
||||||
itemSetSubmenu(
|
|
||||||
(item.impl).(*linuxMenuItem).native,
|
|
||||||
(menu.impl).(*linuxMenu).native)
|
|
||||||
|
|
||||||
if item.role == ServicesMenu {
|
|
||||||
// FIXME: what does this mean?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxMenu) addMenuItem(parent *Menu, menu *MenuItem) {
|
|
||||||
var shellAppend func(uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(&shellAppend, gtk, "gtk_menu_shell_append")
|
|
||||||
shellAppend(
|
|
||||||
(parent.impl).(*linuxMenu).native,
|
|
||||||
(menu.impl).(*linuxMenuItem).native,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxMenu) addMenuSeparator(menu *Menu) {
|
|
||||||
var newSeparator func() uintptr
|
|
||||||
purego.RegisterLibFunc(&newSeparator, gtk, "gtk_separator_menu_item_new")
|
|
||||||
var shellAppend func(uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(&shellAppend, gtk, "gtk_menu_shell_append")
|
|
||||||
|
|
||||||
sep := newSeparator()
|
|
||||||
native := (menu.impl).(*linuxMenu).native
|
|
||||||
shellAppend(native, sep)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxMenu) addServicesMenu(menu *Menu) {
|
|
||||||
fmt.Println("addServicesMenu - not implemented")
|
|
||||||
//C.addServicesMenu(unsafe.Pointer(menu.impl.(*linuxMenu).nsMenu))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *linuxMenu) createMenu(name string, items []*MenuItem) *Menu {
|
|
||||||
impl := newMenuImpl(&Menu{label: name})
|
|
||||||
menu := &Menu{
|
|
||||||
label: name,
|
|
||||||
items: items,
|
|
||||||
impl: impl,
|
|
||||||
}
|
|
||||||
impl.menu = menu
|
|
||||||
return menu
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultApplicationMenu() *Menu {
|
|
||||||
menu := NewMenu()
|
|
||||||
menu.AddRole(AppMenu)
|
|
||||||
menu.AddRole(FileMenu)
|
|
||||||
menu.AddRole(EditMenu)
|
|
||||||
menu.AddRole(ViewMenu)
|
|
||||||
menu.AddRole(WindowMenu)
|
|
||||||
menu.AddRole(HelpMenu)
|
|
||||||
return menu
|
|
||||||
}
|
|
@ -5,48 +5,31 @@ package application
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "gtk/gtk.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
type linuxMenuItem struct {
|
type linuxMenuItem struct {
|
||||||
menuItem *MenuItem
|
menuItem *MenuItem
|
||||||
native unsafe.Pointer
|
native pointer
|
||||||
handlerId C.gulong
|
handlerId uint
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l linuxMenuItem) setTooltip(tooltip string) {
|
func (l linuxMenuItem) setTooltip(tooltip string) {
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
l.blockSignal()
|
l.blockSignal()
|
||||||
defer l.unBlockSignal()
|
defer l.unBlockSignal()
|
||||||
|
menuItemSetToolTip(l.native, tooltip)
|
||||||
value := C.CString(tooltip)
|
|
||||||
C.gtk_widget_set_tooltip_text(
|
|
||||||
(*C.GtkWidget)(l.native),
|
|
||||||
value)
|
|
||||||
C.free(unsafe.Pointer(value))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l linuxMenuItem) blockSignal() {
|
func (l linuxMenuItem) blockSignal() {
|
||||||
if l.handlerId != 0 {
|
if l.handlerId != 0 {
|
||||||
C.g_signal_handler_block(C.gpointer(l.native), l.handlerId)
|
menuItemSignalBlock(l.native, l.handlerId, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l linuxMenuItem) unBlockSignal() {
|
func (l linuxMenuItem) unBlockSignal() {
|
||||||
if l.handlerId != 0 {
|
if l.handlerId != 0 {
|
||||||
C.g_signal_handler_unblock(C.gpointer(l.native), l.handlerId)
|
menuItemSignalBlock(l.native, l.handlerId, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,35 +37,19 @@ func (l linuxMenuItem) setLabel(s string) {
|
|||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
l.blockSignal()
|
l.blockSignal()
|
||||||
defer l.unBlockSignal()
|
defer l.unBlockSignal()
|
||||||
value := C.CString(s)
|
menuItemSetLabel(l.native, s)
|
||||||
C.gtk_menu_item_set_label(
|
|
||||||
(*C.GtkMenuItem)(l.native),
|
|
||||||
value)
|
|
||||||
C.free(unsafe.Pointer(value))
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l linuxMenuItem) isChecked() bool {
|
func (l linuxMenuItem) isChecked() bool {
|
||||||
if C.gtk_check_menu_item_get_active((*C.GtkCheckMenuItem)(l.native)) == C.int(1) {
|
return menuItemChecked(l.native)
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l linuxMenuItem) setDisabled(disabled bool) {
|
func (l linuxMenuItem) setDisabled(disabled bool) {
|
||||||
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
l.blockSignal()
|
l.blockSignal()
|
||||||
defer l.unBlockSignal()
|
defer l.unBlockSignal()
|
||||||
|
menuItemSetDisabled(l.native, disabled)
|
||||||
value := C.int(1)
|
|
||||||
if disabled {
|
|
||||||
value = C.int(0)
|
|
||||||
}
|
|
||||||
C.gtk_widget_set_sensitive(
|
|
||||||
(*C.GtkWidget)(l.native),
|
|
||||||
value)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,15 +57,15 @@ func (l linuxMenuItem) setChecked(checked bool) {
|
|||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
l.blockSignal()
|
l.blockSignal()
|
||||||
defer l.unBlockSignal()
|
defer l.unBlockSignal()
|
||||||
|
menuItemSetChecked(l.native, checked)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
value := C.int(0)
|
func (l linuxMenuItem) setHidden(hidden bool) {
|
||||||
if checked {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
value = C.int(1)
|
l.blockSignal()
|
||||||
}
|
defer l.unBlockSignal()
|
||||||
|
widgetSetVisible(l.native, hidden)
|
||||||
C.gtk_check_menu_item_set_active(
|
|
||||||
(*C.GtkCheckMenuItem)(l.native),
|
|
||||||
value)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,41 +87,30 @@ func newMenuItemImpl(item *MenuItem) *linuxMenuItem {
|
|||||||
result := &linuxMenuItem{
|
result := &linuxMenuItem{
|
||||||
menuItem: item,
|
menuItem: item,
|
||||||
}
|
}
|
||||||
cLabel := C.CString(item.label)
|
|
||||||
switch item.itemType {
|
switch item.itemType {
|
||||||
case text:
|
case text:
|
||||||
result.native = unsafe.Pointer(C.gtk_menu_item_new_with_label(cLabel))
|
result.native = menuItemNew(item.label)
|
||||||
|
|
||||||
case checkbox:
|
case checkbox:
|
||||||
result.native = unsafe.Pointer(C.gtk_check_menu_item_new_with_label(cLabel))
|
result.native = menuCheckItemNew(item.label)
|
||||||
result.setChecked(item.checked)
|
result.setChecked(item.checked)
|
||||||
if item.itemType == checkbox || item.itemType == radio {
|
|
||||||
// C.setMenuItemChecked(result.nsMenuItem, C.bool(item.checked))
|
|
||||||
}
|
|
||||||
if item.accelerator != nil {
|
if item.accelerator != nil {
|
||||||
result.setAccelerator(item.accelerator)
|
result.setAccelerator(item.accelerator)
|
||||||
}
|
}
|
||||||
case radio:
|
|
||||||
panic("Shouldn't get here with a radio item")
|
|
||||||
|
|
||||||
case submenu:
|
case submenu:
|
||||||
result.native = unsafe.Pointer(C.gtk_menu_item_new_with_label(cLabel))
|
result.native = menuItemNew(item.label)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("WTF")
|
panic(fmt.Sprintf("Unknown menu type: %v", item.itemType))
|
||||||
}
|
}
|
||||||
result.setDisabled(result.menuItem.disabled)
|
result.setDisabled(result.menuItem.disabled)
|
||||||
|
|
||||||
C.free(unsafe.Pointer(cLabel))
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRadioItemImpl(item *MenuItem, group *C.GSList) *linuxMenuItem {
|
func newRadioItemImpl(item *MenuItem, group GSListPointer) *linuxMenuItem {
|
||||||
cLabel := C.CString(item.label)
|
|
||||||
defer C.free(unsafe.Pointer(cLabel))
|
|
||||||
result := &linuxMenuItem{
|
result := &linuxMenuItem{
|
||||||
menuItem: item,
|
menuItem: item,
|
||||||
native: unsafe.Pointer(C.gtk_radio_menu_item_new_with_label(group, cLabel)),
|
native: menuRadioItemNew(group, item.label),
|
||||||
}
|
}
|
||||||
result.setChecked(item.checked)
|
result.setChecked(item.checked)
|
||||||
result.setDisabled(result.menuItem.disabled)
|
result.setDisabled(result.menuItem.disabled)
|
||||||
@ -182,6 +138,7 @@ func newHideMenuItem() *MenuItem {
|
|||||||
return newMenuItem("Hide " + globalApplication.options.Name).
|
return newMenuItem("Hide " + globalApplication.options.Name).
|
||||||
SetAccelerator("CmdOrCtrl+h").
|
SetAccelerator("CmdOrCtrl+h").
|
||||||
OnClick(func(ctx *Context) {
|
OnClick(func(ctx *Context) {
|
||||||
|
|
||||||
// C.hideApplication()
|
// C.hideApplication()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,403 +0,0 @@
|
|||||||
//go:build linux && purego
|
|
||||||
|
|
||||||
package application
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/ebitengine/purego"
|
|
||||||
)
|
|
||||||
|
|
||||||
type linuxMenuItem struct {
|
|
||||||
menuItem *MenuItem
|
|
||||||
native uintptr
|
|
||||||
handlerId uint
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l linuxMenuItem) setTooltip(tooltip string) {
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
l.blockSignal()
|
|
||||||
defer l.unBlockSignal()
|
|
||||||
var setToolTip func(uintptr, string)
|
|
||||||
purego.RegisterLibFunc(&setToolTip, gtk, "gtk_widget_set_tooltip_text")
|
|
||||||
|
|
||||||
setToolTip(l.native, tooltip)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l linuxMenuItem) blockSignal() {
|
|
||||||
var block func(uintptr, uint)
|
|
||||||
purego.RegisterLibFunc(&block, gtk, "g_signal_handler_block")
|
|
||||||
|
|
||||||
if l.handlerId != 0 {
|
|
||||||
block(l.native, l.handlerId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l linuxMenuItem) unBlockSignal() {
|
|
||||||
var unblock func(uintptr, uint)
|
|
||||||
purego.RegisterLibFunc(&unblock, gtk, "g_signal_handler_unblock")
|
|
||||||
|
|
||||||
if l.handlerId != 0 {
|
|
||||||
unblock(l.native, l.handlerId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l linuxMenuItem) setLabel(s string) {
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
l.blockSignal()
|
|
||||||
defer l.unBlockSignal()
|
|
||||||
var setLabel func(uintptr, string)
|
|
||||||
purego.RegisterLibFunc(&setLabel, gtk, "gtk_menu_item_set_label")
|
|
||||||
setLabel(l.native, s)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l linuxMenuItem) isChecked() bool {
|
|
||||||
var getActive func(uintptr) int
|
|
||||||
purego.RegisterLibFunc(&getActive, gtk, "gtk_check_menu_item_get_active")
|
|
||||||
|
|
||||||
if getActive(l.native) == 1 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l linuxMenuItem) setDisabled(disabled bool) {
|
|
||||||
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
l.blockSignal()
|
|
||||||
defer l.unBlockSignal()
|
|
||||||
|
|
||||||
var setSensitive func(uintptr, int)
|
|
||||||
purego.RegisterLibFunc(&setSensitive, gtk, "gtk_widget_set_sensitive")
|
|
||||||
|
|
||||||
value := 1
|
|
||||||
if disabled {
|
|
||||||
value = 0
|
|
||||||
}
|
|
||||||
setSensitive(l.native, value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l linuxMenuItem) setChecked(checked bool) {
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
l.blockSignal()
|
|
||||||
defer l.unBlockSignal()
|
|
||||||
|
|
||||||
var setActive func(uintptr, int)
|
|
||||||
purego.RegisterLibFunc(&setActive, gtk, "gtk_check_menu_item_set_active")
|
|
||||||
|
|
||||||
value := 0
|
|
||||||
if checked {
|
|
||||||
value = 1
|
|
||||||
}
|
|
||||||
setActive(l.native, value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l linuxMenuItem) setAccelerator(accelerator *accelerator) {
|
|
||||||
fmt.Println("setAccelerator", accelerator)
|
|
||||||
// Set the keyboard shortcut of the menu item
|
|
||||||
// var modifier C.int
|
|
||||||
// var key *C.char
|
|
||||||
if accelerator != nil {
|
|
||||||
// modifier = C.int(toMacModifier(accelerator.Modifiers))
|
|
||||||
// key = C.CString(accelerator.Key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the key to a string
|
|
||||||
// C.setMenuItemKeyEquivalent(m.nsMenuItem, key, modifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMenuItemImpl(item *MenuItem) *linuxMenuItem {
|
|
||||||
result := &linuxMenuItem{
|
|
||||||
menuItem: item,
|
|
||||||
}
|
|
||||||
var newWithLabel func(string) uintptr
|
|
||||||
purego.RegisterLibFunc(&newWithLabel, gtk, "gtk_menu_item_new_with_label")
|
|
||||||
var newCBWithLabel func(string) uintptr
|
|
||||||
purego.RegisterLibFunc(&newCBWithLabel, gtk, "gtk_check_menu_item_new_with_label")
|
|
||||||
|
|
||||||
switch item.itemType {
|
|
||||||
case text:
|
|
||||||
result.native = newWithLabel(item.label)
|
|
||||||
|
|
||||||
case checkbox:
|
|
||||||
result.native = newCBWithLabel(item.label)
|
|
||||||
result.setChecked(item.checked)
|
|
||||||
if item.accelerator != nil {
|
|
||||||
result.setAccelerator(item.accelerator)
|
|
||||||
}
|
|
||||||
case radio:
|
|
||||||
panic("Shouldn't get here with a radio item")
|
|
||||||
|
|
||||||
case submenu:
|
|
||||||
result.native = newWithLabel(item.label)
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("WTF")
|
|
||||||
}
|
|
||||||
result.setDisabled(result.menuItem.disabled)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRadioItemImpl(item *MenuItem, group uintptr) *linuxMenuItem {
|
|
||||||
var newWithLabel func(uintptr, string) uintptr
|
|
||||||
purego.RegisterLibFunc(&newWithLabel, gtk, "gtk_radio_menu_item_new_with_label")
|
|
||||||
|
|
||||||
result := &linuxMenuItem{
|
|
||||||
menuItem: item,
|
|
||||||
native: newWithLabel(group, item.label),
|
|
||||||
}
|
|
||||||
result.setChecked(item.checked)
|
|
||||||
result.setDisabled(result.menuItem.disabled)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSpeechMenu() *MenuItem {
|
|
||||||
speechMenu := NewMenu()
|
|
||||||
speechMenu.Add("Start Speaking").
|
|
||||||
SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+.").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.startSpeaking()
|
|
||||||
})
|
|
||||||
speechMenu.Add("Stop Speaking").
|
|
||||||
SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+,").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.stopSpeaking()
|
|
||||||
})
|
|
||||||
subMenu := newSubMenuItem("Speech")
|
|
||||||
subMenu.submenu = speechMenu
|
|
||||||
return subMenu
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHideMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Hide " + globalApplication.options.Name).
|
|
||||||
SetAccelerator("CmdOrCtrl+h").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.hideApplication()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHideOthersMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Hide Others").
|
|
||||||
SetAccelerator("CmdOrCtrl+OptionOrAlt+h").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.hideOthers()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newUnhideMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Show All").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.showAll()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newUndoMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Undo").
|
|
||||||
SetAccelerator("CmdOrCtrl+z").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.undo()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// newRedoMenuItem creates a new menu item for redoing the last action
|
|
||||||
func newRedoMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Redo").
|
|
||||||
SetAccelerator("CmdOrCtrl+Shift+z").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.redo()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCutMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Cut").
|
|
||||||
SetAccelerator("CmdOrCtrl+x").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.cut()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCopyMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Copy").
|
|
||||||
SetAccelerator("CmdOrCtrl+c").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.copy()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPasteMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Paste").
|
|
||||||
SetAccelerator("CmdOrCtrl+v").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.paste()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPasteAndMatchStyleMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Paste and Match Style").
|
|
||||||
SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+v").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.pasteAndMatchStyle()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDeleteMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Delete").
|
|
||||||
SetAccelerator("backspace").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.delete()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newQuitMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Quit " + globalApplication.options.Name).
|
|
||||||
SetAccelerator("CmdOrCtrl+q").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
globalApplication.Quit()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSelectAllMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Select All").
|
|
||||||
SetAccelerator("CmdOrCtrl+a").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
// C.selectAll()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAboutMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("About " + globalApplication.options.Name).
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
globalApplication.ShowAboutDialog()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCloseMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Close").
|
|
||||||
SetAccelerator("CmdOrCtrl+w").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.Close()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newReloadMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Reload").
|
|
||||||
SetAccelerator("CmdOrCtrl+r").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.Reload()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newForceReloadMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Force Reload").
|
|
||||||
SetAccelerator("CmdOrCtrl+Shift+r").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.ForceReload()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newToggleFullscreenMenuItem() *MenuItem {
|
|
||||||
result := newMenuItem("Toggle Full Screen").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.ToggleFullscreen()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if runtime.GOOS == "darwin" {
|
|
||||||
result.SetAccelerator("Ctrl+Command+F")
|
|
||||||
} else {
|
|
||||||
result.SetAccelerator("F11")
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func newToggleDevToolsMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Toggle Developer Tools").
|
|
||||||
SetAccelerator("Alt+Command+I").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.ToggleDevTools()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newZoomResetMenuItem() *MenuItem {
|
|
||||||
// reset zoom menu item
|
|
||||||
return newMenuItem("Actual Size").
|
|
||||||
SetAccelerator("CmdOrCtrl+0").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.ZoomReset()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newZoomInMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Zoom In").
|
|
||||||
SetAccelerator("CmdOrCtrl+plus").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.ZoomIn()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newZoomOutMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Zoom Out").
|
|
||||||
SetAccelerator("CmdOrCtrl+-").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.ZoomOut()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMinimizeMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Minimize").
|
|
||||||
SetAccelerator("CmdOrCtrl+M").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.Minimise()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newZoomMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Zoom").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.Zoom()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFullScreenMenuItem() *MenuItem {
|
|
||||||
return newMenuItem("Fullscreen").
|
|
||||||
OnClick(func(ctx *Context) {
|
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
|
||||||
if currentWindow != nil {
|
|
||||||
currentWindow.Fullscreen()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
@ -4,16 +4,19 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *MessageProcessor) callErrorCallback(window *WebviewWindow, message string, callID *string, err error) {
|
func (m *MessageProcessor) callErrorCallback(window *WebviewWindow, message string, callID *string, err error) {
|
||||||
errorMsg := fmt.Sprintf(message, err)
|
errorMsg := fmt.Sprintf(message, err)
|
||||||
m.Error(errorMsg)
|
m.Error(errorMsg)
|
||||||
window.CallError(callID, errorMsg)
|
msg := "_wails.callErrorCallback('" + *callID + "', " + strconv.Quote(errorMsg) + ");"
|
||||||
|
window.ExecJS(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MessageProcessor) callCallback(window *WebviewWindow, callID *string, result string, isJSON bool) {
|
func (m *MessageProcessor) callCallback(window *WebviewWindow, callID *string, result string, isJSON bool) {
|
||||||
window.CallResponse(callID, result)
|
msg := fmt.Sprintf("_wails.callCallback('%s', %s, %v);", *callID, strconv.Quote(result), isJSON)
|
||||||
|
window.ExecJS(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MessageProcessor) processCallMethod(method string, rw http.ResponseWriter, _ *http.Request, window *WebviewWindow, params QueryParams) {
|
func (m *MessageProcessor) processCallMethod(method string, rw http.ResponseWriter, _ *http.Request, window *WebviewWindow, params QueryParams) {
|
||||||
|
@ -5,16 +5,19 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *MessageProcessor) dialogErrorCallback(window *WebviewWindow, message string, dialogID *string, err error) {
|
func (m *MessageProcessor) dialogErrorCallback(window *WebviewWindow, message string, dialogID *string, err error) {
|
||||||
errorMsg := fmt.Sprintf(message, err)
|
errorMsg := fmt.Sprintf(message, err)
|
||||||
m.Error(errorMsg)
|
m.Error(errorMsg)
|
||||||
window.DialogError(dialogID, errorMsg)
|
msg := "_wails.dialogErrorCallback('" + *dialogID + "', " + strconv.Quote(errorMsg) + ");"
|
||||||
|
window.ExecJS(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MessageProcessor) dialogCallback(window *WebviewWindow, dialogID *string, result string, isJSON bool) {
|
func (m *MessageProcessor) dialogCallback(window *WebviewWindow, dialogID *string, result string, isJSON bool) {
|
||||||
window.DialogResponse(dialogID, result)
|
msg := fmt.Sprintf("_wails.dialogCallback('%s', %s, %v);", *dialogID, strconv.Quote(result), isJSON)
|
||||||
|
window.ExecJS(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) {
|
func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) {
|
||||||
|
@ -2,91 +2,26 @@
|
|||||||
|
|
||||||
package application
|
package application
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gdk/gdk.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
typedef struct Screen {
|
|
||||||
const char* id;
|
|
||||||
const char* name;
|
|
||||||
int p_width;
|
|
||||||
int p_height;
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
int w_width;
|
|
||||||
int w_height;
|
|
||||||
int w_x;
|
|
||||||
int w_y;
|
|
||||||
float scale;
|
|
||||||
double rotation;
|
|
||||||
bool isPrimary;
|
|
||||||
} Screen;
|
|
||||||
|
|
||||||
|
|
||||||
int GetNumScreens(){
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *linuxApp) getPrimaryScreen() (*Screen, error) {
|
func (m *linuxApp) getPrimaryScreen() (*Screen, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *linuxApp) getScreenByIndex(display *C.struct__GdkDisplay, index int) *Screen {
|
|
||||||
monitor := C.gdk_display_get_monitor(display, C.int(index))
|
|
||||||
|
|
||||||
// TODO: Do we need to update Screen to contain current info?
|
|
||||||
// currentMonitor := C.gdk_display_get_monitor_at_window(display, window)
|
|
||||||
|
|
||||||
var geometry C.GdkRectangle
|
|
||||||
C.gdk_monitor_get_geometry(monitor, &geometry)
|
|
||||||
primary := false
|
|
||||||
if C.gdk_monitor_is_primary(monitor) == 1 {
|
|
||||||
primary = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Screen{
|
|
||||||
IsPrimary: primary,
|
|
||||||
Scale: 1.0,
|
|
||||||
X: int(geometry.x),
|
|
||||||
Y: int(geometry.y),
|
|
||||||
Size: Size{
|
|
||||||
Height: int(geometry.height),
|
|
||||||
Width: int(geometry.width),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) getScreens() ([]*Screen, error) {
|
func (m *linuxApp) getScreens() ([]*Screen, error) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
var screens []*Screen
|
var screens []*Screen
|
||||||
|
var err error
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
window := C.gtk_application_get_active_window((*C.GtkApplication)(m.application))
|
screens, err = getScreens(m.application)
|
||||||
display := C.gdk_window_get_display((*C.GdkWindow)(unsafe.Pointer(window)))
|
|
||||||
count := C.gdk_display_get_n_monitors(display)
|
|
||||||
for i := 0; i < int(count); i++ {
|
|
||||||
screens = append(screens,
|
|
||||||
m.getScreenByIndex(display, i),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
wg.Done()
|
wg.Done()
|
||||||
})
|
})
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return screens, nil
|
return screens, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getScreenForWindow(window *linuxWebviewWindow) (*Screen, error) {
|
func getScreenForWindow(window *linuxWebviewWindow) (*Screen, error) {
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
//go:build linux && purego
|
|
||||||
|
|
||||||
package application
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/ebitengine/purego"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (m *linuxApp) getPrimaryScreen() (*Screen, error) {
|
|
||||||
return nil, fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) getScreenByIndex(display uintptr, index int) *Screen {
|
|
||||||
fmt.Println("getScreenByIndex")
|
|
||||||
var getMonitor func(uintptr, int) uintptr
|
|
||||||
purego.RegisterLibFunc(&getMonitor, gtk, "gdk_display_get_monitor")
|
|
||||||
|
|
||||||
monitor := getMonitor(display, index)
|
|
||||||
|
|
||||||
// TODO: Do we need to update Screen to contain current info?
|
|
||||||
// currentMonitor := C.gdk_display_get_monitor_at_window(display, window)
|
|
||||||
|
|
||||||
var getGeometry func(uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(&getGeometry, gtk, "gdk_monitor_get_geometry")
|
|
||||||
|
|
||||||
//var geometry C.GdkRectangle
|
|
||||||
/*
|
|
||||||
struct GdkRectangle {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
geometry := make([]byte, 16)
|
|
||||||
getGeometry(monitor, uintptr(unsafe.Pointer(&geometry[0])))
|
|
||||||
fmt.Println("geometry: %v\n", geometry)
|
|
||||||
|
|
||||||
var isPrimary func(uintptr) int
|
|
||||||
purego.RegisterLibFunc(&isPrimary, gtk, "gdk_monitor_is_primary")
|
|
||||||
|
|
||||||
primary := false
|
|
||||||
if isPrimary(monitor) == 1 {
|
|
||||||
primary = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Screen{
|
|
||||||
IsPrimary: primary,
|
|
||||||
Scale: 1.0,
|
|
||||||
X: 0, //int(geometry.x),
|
|
||||||
Y: 0, //int(geometry.y),
|
|
||||||
Size: Size{
|
|
||||||
Height: 1024, //int(geometry.height),
|
|
||||||
Width: 1024, //int(geometry.width),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *linuxApp) getScreens() ([]*Screen, error) {
|
|
||||||
fmt.Println("getScreens")
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
var screens []*Screen
|
|
||||||
wg.Add(1)
|
|
||||||
|
|
||||||
var getWindow func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getWindow, gtk, "gtk_application_get_active_window")
|
|
||||||
var getDisplay func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getDisplay, gtk, "gdk_window_get_display")
|
|
||||||
var getMonitorCount func(uintptr) int
|
|
||||||
purego.RegisterLibFunc(&getMonitorCount, gtk, "getNMonitors")
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
window := getWindow(m.application)
|
|
||||||
display := getDisplay(window)
|
|
||||||
count := getMonitorCount(display)
|
|
||||||
for i := 0; i < int(count); i++ {
|
|
||||||
screens = append(screens,
|
|
||||||
m.getScreenByIndex(display, i),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
})
|
|
||||||
wg.Wait()
|
|
||||||
return screens, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getScreenForWindow(window *linuxWebviewWindow) (*Screen, error) {
|
|
||||||
return window.getScreen()
|
|
||||||
}
|
|
@ -130,10 +130,6 @@ func (s *macosSystemTray) setIcon(icon []byte) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *macosSystemTray) setDarkModeIcon(icon []byte) {
|
|
||||||
s.setIcon(icon)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *macosSystemTray) setTemplateIcon(icon []byte) {
|
func (s *macosSystemTray) setTemplateIcon(icon []byte) {
|
||||||
s.icon = icon
|
s.icon = icon
|
||||||
s.isTemplateIcon = true
|
s.isTemplateIcon = true
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
@ -150,39 +149,6 @@ func (w *WebviewWindow) addCancellationFunction(canceller func()) {
|
|||||||
w.cancellers = append(w.cancellers, canceller)
|
w.cancellers = append(w.cancellers, canceller)
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatJS ensures the 'data' provided marshals to valid json or panics
|
|
||||||
func (w *WebviewWindow) formatJS(f string, callID string, data string) string {
|
|
||||||
j, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(f, callID, j)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WebviewWindow) CallError(callID *string, result string) {
|
|
||||||
if w.impl != nil {
|
|
||||||
w.impl.execJS(w.formatJS("_wails.callErrorCallback('%s', %s);", *callID, result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WebviewWindow) CallResponse(callID *string, result string) {
|
|
||||||
if w.impl != nil {
|
|
||||||
w.impl.execJS(w.formatJS("_wails.callCallback('%s', %s, true);", *callID, result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WebviewWindow) DialogError(dialogID *string, result string) {
|
|
||||||
if w.impl != nil {
|
|
||||||
w.impl.execJS(w.formatJS("_wails.dialogErrorCallback('%s', %s);", *dialogID, result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WebviewWindow) DialogResponse(dialogID *string, result string) {
|
|
||||||
if w.impl != nil {
|
|
||||||
w.impl.execJS(w.formatJS("_wails.dialogCallback('%s', %s, true);", *dialogID, result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTitle sets the title of the window
|
// SetTitle sets the title of the window
|
||||||
func (w *WebviewWindow) SetTitle(title string) *WebviewWindow {
|
func (w *WebviewWindow) SetTitle(title string) *WebviewWindow {
|
||||||
w.options.Title = title
|
w.options.Title = title
|
||||||
|
@ -1,60 +1,19 @@
|
|||||||
//go:build linux && !purego
|
//go:build linux
|
||||||
|
|
||||||
package application
|
package application
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gdk/gdk.h>
|
|
||||||
#include <webkit2/webkit2.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
|
|
||||||
// exported below
|
|
||||||
extern gboolean buttonEvent(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
|
|
||||||
extern void processRequest(void *request, gpointer user_data);
|
|
||||||
extern void onDragNDrop(
|
|
||||||
void *target,
|
|
||||||
GdkDragContext* context,
|
|
||||||
gint x,
|
|
||||||
gint y,
|
|
||||||
gpointer seldata,
|
|
||||||
guint info,
|
|
||||||
guint time,
|
|
||||||
gpointer data);
|
|
||||||
// exported below (end)
|
|
||||||
|
|
||||||
static void signal_connect(GtkWidget *widget, char *event, void *cb, void* data) {
|
|
||||||
// g_signal_connect is a macro and can't be called directly
|
|
||||||
g_signal_connect(widget, event, cb, data);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
"github.com/wailsapp/wails/v3/pkg/events"
|
"github.com/wailsapp/wails/v3/pkg/events"
|
||||||
)
|
)
|
||||||
|
|
||||||
var showDevTools = func(window unsafe.Pointer) {}
|
var showDevTools = func(window unsafe.Pointer) {}
|
||||||
|
|
||||||
func gtkBool(input bool) C.gboolean {
|
|
||||||
if input {
|
|
||||||
return C.gboolean(1)
|
|
||||||
}
|
|
||||||
return C.gboolean(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
type dragInfo struct {
|
type dragInfo struct {
|
||||||
XRoot int
|
XRoot int
|
||||||
YRoot int
|
YRoot int
|
||||||
@ -64,14 +23,14 @@ type dragInfo struct {
|
|||||||
|
|
||||||
type linuxWebviewWindow struct {
|
type linuxWebviewWindow struct {
|
||||||
id uint
|
id uint
|
||||||
application unsafe.Pointer
|
application pointer
|
||||||
window unsafe.Pointer
|
window pointer
|
||||||
webview unsafe.Pointer
|
webview pointer
|
||||||
parent *WebviewWindow
|
parent *WebviewWindow
|
||||||
menubar *C.GtkWidget
|
menubar pointer
|
||||||
vbox *C.GtkWidget
|
vbox pointer
|
||||||
menu *menu.Menu
|
menu *menu.Menu
|
||||||
accels *C.GtkAccelGroup
|
accels pointer
|
||||||
lastWidth int
|
lastWidth int
|
||||||
lastHeight int
|
lastHeight int
|
||||||
drag dragInfo
|
drag dragInfo
|
||||||
@ -81,173 +40,30 @@ var (
|
|||||||
registered bool = false // avoid 'already registered message' about 'wails://'
|
registered bool = false // avoid 'already registered message' about 'wails://'
|
||||||
)
|
)
|
||||||
|
|
||||||
//export buttonEvent
|
func (w *linuxWebviewWindow) startDrag() error {
|
||||||
func buttonEvent(_ *C.GtkWidget, event *C.GdkEventButton, data unsafe.Pointer) C.gboolean {
|
return nil
|
||||||
// Constants (defined here to be easier to use with )
|
|
||||||
GdkButtonPress := C.GDK_BUTTON_PRESS // 4
|
|
||||||
Gdk2ButtonPress := C.GDK_2BUTTON_PRESS // 5 for double-click
|
|
||||||
GdkButtonRelease := C.GDK_BUTTON_RELEASE // 7
|
|
||||||
|
|
||||||
windowId := uint(*((*C.uint)(data)))
|
|
||||||
window := globalApplication.getWindowForID(windowId)
|
|
||||||
if window == nil {
|
|
||||||
return C.gboolean(0)
|
|
||||||
}
|
|
||||||
lw, ok := (window.impl).(*linuxWebviewWindow)
|
|
||||||
if !ok {
|
|
||||||
return C.gboolean(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if event == nil {
|
|
||||||
return C.gboolean(0)
|
|
||||||
}
|
|
||||||
if event.button == 3 {
|
|
||||||
return C.gboolean(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch int(event._type) {
|
|
||||||
case GdkButtonPress:
|
|
||||||
lw.startDrag(uint(event.button), int(event.x_root), int(event.y_root))
|
|
||||||
case Gdk2ButtonPress:
|
|
||||||
fmt.Printf("%d - button %d - double-clicked\n", windowId, int(event.button))
|
|
||||||
case GdkButtonRelease:
|
|
||||||
lw.endDrag(uint(event.button), int(event.x_root), int(event.y_root))
|
|
||||||
}
|
|
||||||
|
|
||||||
return C.gboolean(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) startDrag(button uint, x, y int) {
|
|
||||||
fmt.Println("startDrag ", button, x, y)
|
|
||||||
w.drag.XRoot = x
|
|
||||||
w.drag.YRoot = y
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) endDrag(button uint, x, y int) {
|
func (w *linuxWebviewWindow) endDrag(button uint, x, y int) {
|
||||||
fmt.Println("endDrag", button, x, y)
|
fmt.Println("endDrag", button, x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export onDragNDrop
|
|
||||||
func onDragNDrop(target unsafe.Pointer, context *C.GdkDragContext, x C.gint, y C.gint, seldata unsafe.Pointer, info C.guint, time C.guint, data unsafe.Pointer) {
|
|
||||||
fmt.Println("target", target, info)
|
|
||||||
var length C.gint
|
|
||||||
selection := unsafe.Pointer(C.gtk_selection_data_get_data_with_length((*C.GtkSelectionData)(seldata), &length))
|
|
||||||
extracted := C.g_uri_list_extract_uris((*C.char)(selection))
|
|
||||||
defer C.g_strfreev(extracted)
|
|
||||||
|
|
||||||
uris := unsafe.Slice(
|
|
||||||
(**C.char)(unsafe.Pointer(extracted)),
|
|
||||||
int(length))
|
|
||||||
|
|
||||||
var filenames []string
|
|
||||||
for _, uri := range uris {
|
|
||||||
if uri == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
filenames = append(filenames, strings.TrimPrefix(C.GoString(uri), "file://"))
|
|
||||||
}
|
|
||||||
windowDragAndDropBuffer <- &dragAndDropMessage{
|
|
||||||
windowId: uint(*((*C.uint)(data))),
|
|
||||||
filenames: filenames,
|
|
||||||
}
|
|
||||||
C.gtk_drag_finish(context, C.true, C.false, time)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export processRequest
|
|
||||||
func processRequest(request unsafe.Pointer, data unsafe.Pointer) {
|
|
||||||
windowId := uint(*((*C.uint)(data)))
|
|
||||||
webviewRequests <- &webViewAssetRequest{
|
|
||||||
Request: webview.NewRequest(request),
|
|
||||||
windowId: windowId,
|
|
||||||
windowName: globalApplication.getWindowForID(windowId).Name(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) enableDND() {
|
func (w *linuxWebviewWindow) enableDND() {
|
||||||
dnd := C.CString("text/uri-list")
|
windowEnableDND(w.parent.id, w.webview)
|
||||||
defer C.free(unsafe.Pointer(dnd))
|
|
||||||
targetentry := C.gtk_target_entry_new(dnd, 0, C.guint(w.parent.id))
|
|
||||||
defer C.gtk_target_entry_free(targetentry)
|
|
||||||
C.gtk_drag_dest_set((*C.GtkWidget)(w.webview), C.GTK_DEST_DEFAULT_DROP, targetentry, 1, C.GDK_ACTION_COPY)
|
|
||||||
event := C.CString("drag-data-received")
|
|
||||||
defer C.free(unsafe.Pointer(event))
|
|
||||||
id := C.uint(w.parent.id)
|
|
||||||
C.signal_connect((*C.GtkWidget)(unsafe.Pointer(w.webview)), event, C.onDragNDrop, unsafe.Pointer(C.gpointer(&id)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) newWebview(gpuPolicy int) unsafe.Pointer {
|
|
||||||
manager := C.webkit_user_content_manager_new()
|
|
||||||
external := C.CString("external")
|
|
||||||
C.webkit_user_content_manager_register_script_message_handler(manager, external)
|
|
||||||
|
|
||||||
C.free(unsafe.Pointer(external))
|
|
||||||
webview := C.webkit_web_view_new_with_user_content_manager(manager)
|
|
||||||
id := C.uint(w.parent.id)
|
|
||||||
if !registered {
|
|
||||||
wails := C.CString("wails")
|
|
||||||
C.webkit_web_context_register_uri_scheme(
|
|
||||||
C.webkit_web_context_get_default(),
|
|
||||||
wails,
|
|
||||||
C.WebKitURISchemeRequestCallback(C.processRequest),
|
|
||||||
C.gpointer(&id),
|
|
||||||
nil)
|
|
||||||
registered = true
|
|
||||||
C.free(unsafe.Pointer(wails))
|
|
||||||
}
|
|
||||||
settings := C.webkit_web_view_get_settings((*C.WebKitWebView)(unsafe.Pointer(webview)))
|
|
||||||
wails_io := C.CString("wails.io")
|
|
||||||
empty := C.CString("")
|
|
||||||
defer C.free(unsafe.Pointer(wails_io))
|
|
||||||
defer C.free(unsafe.Pointer(empty))
|
|
||||||
C.webkit_settings_set_user_agent_with_application_details(settings, wails_io, empty)
|
|
||||||
|
|
||||||
switch gpuPolicy {
|
|
||||||
case 0:
|
|
||||||
C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS)
|
|
||||||
break
|
|
||||||
case 1:
|
|
||||||
C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND)
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
C.webkit_settings_set_hardware_acceleration_policy(settings, C.WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND)
|
|
||||||
}
|
|
||||||
return unsafe.Pointer(webview)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) connectSignals() {
|
func (w *linuxWebviewWindow) connectSignals() {
|
||||||
event := C.CString("delete-event")
|
cb := func(e events.WindowEventType) {
|
||||||
defer C.free(unsafe.Pointer(event))
|
w.parent.emit(e)
|
||||||
|
|
||||||
// Window close handler
|
|
||||||
|
|
||||||
if w.parent.options.HideOnClose {
|
|
||||||
C.signal_connect((*C.GtkWidget)(w.window), event, C.gtk_widget_hide_on_delete, C.NULL)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// C.signal_connect((*C.GtkWidget)(window), event, C.close_button_pressed, w.parent.id)
|
|
||||||
}
|
}
|
||||||
/*
|
windowSetupSignalHandlers(w.parent.id, w.window, w.webview, cb)
|
||||||
event = C.CString("load-changed")
|
|
||||||
defer C.free(unsafe.Pointer(event))
|
|
||||||
C.signal_connect(webview, event, C.webviewLoadChanged, unsafe.Pointer(&w.parent.id))
|
|
||||||
*/
|
|
||||||
id := C.uint(w.parent.id)
|
|
||||||
event = C.CString("button-press-event")
|
|
||||||
C.signal_connect((*C.GtkWidget)(unsafe.Pointer(w.webview)), event, C.buttonEvent, unsafe.Pointer(&id))
|
|
||||||
C.free(unsafe.Pointer(event))
|
|
||||||
event = C.CString("button-release-event")
|
|
||||||
defer C.free(unsafe.Pointer(event))
|
|
||||||
C.signal_connect((*C.GtkWidget)(unsafe.Pointer(w.webview)), event, C.buttonEvent, unsafe.Pointer(&id))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) openContextMenu(menu *Menu, data *ContextMenuData) {
|
func (w *linuxWebviewWindow) openContextMenu(menu *Menu, data *ContextMenuData) {
|
||||||
// Create the menu
|
// Create the menu
|
||||||
thisMenu := newMenuImpl(menu)
|
thisMenu := newMenuImpl(menu)
|
||||||
thisMenu.update()
|
thisMenu.update()
|
||||||
fmt.Println("linux.openContextMenu()")
|
fmt.Println("linux.openContextMenu() - not implemented")
|
||||||
/* void
|
/* void
|
||||||
gtk_menu_popup_at_rect (
|
gtk_menu_popup_at_rect (
|
||||||
GtkMenu* menu,
|
GtkMenu* menu,
|
||||||
@ -261,25 +77,19 @@ func (w *linuxWebviewWindow) openContextMenu(menu *Menu, data *ContextMenuData)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) getZoom() float64 {
|
func (w *linuxWebviewWindow) getZoom() float64 {
|
||||||
return float64(C.webkit_web_view_get_zoom_level((*C.WebKitWebView)(w.webview)))
|
return windowZoom(w.webview)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setZoom(zoom float64) {
|
func (w *linuxWebviewWindow) setZoom(zoom float64) {
|
||||||
C.webkit_web_view_set_zoom_level((*C.WebKitWebView)(w.webview), C.double(zoom))
|
windowZoomSet(w.webview, zoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setFrameless(frameless bool) {
|
func (w *linuxWebviewWindow) setFrameless(frameless bool) {
|
||||||
if frameless {
|
windowSetFrameless(w.window, frameless)
|
||||||
C.gtk_window_set_decorated((*C.GtkWindow)(w.window), C.gboolean(0))
|
|
||||||
} else {
|
|
||||||
C.gtk_window_set_decorated((*C.GtkWindow)(w.window), C.gboolean(1))
|
|
||||||
// TODO: Deal with transparency for the titlebar if possible
|
|
||||||
// Perhaps we just make it undecorated and add a menu bar inside?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) getScreen() (*Screen, error) {
|
func (w *linuxWebviewWindow) getScreen() (*Screen, error) {
|
||||||
mx, my, width, height, scale := w.getCurrentMonitorGeometry()
|
mx, my, width, height, scale := windowGetCurrentMonitorGeometry(w.window)
|
||||||
return &Screen{
|
return &Screen{
|
||||||
ID: fmt.Sprintf("%d", w.id), // A unique identifier for the display
|
ID: fmt.Sprintf("%d", w.id), // A unique identifier for the display
|
||||||
Name: w.parent.Name(), // The name of the display
|
Name: w.parent.Name(), // The name of the display
|
||||||
@ -294,14 +104,22 @@ func (w *linuxWebviewWindow) getScreen() (*Screen, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *linuxWebviewWindow) focus() {
|
||||||
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
|
windowPresent(w.window)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) show() {
|
func (w *linuxWebviewWindow) show() {
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
C.gtk_widget_show_all((*C.GtkWidget)(w.window))
|
windowShow(w.window)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) hide() {
|
func (w *linuxWebviewWindow) hide() {
|
||||||
C.gtk_widget_hide((*C.GtkWidget)(w.window))
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
|
windowHide(w.window)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) isNormal() bool {
|
func (w *linuxWebviewWindow) isNormal() bool {
|
||||||
@ -309,10 +127,7 @@ func (w *linuxWebviewWindow) isNormal() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) isVisible() bool {
|
func (w *linuxWebviewWindow) isVisible() bool {
|
||||||
if C.gtk_widget_is_visible((*C.GtkWidget)(w.window)) == 1 {
|
return windowIsVisible(w.window)
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setFullscreenButtonEnabled(enabled bool) {
|
func (w *linuxWebviewWindow) setFullscreenButtonEnabled(enabled bool) {
|
||||||
@ -321,14 +136,14 @@ func (w *linuxWebviewWindow) setFullscreenButtonEnabled(enabled bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) disableSizeConstraints() {
|
func (w *linuxWebviewWindow) disableSizeConstraints() {
|
||||||
x, y, width, height, scale := w.getCurrentMonitorGeometry()
|
x, y, width, height, scale := windowGetCurrentMonitorGeometry(w.window)
|
||||||
w.setMinMaxSize(x, y, width*scale, height*scale)
|
w.setMinMaxSize(x, y, width*scale, height*scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) unfullscreen() {
|
func (w *linuxWebviewWindow) unfullscreen() {
|
||||||
fmt.Println("unfullscreen")
|
fmt.Println("unfullscreen")
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
C.gtk_window_unfullscreen((*C.GtkWindow)(w.window))
|
windowUnfullscreen(w.window)
|
||||||
w.unmaximise()
|
w.unmaximise()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -337,38 +152,31 @@ func (w *linuxWebviewWindow) fullscreen() {
|
|||||||
w.maximise()
|
w.maximise()
|
||||||
w.lastWidth, w.lastHeight = w.size()
|
w.lastWidth, w.lastHeight = w.size()
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
x, y, width, height, scale := w.getCurrentMonitorGeometry()
|
x, y, width, height, scale := windowGetCurrentMonitorGeometry(w.window)
|
||||||
if x == -1 && y == -1 && width == -1 && height == -1 {
|
if x == -1 && y == -1 && width == -1 && height == -1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.setMinMaxSize(0, 0, width*scale, height*scale)
|
w.setMinMaxSize(0, 0, width*scale, height*scale)
|
||||||
w.setSize(width*scale, height*scale)
|
w.setSize(width*scale, height*scale)
|
||||||
C.gtk_window_fullscreen((*C.GtkWindow)(w.window))
|
windowFullscreen(w.window)
|
||||||
w.setRelativePosition(0, 0)
|
w.setRelativePosition(0, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setEnabled(enabled bool) {
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
C.gtk_widget_set_sensitive((*C.GtkWidget)(w.window), C.gboolean(enabled))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) unminimise() {
|
func (w *linuxWebviewWindow) unminimise() {
|
||||||
C.gtk_window_present((*C.GtkWindow)(w.window))
|
windowPresent(w.window)
|
||||||
// gtk_window_unminimize ((*C.GtkWindow)(w.window)) /// gtk4
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) unmaximise() {
|
func (w *linuxWebviewWindow) unmaximise() {
|
||||||
C.gtk_window_unmaximize((*C.GtkWindow)(w.window))
|
windowUnmaximize(w.window)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) maximise() {
|
func (w *linuxWebviewWindow) maximise() {
|
||||||
C.gtk_window_maximize((*C.GtkWindow)(w.window))
|
windowMaximize(w.window)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) minimise() {
|
func (w *linuxWebviewWindow) minimise() {
|
||||||
C.gtk_window_iconify((*C.GtkWindow)(w.window))
|
windowMinimize(w.window)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) on(eventID uint) {
|
func (w *linuxWebviewWindow) on(eventID uint) {
|
||||||
@ -387,96 +195,58 @@ func (w *linuxWebviewWindow) windowZoom() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) close() {
|
func (w *linuxWebviewWindow) close() {
|
||||||
C.gtk_window_close((*C.GtkWindow)(w.window))
|
windowClose(w.window)
|
||||||
if !w.parent.options.HideOnClose {
|
|
||||||
globalApplication.deleteWindowByID(w.parent.id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) zoomIn() {
|
func (w *linuxWebviewWindow) zoomIn() {
|
||||||
lvl := C.webkit_web_view_get_zoom_level((*C.WebKitWebView)(w.webview))
|
windowZoomIn(w.webview)
|
||||||
C.webkit_web_view_set_zoom_level((*C.WebKitWebView)(w.webview), lvl+0.5)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) zoomOut() {
|
func (w *linuxWebviewWindow) zoomOut() {
|
||||||
lvl := C.webkit_web_view_get_zoom_level((*C.WebKitWebView)(w.webview))
|
windowZoomOut(w.webview)
|
||||||
C.webkit_web_view_set_zoom_level((*C.WebKitWebView)(w.webview), lvl-0.5)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) zoomReset() {
|
func (w *linuxWebviewWindow) zoomReset() {
|
||||||
C.webkit_web_view_set_zoom_level((*C.WebKitWebView)(w.webview), 0.0)
|
windowZoomSet(w.webview, 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) reload() {
|
func (w *linuxWebviewWindow) reload() {
|
||||||
// TODO: This should be a constant somewhere I feel
|
windowReload(w.webview, "wails://")
|
||||||
uri := C.CString("wails://")
|
|
||||||
C.webkit_web_view_load_uri((*C.WebKitWebView)(w.window), uri)
|
|
||||||
C.free(unsafe.Pointer(uri))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) forceReload() {
|
func (w *linuxWebviewWindow) forceReload() {
|
||||||
w.reload()
|
w.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w linuxWebviewWindow) getCurrentMonitor() *C.GdkMonitor {
|
|
||||||
// Get the monitor that the window is currently on
|
|
||||||
display := C.gtk_widget_get_display((*C.GtkWidget)(w.window))
|
|
||||||
gdk_window := C.gtk_widget_get_window((*C.GtkWidget)(w.window))
|
|
||||||
if gdk_window == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return C.gdk_display_get_monitor_at_window(display, gdk_window)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w linuxWebviewWindow) getCurrentMonitorGeometry() (x int, y int, width int, height int, scale int) {
|
|
||||||
monitor := w.getCurrentMonitor()
|
|
||||||
if monitor == nil {
|
|
||||||
return -1, -1, -1, -1, 1
|
|
||||||
}
|
|
||||||
var result C.GdkRectangle
|
|
||||||
C.gdk_monitor_get_geometry(monitor, &result)
|
|
||||||
scale = int(C.gdk_monitor_get_scale_factor(monitor))
|
|
||||||
return int(result.x), int(result.y), int(result.width), int(result.height), scale
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) center() {
|
func (w *linuxWebviewWindow) center() {
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
x, y, width, height, _ := w.getCurrentMonitorGeometry()
|
x, y, width, height, _ := windowGetCurrentMonitorGeometry(w.window)
|
||||||
if x == -1 && y == -1 && width == -1 && height == -1 {
|
if x == -1 && y == -1 && width == -1 && height == -1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
windowWidth, windowHeight := windowGetSize(w.window)
|
||||||
|
|
||||||
var windowWidth C.int
|
newX := ((width - int(windowWidth)) / 2) + x
|
||||||
var windowHeight C.int
|
newY := ((height - int(windowHeight)) / 2) + y
|
||||||
C.gtk_window_get_size((*C.GtkWindow)(w.window), &windowWidth, &windowHeight)
|
|
||||||
|
|
||||||
newX := C.int(((width - int(windowWidth)) / 2) + x)
|
|
||||||
newY := C.int(((height - int(windowHeight)) / 2) + y)
|
|
||||||
|
|
||||||
// Place the window at the center of the monitor
|
// Place the window at the center of the monitor
|
||||||
C.gtk_window_move((*C.GtkWindow)(w.window), newX, newY)
|
windowMove(w.window, newX, newY)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) isMinimised() bool {
|
func (w *linuxWebviewWindow) isMinimised() bool {
|
||||||
gdkwindow := C.gtk_widget_get_window((*C.GtkWidget)(w.window))
|
return windowIsMinimized(w.window)
|
||||||
state := C.gdk_window_get_state(gdkwindow)
|
|
||||||
return state&C.GDK_WINDOW_STATE_ICONIFIED > 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) isMaximised() bool {
|
func (w *linuxWebviewWindow) isMaximised() bool {
|
||||||
return w.syncMainThreadReturningBool(func() bool {
|
return w.syncMainThreadReturningBool(func() bool {
|
||||||
gdkwindow := C.gtk_widget_get_window((*C.GtkWidget)(w.window))
|
return windowIsMaximized(w.window)
|
||||||
state := C.gdk_window_get_state(gdkwindow)
|
|
||||||
return state&C.GDK_WINDOW_STATE_MAXIMIZED > 0 && state&C.GDK_WINDOW_STATE_FULLSCREEN == 0
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) isFullscreen() bool {
|
func (w *linuxWebviewWindow) isFullscreen() bool {
|
||||||
return w.syncMainThreadReturningBool(func() bool {
|
return w.syncMainThreadReturningBool(func() bool {
|
||||||
gdkwindow := C.gtk_widget_get_window((*C.GtkWidget)(w.window))
|
return windowIsFullscreen(w.window)
|
||||||
state := C.gdk_window_get_state(gdkwindow)
|
|
||||||
return state&C.GDK_WINDOW_STATE_FULLSCREEN > 0
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,16 +268,7 @@ func (w *linuxWebviewWindow) restore() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) execJS(js string) {
|
func (w *linuxWebviewWindow) execJS(js string) {
|
||||||
value := C.CString(js)
|
windowExecJS(w.webview, js)
|
||||||
C.webkit_web_view_evaluate_javascript((*C.WebKitWebView)(w.webview),
|
|
||||||
value,
|
|
||||||
C.long(len(js)),
|
|
||||||
nil,
|
|
||||||
C.CString(""),
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
nil)
|
|
||||||
C.free(unsafe.Pointer(value))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setURL(uri string) {
|
func (w *linuxWebviewWindow) setURL(uri string) {
|
||||||
@ -520,13 +281,11 @@ func (w *linuxWebviewWindow) setURL(uri string) {
|
|||||||
uri = url.String()
|
uri = url.String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target := C.CString(uri)
|
windowSetURL(w.webview, uri)
|
||||||
C.webkit_web_view_load_uri((*C.WebKitWebView)(w.webview), target)
|
|
||||||
C.free(unsafe.Pointer(target))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) {
|
func (w *linuxWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) {
|
||||||
C.gtk_window_set_keep_above((*C.GtkWindow)(w.window), gtkBool(alwaysOnTop))
|
windowSetKeepAbove(w.window, alwaysOnTop)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWindowImpl(parent *WebviewWindow) *linuxWebviewWindow {
|
func newWindowImpl(parent *WebviewWindow) *linuxWebviewWindow {
|
||||||
@ -541,18 +300,15 @@ func newWindowImpl(parent *WebviewWindow) *linuxWebviewWindow {
|
|||||||
|
|
||||||
func (w *linuxWebviewWindow) setTitle(title string) {
|
func (w *linuxWebviewWindow) setTitle(title string) {
|
||||||
if !w.parent.options.Frameless {
|
if !w.parent.options.Frameless {
|
||||||
cTitle := C.CString(title)
|
windowSetTitle(w.window, title)
|
||||||
C.gtk_window_set_title((*C.GtkWindow)(w.window), cTitle)
|
|
||||||
C.free(unsafe.Pointer(cTitle))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setSize(width, height int) {
|
func (w *linuxWebviewWindow) setSize(width, height int) {
|
||||||
C.gtk_window_resize((*C.GtkWindow)(w.window), C.gint(width), C.gint(height))
|
windowResize(w.window, width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setMinMaxSize(minWidth, minHeight, maxWidth, maxHeight int) {
|
func (w *linuxWebviewWindow) setMinMaxSize(minWidth, minHeight, maxWidth, maxHeight int) {
|
||||||
fmt.Println("setMinMaxSize", minWidth, minHeight, maxWidth, maxHeight)
|
|
||||||
if minWidth == 0 {
|
if minWidth == 0 {
|
||||||
minWidth = -1
|
minWidth = -1
|
||||||
}
|
}
|
||||||
@ -565,13 +321,7 @@ func (w *linuxWebviewWindow) setMinMaxSize(minWidth, minHeight, maxWidth, maxHei
|
|||||||
if maxHeight == 0 {
|
if maxHeight == 0 {
|
||||||
maxHeight = -1
|
maxHeight = -1
|
||||||
}
|
}
|
||||||
size := C.GdkGeometry{
|
windowSetGeometryHints(w.window, minWidth, minHeight, maxWidth, maxHeight)
|
||||||
min_width: C.int(minWidth),
|
|
||||||
min_height: C.int(minHeight),
|
|
||||||
max_width: C.int(maxWidth),
|
|
||||||
max_height: C.int(maxHeight),
|
|
||||||
}
|
|
||||||
C.gtk_window_set_geometry_hints((*C.GtkWindow)(w.window), nil, &size, C.GDK_HINT_MAX_SIZE|C.GDK_HINT_MIN_SIZE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setMinSize(width, height int) {
|
func (w *linuxWebviewWindow) setMinSize(width, height int) {
|
||||||
@ -583,40 +333,33 @@ func (w *linuxWebviewWindow) setMaxSize(width, height int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setResizable(resizable bool) {
|
func (w *linuxWebviewWindow) setResizable(resizable bool) {
|
||||||
if resizable {
|
windowSetResizable(w.window, resizable)
|
||||||
C.gtk_window_set_resizable((*C.GtkWindow)(w.window), 1)
|
|
||||||
} else {
|
|
||||||
C.gtk_window_set_resizable((*C.GtkWindow)(w.window), 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) toggleDevTools() {
|
func (w *linuxWebviewWindow) toggleDevTools() {
|
||||||
settings := C.webkit_web_view_get_settings((*C.WebKitWebView)(w.webview))
|
windowToggleDevTools(w.webview)
|
||||||
enabled := C.webkit_settings_get_enable_developer_extras(settings)
|
|
||||||
if enabled == C.int(0) {
|
|
||||||
enabled = C.int(1)
|
|
||||||
} else {
|
|
||||||
enabled = C.int(0)
|
|
||||||
}
|
|
||||||
C.webkit_settings_set_enable_developer_extras(settings, enabled)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) size() (int, int) {
|
func (w *linuxWebviewWindow) size() (int, int) {
|
||||||
var width, height C.int
|
/* var width, height C.int
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
C.gtk_window_get_size((*C.GtkWindow)(w.window), &width, &height)
|
|
||||||
wg.Done()
|
C.gtk_window_get_size((*C.GtkWindow)(w.window), &width, &height)
|
||||||
})
|
wg.Done()
|
||||||
wg.Wait()
|
})
|
||||||
return int(width), int(height)
|
wg.Wait()
|
||||||
|
return int(width), int(height)
|
||||||
|
*/
|
||||||
|
// Does this need to be guarded?
|
||||||
|
return windowGetSize(w.window)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setRelativePosition(x, y int) {
|
func (w *linuxWebviewWindow) setRelativePosition(x, y int) {
|
||||||
mx, my, _, _, _ := w.getCurrentMonitorGeometry()
|
mx, my, _, _, _ := windowGetCurrentMonitorGeometry(w.window)
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
C.gtk_window_move((*C.GtkWindow)(w.window), C.int(x+mx), C.int(y+my))
|
windowMove(w.window, x+mx, y+my)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,16 +373,21 @@ func (w *linuxWebviewWindow) height() int {
|
|||||||
return height
|
return height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *linuxWebviewWindow) setAbsolutePosition(x int, y int) {
|
||||||
|
// Set the window's absolute position
|
||||||
|
windowMove(w.window, x, y)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) absolutePosition() (int, int) {
|
func (w *linuxWebviewWindow) absolutePosition() (int, int) {
|
||||||
var x, y C.int
|
var x, y int
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
C.gtk_window_get_position((*C.GtkWindow)(w.window), &x, &y)
|
x, y = windowGetAbsolutePosition(w.window)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
})
|
})
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return int(x), int(y)
|
return x, y
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) run() {
|
func (w *linuxWebviewWindow) run() {
|
||||||
@ -651,21 +399,12 @@ func (w *linuxWebviewWindow) run() {
|
|||||||
menu := app.applicationMenu
|
menu := app.applicationMenu
|
||||||
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
w.window = unsafe.Pointer(C.gtk_application_window_new((*C.GtkApplication)(w.application)))
|
w.window, w.webview = windowNew(app.application, menu, w.parent.id, 1)
|
||||||
app.registerWindow((*C.GtkWindow)(w.window), w.parent.id) // record our mapping
|
app.registerWindow(w.window, w.parent.id) // record our mapping
|
||||||
C.g_object_ref_sink(C.gpointer(w.window))
|
|
||||||
w.webview = w.newWebview(1)
|
|
||||||
w.connectSignals()
|
w.connectSignals()
|
||||||
if w.parent.options.EnableDragAndDrop {
|
if w.parent.options.EnableDragAndDrop {
|
||||||
w.enableDND()
|
w.enableDND()
|
||||||
}
|
}
|
||||||
w.vbox = C.gtk_box_new(C.GTK_ORIENTATION_VERTICAL, 0)
|
|
||||||
C.gtk_container_add((*C.GtkContainer)(w.window), w.vbox)
|
|
||||||
if menu != nil {
|
|
||||||
C.gtk_box_pack_start((*C.GtkBox)(unsafe.Pointer(w.vbox)), (*C.GtkWidget)(menu), 0, 0, 0)
|
|
||||||
}
|
|
||||||
C.gtk_box_pack_start((*C.GtkBox)(unsafe.Pointer(w.vbox)), (*C.GtkWidget)(w.webview), 1, 1, 0)
|
|
||||||
|
|
||||||
w.setTitle(w.parent.options.Title)
|
w.setTitle(w.parent.options.Title)
|
||||||
w.setAlwaysOnTop(w.parent.options.AlwaysOnTop)
|
w.setAlwaysOnTop(w.parent.options.AlwaysOnTop)
|
||||||
w.setResizable(!w.parent.options.DisableResize)
|
w.setResizable(!w.parent.options.DisableResize)
|
||||||
@ -712,7 +451,6 @@ func (w *linuxWebviewWindow) run() {
|
|||||||
}
|
}
|
||||||
if w.parent.options.CSS != "" {
|
if w.parent.options.CSS != "" {
|
||||||
js := fmt.Sprintf("(function() { var style = document.createElement('style'); style.appendChild(document.createTextNode('%s')); document.head.appendChild(style); })();", w.parent.options.CSS)
|
js := fmt.Sprintf("(function() { var style = document.createElement('style'); style.appendChild(document.createTextNode('%s')); document.head.appendChild(style); })();", w.parent.options.CSS)
|
||||||
fmt.Println(js)
|
|
||||||
w.execJS(js)
|
w.execJS(js)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -724,74 +462,59 @@ func (w *linuxWebviewWindow) run() {
|
|||||||
if w.parent.options.X != 0 || w.parent.options.Y != 0 {
|
if w.parent.options.X != 0 || w.parent.options.Y != 0 {
|
||||||
w.setRelativePosition(w.parent.options.X, w.parent.options.Y)
|
w.setRelativePosition(w.parent.options.X, w.parent.options.Y)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("attempting to set in the center")
|
w.center() // needs to be queued until after GTK starts up!
|
||||||
w.center()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setTransparent() {
|
func (w *linuxWebviewWindow) setTransparent() {
|
||||||
screen := C.gtk_widget_get_screen((*C.GtkWidget)(w.window))
|
windowSetTransparent(w.window)
|
||||||
visual := C.gdk_screen_get_rgba_visual(screen)
|
|
||||||
|
|
||||||
if visual != nil && C.gdk_screen_is_composited(screen) == C.int(1) {
|
|
||||||
C.gtk_widget_set_app_paintable((*C.GtkWidget)(w.window), C.gboolean(1))
|
|
||||||
C.gtk_widget_set_visual((*C.GtkWidget)(w.window), visual)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setBackgroundColour(colour RGBA) {
|
func (w *linuxWebviewWindow) setBackgroundColour(colour RGBA) {
|
||||||
if colour.Alpha != 0 {
|
if colour.Alpha != 0 {
|
||||||
w.setTransparent()
|
w.setTransparent()
|
||||||
}
|
}
|
||||||
rgba := C.GdkRGBA{C.double(colour.Red) / 255.0, C.double(colour.Green) / 255.0, C.double(colour.Blue) / 255.0, C.double(colour.Alpha) / 255.0}
|
windowSetBackgroundColour(w.webview, colour)
|
||||||
fmt.Println(unsafe.Pointer(&rgba))
|
|
||||||
C.webkit_web_view_set_background_color((*C.WebKitWebView)(w.webview), &rgba)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) relativePosition() (int, int) {
|
func (w *linuxWebviewWindow) relativePosition() (int, int) {
|
||||||
var x, y C.int
|
var x, y int
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go globalApplication.dispatchOnMainThread(func() {
|
go globalApplication.dispatchOnMainThread(func() {
|
||||||
C.gtk_window_get_position((*C.GtkWindow)(w.window), &x, &y)
|
x, y = windowGetRelativePosition(w.window)
|
||||||
|
|
||||||
// The position must be relative to the screen it is on
|
|
||||||
// We need to get the screen it is on
|
|
||||||
screen := C.gtk_widget_get_screen((*C.GtkWidget)(w.window))
|
|
||||||
monitor := C.gdk_screen_get_monitor_at_window(screen, (*C.GdkWindow)(w.window))
|
|
||||||
geometry := C.GdkRectangle{}
|
|
||||||
C.gdk_screen_get_monitor_geometry(screen, monitor, &geometry)
|
|
||||||
x = x - geometry.x
|
|
||||||
y = y - geometry.y
|
|
||||||
|
|
||||||
// TODO: Scale based on DPI
|
|
||||||
|
|
||||||
wg.Done()
|
wg.Done()
|
||||||
})
|
})
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return int(x), int(y)
|
return x, y
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) destroy() {
|
func (w *linuxWebviewWindow) destroy() {
|
||||||
C.gtk_window_close((*C.GtkWindow)(w.window))
|
windowDestroy(w.window)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *linuxWebviewWindow) setEnabled(enabled bool) {
|
||||||
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
|
widgetSetSensitive(w.window, enabled)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setHTML(html string) {
|
func (w *linuxWebviewWindow) setHTML(html string) {
|
||||||
cHTML := C.CString(html)
|
windowSetHTML(w.webview, html)
|
||||||
uri := C.CString("wails://")
|
}
|
||||||
empty := C.CString("")
|
|
||||||
defer C.free(unsafe.Pointer(cHTML))
|
func (w *linuxWebviewWindow) startResize(border string) error {
|
||||||
defer C.free(unsafe.Pointer(uri))
|
// FIXME: what do we need to do here?
|
||||||
defer C.free(unsafe.Pointer(empty))
|
return nil
|
||||||
C.webkit_web_view_load_alternate_html(
|
|
||||||
(*C.WebKitWebView)(w.webview),
|
|
||||||
cHTML,
|
|
||||||
uri,
|
|
||||||
empty)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) nativeWindowHandle() uintptr {
|
func (w *linuxWebviewWindow) nativeWindowHandle() uintptr {
|
||||||
return uintptr(w.window)
|
return uintptr(w.window)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *linuxWebviewWindow) print() error {
|
||||||
|
w.execJS("window.print();")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1,764 +0,0 @@
|
|||||||
//go:build linux && purego
|
|
||||||
|
|
||||||
package application
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/ebitengine/purego"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/events"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
registered bool = false // avoid 'already registered message'
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/gdkwindow.h#L121
|
|
||||||
GDK_HINT_MIN_SIZE = 1 << 1
|
|
||||||
GDK_HINT_MAX_SIZE = 1 << 2
|
|
||||||
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/gdkevents.h#L512
|
|
||||||
GDK_WINDOW_STATE_ICONIFIED = 1 << 1
|
|
||||||
GDK_WINDOW_STATE_MAXIMIZED = 1 << 2
|
|
||||||
GDK_WINDOW_STATE_FULLSCREEN = 1 << 4
|
|
||||||
)
|
|
||||||
|
|
||||||
type GdkGeometry struct {
|
|
||||||
minWidth int32
|
|
||||||
minHeight int32
|
|
||||||
maxWidth int32
|
|
||||||
maxHeight int32
|
|
||||||
baseWidth int32
|
|
||||||
baseHeight int32
|
|
||||||
widthInc int32
|
|
||||||
heightInc int32
|
|
||||||
padding int32
|
|
||||||
minAspect float64
|
|
||||||
maxAspect float64
|
|
||||||
GdkGravity int32
|
|
||||||
}
|
|
||||||
|
|
||||||
type linuxWebviewWindow struct {
|
|
||||||
application uintptr
|
|
||||||
window uintptr
|
|
||||||
webview uintptr
|
|
||||||
parent *WebviewWindow
|
|
||||||
menubar uintptr
|
|
||||||
vbox uintptr
|
|
||||||
menu *menu.Menu
|
|
||||||
accels uintptr
|
|
||||||
minWidth, minHeight, maxWidth, maxHeight int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) newWebview(gpuPolicy int) uintptr {
|
|
||||||
var newContentMgr func() uintptr
|
|
||||||
purego.RegisterLibFunc(
|
|
||||||
&newContentMgr,
|
|
||||||
webkit,
|
|
||||||
"webkit_user_content_manager_new")
|
|
||||||
var registerScriptMessageHandler func(uintptr, string)
|
|
||||||
purego.RegisterLibFunc(®isterScriptMessageHandler, webkit, "webkit_user_content_manager_register_script_message_handler")
|
|
||||||
var newWebview func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&newWebview, webkit, "webkit_web_view_new_with_user_content_manager")
|
|
||||||
|
|
||||||
manager := newContentMgr()
|
|
||||||
registerScriptMessageHandler(manager, "external")
|
|
||||||
webview := newWebview(manager)
|
|
||||||
if !registered {
|
|
||||||
var registerUriScheme func(uintptr, string, uintptr, uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(®isterUriScheme, webkit, "webkit_web_context_register_uri_scheme")
|
|
||||||
cb := purego.NewCallback(func(request uintptr) {
|
|
||||||
processURLRequest(w.parent.id, request)
|
|
||||||
})
|
|
||||||
var defaultContext func() uintptr
|
|
||||||
purego.RegisterLibFunc(&defaultContext, webkit, "webkit_web_context_get_default")
|
|
||||||
registerUriScheme(defaultContext(), "wails", cb, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
var g_signal_connect func(uintptr, string, uintptr, uintptr, bool, int) int
|
|
||||||
purego.RegisterLibFunc(&g_signal_connect, gtk, "g_signal_connect_data")
|
|
||||||
|
|
||||||
loadChanged := purego.NewCallback(func(window uintptr) {
|
|
||||||
//fmt.Println("loadChanged", window)
|
|
||||||
})
|
|
||||||
if g_signal_connect(webview, "load-changed", loadChanged, 0, false, 0) == 0 {
|
|
||||||
fmt.Println("failed to connect 'load-changed' event")
|
|
||||||
}
|
|
||||||
|
|
||||||
if g_signal_connect(webview, "button-press-event", purego.NewCallback(w.buttonPress), 0, false, 0) == 0 {
|
|
||||||
fmt.Println("failed to connect 'button-press-event")
|
|
||||||
}
|
|
||||||
if g_signal_connect(webview, "button-release-event", purego.NewCallback(w.buttonRelease), 0, false, 0) == 0 {
|
|
||||||
fmt.Println("failed to connect 'button-release-event")
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDelete := purego.NewCallback(func(uintptr) {
|
|
||||||
w.close()
|
|
||||||
if !w.parent.options.HideOnClose {
|
|
||||||
fmt.Println("Need to do more!")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
g_signal_connect(w.window, "delete-event", handleDelete, 0, false, 0)
|
|
||||||
|
|
||||||
var getSettings func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getSettings, webkit, "webkit_web_view_get_settings")
|
|
||||||
var setSettings func(uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(&setSettings, webkit, "webkit_web_view_set_settings")
|
|
||||||
var setUserAgent func(uintptr, string, string)
|
|
||||||
purego.RegisterLibFunc(&setUserAgent, webkit, "webkit_settings_set_user_agent_with_application_details")
|
|
||||||
settings := getSettings(webview)
|
|
||||||
setUserAgent(settings, "wails.io", "")
|
|
||||||
|
|
||||||
var setHWAccel func(uintptr, int)
|
|
||||||
purego.RegisterLibFunc(&setHWAccel, webkit, "webkit_settings_set_hardware_acceleration_policy")
|
|
||||||
|
|
||||||
setHWAccel(settings, gpuPolicy)
|
|
||||||
setSettings(webview, settings)
|
|
||||||
|
|
||||||
return webview
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) openContextMenu(menu *Menu, data *ContextMenuData) {
|
|
||||||
// Create the menu
|
|
||||||
thisMenu := newMenuImpl(menu)
|
|
||||||
thisMenu.update()
|
|
||||||
fmt.Println("linux.openContextMenu()")
|
|
||||||
//C.windowShowMenu(w.nsWindow, thisMenu.nsMenu, C.int(data.X), C.int(data.Y))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) getZoom() float64 {
|
|
||||||
var getZoom func(uintptr) float32
|
|
||||||
purego.RegisterLibFunc(&getZoom, webkit, "webkit_web_view_get_zoom_level")
|
|
||||||
return float64(getZoom(w.webview))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setZoom(zoom float64) {
|
|
||||||
var setZoom func(uintptr, float64)
|
|
||||||
purego.RegisterLibFunc(&setZoom, webkit, "webkit_web_view_set_zoom_level")
|
|
||||||
setZoom(w.webview, zoom)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setFrameless(frameless bool) {
|
|
||||||
var setDecorated func(uintptr, int)
|
|
||||||
purego.RegisterLibFunc(&setDecorated, gtk, "gtk_window_set_decorated")
|
|
||||||
decorated := 1
|
|
||||||
if frameless {
|
|
||||||
decorated = 0
|
|
||||||
}
|
|
||||||
setDecorated(w.window, decorated)
|
|
||||||
if !frameless {
|
|
||||||
// TODO: Deal with transparency for the titlebar if possible
|
|
||||||
// Perhaps we just make it undecorated and add a menu bar inside?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) getScreen() (*Screen, error) {
|
|
||||||
return getScreenForWindow(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) show() {
|
|
||||||
var widgetShow func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&widgetShow, gtk, "gtk_widget_show_all")
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
widgetShow(w.window)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) hide() {
|
|
||||||
var widgetHide func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&widgetHide, gtk, "gtk_widget_hide")
|
|
||||||
widgetHide(w.window)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setFullscreenButtonEnabled(enabled bool) {
|
|
||||||
// C.setFullscreenButtonEnabled(w.nsWindow, C.bool(enabled))
|
|
||||||
fmt.Println("setFullscreenButtonEnabled - not implemented")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) disableSizeConstraints() {
|
|
||||||
x, y, width, height, scale := w.getCurrentMonitorGeometry()
|
|
||||||
w.setMinMaxSize(x, y, width*scale, height*scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) unfullscreen() {
|
|
||||||
var unfullScreen func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&unfullScreen, gtk, "gtk_window_unfullscreen")
|
|
||||||
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
unfullScreen(w.window)
|
|
||||||
w.unmaximise()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setEnabled(enabled bool) {
|
|
||||||
var gtkWidgetSensitive func(uintptr, int)
|
|
||||||
purego.RegisterLibFunc(>kWidgetSensitive, gtk, "gtk_widget_set_sensitive")
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
gtkWidgetSensitive(w.window, boolToInt(enabled))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) fullscreen() {
|
|
||||||
var fullScreen func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&fullScreen, gtk, "gtk_window_fullscreen")
|
|
||||||
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
w.maximise()
|
|
||||||
// w.lastWidth, w.lastHeight = w.size() // do we need this?
|
|
||||||
|
|
||||||
x, y, width, height, scale := w.getCurrentMonitorGeometry()
|
|
||||||
if x == -1 && y == -1 && width == -1 && height == -1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.setMinMaxSize(0, 0, width*scale, height*scale)
|
|
||||||
w.setSize(width*scale, height*scale)
|
|
||||||
w.setRelativePosition(0, 0)
|
|
||||||
fullScreen(w.window)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) unminimise() {
|
|
||||||
var present func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&present, gtk, "gtk_window_present")
|
|
||||||
present(w.window)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) unmaximise() {
|
|
||||||
var unmaximize func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&unmaximize, gtk, "gtk_window_unmaximize")
|
|
||||||
unmaximize(w.window)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) maximise() {
|
|
||||||
var maximize func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&maximize, gtk, "gtk_window_maximize")
|
|
||||||
maximize(w.window)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) minimise() {
|
|
||||||
var iconify func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&iconify, gtk, "gtk_window_iconify")
|
|
||||||
iconify(w.window)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) on(eventID uint) {
|
|
||||||
// Don't think this is correct!
|
|
||||||
// GTK Events are strings
|
|
||||||
fmt.Println("on()", eventID)
|
|
||||||
//C.registerListener(C.uint(eventID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) zoom() {
|
|
||||||
w.zoomIn()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) windowZoom() {
|
|
||||||
w.zoom()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) close() {
|
|
||||||
var close func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&close, gtk, "gtk_window_close")
|
|
||||||
close(w.window)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) zoomIn() {
|
|
||||||
var getZoom func(uintptr) float32
|
|
||||||
purego.RegisterLibFunc(&getZoom, webkit, "webkit_web_view_get_zoom_level")
|
|
||||||
var setZoom func(uintptr, float32)
|
|
||||||
purego.RegisterLibFunc(&setZoom, webkit, "webkit_web_view_set_zoom_level")
|
|
||||||
lvl := getZoom(w.webview)
|
|
||||||
setZoom(w.webview, lvl+0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) zoomOut() {
|
|
||||||
var getZoom func(uintptr) float32
|
|
||||||
purego.RegisterLibFunc(&getZoom, webkit, "webkit_web_view_get_zoom_level")
|
|
||||||
var setZoom func(uintptr, float32)
|
|
||||||
purego.RegisterLibFunc(&setZoom, webkit, "webkit_web_view_set_zoom_level")
|
|
||||||
lvl := getZoom(w.webview)
|
|
||||||
setZoom(w.webview, lvl-0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) zoomReset() {
|
|
||||||
var setZoom func(uintptr, float32)
|
|
||||||
purego.RegisterLibFunc(&setZoom, webkit, "webkit_web_view_set_zoom_level")
|
|
||||||
setZoom(w.webview, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) toggleDevTools() {
|
|
||||||
var getSettings func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getSettings, webkit, "webkit_web_view_get_settings")
|
|
||||||
var isEnabled func(uintptr) bool
|
|
||||||
purego.RegisterLibFunc(&isEnabled, webkit, "webkit_settings_get_enable_developer_extras")
|
|
||||||
var enableDev func(uintptr, bool)
|
|
||||||
purego.RegisterLibFunc(&enableDev, webkit, "webkit_settings_set_enable_developer_extras")
|
|
||||||
settings := getSettings(w.webview)
|
|
||||||
enabled := isEnabled(settings)
|
|
||||||
enableDev(settings, !enabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) reload() {
|
|
||||||
var reload func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&reload, webkit, "webkit_web_view_reload")
|
|
||||||
reload(w.webview)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) forceReload() {
|
|
||||||
var reload func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&reload, webkit, "webkit_web_view_reload_bypass_cache")
|
|
||||||
reload(w.webview)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w linuxWebviewWindow) getCurrentMonitor() uintptr {
|
|
||||||
var getDisplay func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getDisplay, gtk, "gtk_widget_get_display")
|
|
||||||
var getWindow func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getWindow, gtk, "gtk_widget_get_window")
|
|
||||||
var getMonitor func(uintptr, uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getMonitor, gtk, "gdk_display_get_monitor_at_window")
|
|
||||||
|
|
||||||
display := getDisplay(w.window)
|
|
||||||
window := getWindow(w.window)
|
|
||||||
if window == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return getMonitor(display, window)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w linuxWebviewWindow) getCurrentMonitorGeometry() (x int, y int, width int, height int, scale int) {
|
|
||||||
var getGeometry func(uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(&getGeometry, gtk, "gdk_monitor_get_geometry")
|
|
||||||
var getScaleFactor func(uintptr) int
|
|
||||||
purego.RegisterLibFunc(&getScaleFactor, gtk, "gdk_monitor_get_scale_factor")
|
|
||||||
|
|
||||||
monitor := w.getCurrentMonitor()
|
|
||||||
if monitor == 0 {
|
|
||||||
return -1, -1, -1, -1, 1
|
|
||||||
}
|
|
||||||
result := struct {
|
|
||||||
x int32
|
|
||||||
y int32
|
|
||||||
width int32
|
|
||||||
height int32
|
|
||||||
}{}
|
|
||||||
getGeometry(monitor, uintptr(unsafe.Pointer(&result)))
|
|
||||||
scale = getScaleFactor(monitor)
|
|
||||||
return int(result.x), int(result.y), int(result.width), int(result.height), scale
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) center() {
|
|
||||||
fmt.Println("attempting to set in the center")
|
|
||||||
|
|
||||||
x, y, width, height, _ := w.getCurrentMonitorGeometry()
|
|
||||||
if x == -1 && y == -1 && width == -1 && height == -1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
windowWidth, windowHeight := w.size()
|
|
||||||
|
|
||||||
newX := ((width - int(windowWidth)) / 2) + x
|
|
||||||
newY := ((height - int(windowHeight)) / 2) + y
|
|
||||||
|
|
||||||
w.setRelativePosition(newX, newY)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) isMinimised() bool {
|
|
||||||
var getWindow func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getWindow, gtk, "gtk_widget_get_window")
|
|
||||||
var getWindowState func(uintptr) int
|
|
||||||
purego.RegisterLibFunc(&getWindowState, gtk, "gdk_window_get_state")
|
|
||||||
|
|
||||||
return w.syncMainThreadReturningBool(func() bool {
|
|
||||||
state := getWindowState(getWindow(w.window))
|
|
||||||
return state&GDK_WINDOW_STATE_ICONIFIED > 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) isMaximised() bool {
|
|
||||||
var getWindow func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getWindow, gtk, "gtk_widget_get_window")
|
|
||||||
var getWindowState func(uintptr) int
|
|
||||||
purego.RegisterLibFunc(&getWindowState, gtk, "gdk_window_get_state")
|
|
||||||
|
|
||||||
return w.syncMainThreadReturningBool(func() bool {
|
|
||||||
state := getWindowState(getWindow(w.window))
|
|
||||||
return state&GDK_WINDOW_STATE_MAXIMIZED > 0 && state&GDK_WINDOW_STATE_FULLSCREEN == 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) isFullscreen() bool {
|
|
||||||
var getWindow func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getWindow, gtk, "gtk_widget_get_window")
|
|
||||||
var getWindowState func(uintptr) int
|
|
||||||
purego.RegisterLibFunc(&getWindowState, gtk, "gdk_window_get_state")
|
|
||||||
|
|
||||||
return w.syncMainThreadReturningBool(func() bool {
|
|
||||||
state := getWindowState(getWindow(w.window))
|
|
||||||
return state&GDK_WINDOW_STATE_FULLSCREEN > 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) syncMainThreadReturningBool(fn func() bool) bool {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
var result bool
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
result = fn()
|
|
||||||
wg.Done()
|
|
||||||
})
|
|
||||||
wg.Wait()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) restore() {
|
|
||||||
// restore window to normal size
|
|
||||||
fmt.Println("restore")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) execJS(js string) {
|
|
||||||
var evalJS func(uintptr, string, int, uintptr, string, uintptr, uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(&evalJS, webkit, "webkit_web_view_evaluate_javascript")
|
|
||||||
evalJS(w.webview, js, len(js), 0, "", 0, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setURL(uri string) {
|
|
||||||
fmt.Println("setURL", uri)
|
|
||||||
var loadUri func(uintptr, string)
|
|
||||||
purego.RegisterLibFunc(&loadUri, webkit, "webkit_web_view_load_uri")
|
|
||||||
|
|
||||||
url, err := url.Parse(uri)
|
|
||||||
if url != nil && err == nil && url.Scheme == "" && url.Host == "" {
|
|
||||||
// TODO handle this in a central location, the scheme and host might be platform dependant
|
|
||||||
url.Scheme = "wails"
|
|
||||||
url.Host = "wails"
|
|
||||||
uri = url.String()
|
|
||||||
loadUri(w.webview, uri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) {
|
|
||||||
var keepAbove func(uintptr, bool)
|
|
||||||
purego.RegisterLibFunc(&keepAbove, gtk, "gtk_window_set_keep_above")
|
|
||||||
keepAbove(w.window, alwaysOnTop)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWindowImpl(parent *WebviewWindow) *linuxWebviewWindow {
|
|
||||||
return &linuxWebviewWindow{
|
|
||||||
application: getNativeApplication().application,
|
|
||||||
parent: parent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setTitle(title string) {
|
|
||||||
if !w.parent.options.Frameless {
|
|
||||||
var setTitle func(uintptr, string)
|
|
||||||
purego.RegisterLibFunc(&setTitle, gtk, "gtk_window_set_title")
|
|
||||||
setTitle(w.window, title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setSize(width, height int) {
|
|
||||||
var setSize func(uintptr, int, int)
|
|
||||||
purego.RegisterLibFunc(&setSize, gtk, "gtk_window_set_default_size")
|
|
||||||
setSize(w.window, width, height)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setMinMaxSize(minWidth, minHeight, maxWidth, maxHeight int) {
|
|
||||||
fmt.Println("setMinMaxSize", minWidth, minHeight, maxWidth, maxHeight)
|
|
||||||
if minWidth == 0 {
|
|
||||||
minWidth = -1
|
|
||||||
}
|
|
||||||
if minHeight == 0 {
|
|
||||||
minHeight = -1
|
|
||||||
}
|
|
||||||
if maxWidth == 0 {
|
|
||||||
maxWidth = -1
|
|
||||||
}
|
|
||||||
if maxHeight == 0 {
|
|
||||||
maxHeight = -1
|
|
||||||
}
|
|
||||||
size := GdkGeometry{
|
|
||||||
minWidth: int32(minWidth),
|
|
||||||
minHeight: int32(minHeight),
|
|
||||||
maxWidth: int32(maxWidth),
|
|
||||||
maxHeight: int32(maxHeight),
|
|
||||||
}
|
|
||||||
|
|
||||||
var setHints func(uintptr, uintptr, uintptr, int)
|
|
||||||
purego.RegisterLibFunc(&setHints, gtk, "gtk_window_set_geometry_hints")
|
|
||||||
setHints(w.window, 0, uintptr(unsafe.Pointer(&size)), GDK_HINT_MIN_SIZE|GDK_HINT_MAX_SIZE)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setMinSize(width, height int) {
|
|
||||||
w.setMinMaxSize(width, height, w.parent.options.MaxWidth, w.parent.options.MaxHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setMaxSize(width, height int) {
|
|
||||||
w.setMinMaxSize(w.parent.options.MinWidth, w.parent.options.MinHeight, width, height)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setResizable(resizable bool) {
|
|
||||||
var setResizable func(uintptr, int)
|
|
||||||
purego.RegisterLibFunc(&setResizable, gtk, "gtk_window_set_resizable")
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
if resizable {
|
|
||||||
setResizable(w.window, 1)
|
|
||||||
} else {
|
|
||||||
setResizable(w.window, 0)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) size() (int, int) {
|
|
||||||
var width, height int
|
|
||||||
var windowGetSize func(uintptr, *int, *int)
|
|
||||||
purego.RegisterLibFunc(&windowGetSize, gtk, "gtk_window_get_size")
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
windowGetSize(w.window, &width, &height)
|
|
||||||
wg.Done()
|
|
||||||
})
|
|
||||||
wg.Wait()
|
|
||||||
return width, height
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setRelativePosition(x, y int) {
|
|
||||||
var windowMove func(uintptr, int, int)
|
|
||||||
purego.RegisterLibFunc(&windowMove, gtk, "gtk_window_move")
|
|
||||||
mx, my, _, _, _ := w.getCurrentMonitorGeometry()
|
|
||||||
fmt.Println("setRelativePosition", mx, my)
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
windowMove(w.window, x+mx, y+my)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) width() int {
|
|
||||||
width, _ := w.size()
|
|
||||||
return width
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) height() int {
|
|
||||||
_, height := w.size()
|
|
||||||
return height
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) buttonPress(widget uintptr, event uintptr, user_data uintptr) {
|
|
||||||
GdkEventButton := (*byte)(unsafe.Pointer(event))
|
|
||||||
fmt.Println("buttonpress", w.parent.id, widget, GdkEventButton, user_data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) buttonRelease(widget uintptr, event uintptr, user_data uintptr) {
|
|
||||||
GdkEventButton := (*byte)(unsafe.Pointer(event))
|
|
||||||
fmt.Println("buttonrelease", w.parent.id, widget, GdkEventButton, user_data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) run() {
|
|
||||||
for eventId := range w.parent.eventListeners {
|
|
||||||
w.on(eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
|
||||||
app := getNativeApplication()
|
|
||||||
menu := app.applicationMenu
|
|
||||||
var newWindow func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&newWindow, gtk, "gtk_application_window_new")
|
|
||||||
var refSink func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&refSink, gtk, "g_object_ref_sink")
|
|
||||||
var boxNew func(int, int) uintptr
|
|
||||||
purego.RegisterLibFunc(&boxNew, gtk, "gtk_box_new")
|
|
||||||
var containerAdd func(uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(&containerAdd, gtk, "gtk_container_add")
|
|
||||||
var boxPackStart func(uintptr, uintptr, int, int, int)
|
|
||||||
purego.RegisterLibFunc(&boxPackStart, gtk, "gtk_box_pack_start")
|
|
||||||
|
|
||||||
var g_signal_connect func(uintptr, string, uintptr, uintptr, bool, int) int
|
|
||||||
purego.RegisterLibFunc(&g_signal_connect, gtk, "g_signal_connect_data")
|
|
||||||
|
|
||||||
w.window = newWindow(w.application)
|
|
||||||
|
|
||||||
refSink(w.window)
|
|
||||||
w.webview = w.newWebview(1)
|
|
||||||
w.vbox = boxNew(1, 0)
|
|
||||||
containerAdd(w.window, w.vbox)
|
|
||||||
if menu != 0 {
|
|
||||||
w.menubar = menu
|
|
||||||
boxPackStart(w.vbox, menu, 0, 0, 0)
|
|
||||||
}
|
|
||||||
boxPackStart(w.vbox, w.webview, 1, 1, 0)
|
|
||||||
|
|
||||||
w.setSize(w.parent.options.Width, w.parent.options.Height)
|
|
||||||
w.setTitle(w.parent.options.Title)
|
|
||||||
w.setAlwaysOnTop(w.parent.options.AlwaysOnTop)
|
|
||||||
w.setResizable(!w.parent.options.DisableResize)
|
|
||||||
if w.parent.options.MinWidth != 0 &&
|
|
||||||
w.parent.options.MinHeight != 0 &&
|
|
||||||
w.parent.options.MaxWidth != 0 &&
|
|
||||||
w.parent.options.MaxHeight != 0 {
|
|
||||||
w.setMinMaxSize(
|
|
||||||
w.parent.options.MinWidth,
|
|
||||||
w.parent.options.MinHeight,
|
|
||||||
w.parent.options.MaxWidth,
|
|
||||||
w.parent.options.MaxHeight,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
w.setZoom(w.parent.options.Zoom)
|
|
||||||
w.setBackgroundColour(w.parent.options.BackgroundColour)
|
|
||||||
w.setFrameless(w.parent.options.Frameless)
|
|
||||||
|
|
||||||
switch w.parent.options.StartState {
|
|
||||||
case WindowStateMaximised:
|
|
||||||
w.maximise()
|
|
||||||
case WindowStateMinimised:
|
|
||||||
w.minimise()
|
|
||||||
case WindowStateFullscreen:
|
|
||||||
w.fullscreen()
|
|
||||||
|
|
||||||
}
|
|
||||||
if w.parent.options.URL != "" {
|
|
||||||
w.setURL(w.parent.options.URL)
|
|
||||||
}
|
|
||||||
// We need to wait for the HTML to load before we can execute the javascript
|
|
||||||
w.parent.On(events.Mac.WebViewDidFinishNavigation, func(_ *WindowEventContext) {
|
|
||||||
if w.parent.options.JS != "" {
|
|
||||||
w.execJS(w.parent.options.JS)
|
|
||||||
}
|
|
||||||
if w.parent.options.CSS != "" {
|
|
||||||
js := fmt.Sprintf("(function() { var style = document.createElement('style'); style.appendChild(document.createTextNode('%s')); document.head.appendChild(style); })();", w.parent.options.CSS)
|
|
||||||
w.execJS(js)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if w.parent.options.HTML != "" {
|
|
||||||
w.setHTML(w.parent.options.HTML)
|
|
||||||
}
|
|
||||||
if w.parent.options.Hidden == false {
|
|
||||||
w.show()
|
|
||||||
if w.parent.options.X != 0 || w.parent.options.Y != 0 {
|
|
||||||
w.setRelativePosition(w.parent.options.X, w.parent.options.Y)
|
|
||||||
} else {
|
|
||||||
w.center()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setTransparent() {
|
|
||||||
var getScreen func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getScreen, gtk, "gtk_widget_get_screen")
|
|
||||||
var getVisual func(uintptr) uintptr
|
|
||||||
purego.RegisterLibFunc(&getVisual, gtk, "gdk_screen_get_rgba_visual")
|
|
||||||
var isComposited func(uintptr) int
|
|
||||||
purego.RegisterLibFunc(&isComposited, gtk, "gdk_screen_is_composited")
|
|
||||||
var setPaintable func(uintptr, int)
|
|
||||||
purego.RegisterLibFunc(&setPaintable, gtk, "gtk_widget_set_app_paintable")
|
|
||||||
var setVisual func(uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(&setVisual, gtk, "gtk_widget_set_visual")
|
|
||||||
|
|
||||||
screen := getScreen(w.window)
|
|
||||||
visual := getVisual(screen)
|
|
||||||
if visual == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if isComposited(screen) == 1 {
|
|
||||||
setPaintable(w.window, 1)
|
|
||||||
setVisual(w.window, visual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setBackgroundColour(colour RGBA) {
|
|
||||||
if colour.Alpha != 0 {
|
|
||||||
w.setTransparent()
|
|
||||||
}
|
|
||||||
|
|
||||||
var rgbaParse func(uintptr, string) bool
|
|
||||||
purego.RegisterLibFunc(&rgbaParse, gtk, "gdk_rgba_parse")
|
|
||||||
var setBackgroundColor func(uintptr, uintptr)
|
|
||||||
purego.RegisterLibFunc(&setBackgroundColor, webkit, "webkit_web_view_set_background_color")
|
|
||||||
|
|
||||||
rgba := make([]byte, 4*8) // C.sizeof_GdkRGBA == 32
|
|
||||||
pointer := uintptr(unsafe.Pointer(&rgba[0]))
|
|
||||||
if !rgbaParse(
|
|
||||||
pointer,
|
|
||||||
fmt.Sprintf("rgba(%v,%v,%v,%v)",
|
|
||||||
colour.Red,
|
|
||||||
colour.Green,
|
|
||||||
colour.Blue,
|
|
||||||
float32(colour.Alpha)/255.0,
|
|
||||||
)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setBackgroundColor(w.webview, pointer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) relativePosition() (int, int) {
|
|
||||||
var getPosition func(uintptr, *int, *int) bool
|
|
||||||
purego.RegisterLibFunc(&getPosition, gtk, "gtk_window_get_position")
|
|
||||||
|
|
||||||
var x, y int
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
go globalApplication.dispatchOnMainThread(func() {
|
|
||||||
getPosition(w.window, &x, &y)
|
|
||||||
|
|
||||||
// Get the position of the window relative to the screen
|
|
||||||
var getOrigin func(uintptr, *int, *int)
|
|
||||||
purego.RegisterLibFunc(&getOrigin, gtk, "gtk_widget_translate_coordinates")
|
|
||||||
getOrigin(w.window, &x, &y)
|
|
||||||
|
|
||||||
wg.Done()
|
|
||||||
})
|
|
||||||
wg.Wait()
|
|
||||||
return x, y
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) absolutePosition() (int, int) {
|
|
||||||
var getOrigin func(uintptr, *int, *int)
|
|
||||||
purego.RegisterLibFunc(&getOrigin, gtk, "gtk_widget_translate_coordinates")
|
|
||||||
|
|
||||||
var x, y int
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
go globalApplication.dispatchOnMainThread(func() {
|
|
||||||
getOrigin(w.window, nil, nil)
|
|
||||||
wg.Done()
|
|
||||||
})
|
|
||||||
wg.Wait()
|
|
||||||
return x, y
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) destroy() {
|
|
||||||
var close func(uintptr)
|
|
||||||
purego.RegisterLibFunc(&close, gtk, "gtk_window_close")
|
|
||||||
go globalApplication.dispatchOnMainThread(func() {
|
|
||||||
close(w.window)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) setHTML(html string) {
|
|
||||||
var loadHTML func(uintptr, string, string, *string)
|
|
||||||
purego.RegisterLibFunc(&loadHTML, webkit, "webkit_web_view_load_alternate_html")
|
|
||||||
go globalApplication.dispatchOnMainThread(func() {
|
|
||||||
loadHTML(w.webview, html, "wails://", nil)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) isNormal() bool {
|
|
||||||
return !w.isMinimised() && !w.isMaximised() && !w.isFullscreen()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) isVisible() bool {
|
|
||||||
var isVisible func(uintptr) bool
|
|
||||||
purego.RegisterLibFunc(&isVisible, gtk, "gtk_widget_is_visible")
|
|
||||||
return isVisible(w.window)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *linuxWebviewWindow) nativeWindowHandle() uintptr {
|
|
||||||
return w.window
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user