mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 19:31:20 +08:00
Fix up dev. Pull in go-webview and winc
This commit is contained in:
parent
c78ee7e49b
commit
a5bf76b30f
@ -73,6 +73,7 @@ type devFlags struct {
|
||||
debounceMS int
|
||||
devServerURL string
|
||||
appargs string
|
||||
saveConfig bool
|
||||
}
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
@ -96,6 +97,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
command.IntFlag("debounce", "The amount of time to wait to trigger a reload on change", &flags.debounceMS)
|
||||
command.StringFlag("devserverurl", "The url of the dev server to use", &flags.devServerURL)
|
||||
command.StringFlag("appargs", "arguments to pass to the underlying app (quoted and space searated)", &flags.appargs)
|
||||
command.BoolFlag("save", "Save given flags as defaults", &flags.saveConfig)
|
||||
|
||||
command.Action(func() error {
|
||||
// Create logger
|
||||
@ -294,15 +296,12 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var shouldSaveConfig bool
|
||||
|
||||
if flags.assetDir == "" && projectConfig.AssetDirectory != "" {
|
||||
flags.assetDir = projectConfig.AssetDirectory
|
||||
}
|
||||
|
||||
if flags.assetDir != projectConfig.AssetDirectory {
|
||||
projectConfig.AssetDirectory = filepath.ToSlash(flags.assetDir)
|
||||
shouldSaveConfig = true
|
||||
}
|
||||
|
||||
if flags.assetDir != "" {
|
||||
@ -318,7 +317,6 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e
|
||||
|
||||
if flags.reloadDirs != projectConfig.ReloadDirectories {
|
||||
projectConfig.ReloadDirectories = filepath.ToSlash(flags.reloadDirs)
|
||||
shouldSaveConfig = true
|
||||
}
|
||||
|
||||
if flags.devServerURL == defaultDevServerURL && projectConfig.DevServerURL != defaultDevServerURL && projectConfig.DevServerURL != "" {
|
||||
@ -327,7 +325,6 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e
|
||||
|
||||
if flags.devServerURL != projectConfig.DevServerURL {
|
||||
projectConfig.DevServerURL = flags.devServerURL
|
||||
shouldSaveConfig = true
|
||||
}
|
||||
|
||||
if flags.wailsjsdir == "" && projectConfig.WailsJSDir != "" {
|
||||
@ -340,7 +337,6 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e
|
||||
|
||||
if flags.wailsjsdir != projectConfig.WailsJSDir {
|
||||
projectConfig.WailsJSDir = filepath.ToSlash(flags.wailsjsdir)
|
||||
shouldSaveConfig = true
|
||||
}
|
||||
|
||||
if flags.debounceMS == 100 && projectConfig.DebounceMS != 100 {
|
||||
@ -352,14 +348,13 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e
|
||||
|
||||
if flags.debounceMS != projectConfig.DebounceMS {
|
||||
projectConfig.DebounceMS = flags.debounceMS
|
||||
shouldSaveConfig = true
|
||||
}
|
||||
|
||||
if flags.appargs == "" && projectConfig.AppArgs != "" {
|
||||
flags.appargs = projectConfig.AppArgs
|
||||
}
|
||||
|
||||
if shouldSaveConfig {
|
||||
if flags.saveConfig {
|
||||
err = projectConfig.Save()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -584,7 +579,7 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
|
||||
LogRed("Error during build: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
// If we have a new process, save it
|
||||
// If we have a new process, saveConfig it
|
||||
if newBinaryProcess != nil {
|
||||
debugBinaryProcess = newBinaryProcess
|
||||
}
|
||||
|
@ -82,16 +82,12 @@ github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQ
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 h1:nK/JTPyJi5QRqYjVZjXgtN4/dhg2qtngoLxLDVn429k=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM=
|
||||
github.com/leaanthony/gosod v1.0.2/go.mod h1:W8RyeSFBXu7RpIxPGEJfW4moSyGGEjlJMLV25wEbAdU=
|
||||
github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA=
|
||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0=
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js=
|
||||
github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 h1:5iOd93PZbpH4Iir8QkC4coFD+zEQEZSIRcjwjTFZkr0=
|
||||
github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18/go.mod h1:KEbMsKoznsebyGHwLk5LqkFOxL5uXSRdvpP4+avmAMs=
|
||||
github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
|
@ -1,37 +1,37 @@
|
||||
module changeme
|
||||
|
||||
go 1.17
|
||||
go 1.17
|
||||
|
||||
require github.com/wailsapp/wails/v2 {{.WailsVersion}}
|
||||
require github.com/wailsapp/wails/v2 {{.WailsVersion}}
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.3.1 // indirect
|
||||
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||
github.com/gofiber/fiber/v2 v2.17.0 // indirect
|
||||
github.com/gofiber/websocket/v2 v2.0.8 // indirect
|
||||
github.com/google/uuid v1.1.2 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 // indirect
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect
|
||||
github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect
|
||||
github.com/tkrajina/go-reflector v0.5.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.28.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
|
||||
)
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.3.1 // indirect
|
||||
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||
github.com/gofiber/fiber/v2 v2.17.0 // indirect
|
||||
github.com/gofiber/websocket/v2 v2.0.8 // indirect
|
||||
github.com/google/uuid v1.1.2 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 // indirect
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect
|
||||
github.com/tkrajina/go-reflector v0.5.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.28.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
|
||||
)
|
||||
|
||||
// replace github.com/wailsapp/wails/v2 {{.WailsVersion}} => {{.WailsDirectory}}
|
||||
// replace github.com/wailsapp/wails/v2 {{.WailsVersion}} => {{.WailsDirectory}}
|
@ -19,10 +19,10 @@ github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect
|
||||
github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect
|
||||
|
@ -83,16 +83,12 @@ github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQ
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd h1:6m4zZ/esiByaDbzgdvDxjsOaIDgtuG1q2cyhjAi6uAg=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA=
|
||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0=
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js=
|
||||
github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 h1:5iOd93PZbpH4Iir8QkC4coFD+zEQEZSIRcjwjTFZkr0=
|
||||
github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18/go.mod h1:KEbMsKoznsebyGHwLk5LqkFOxL5uXSRdvpP4+avmAMs=
|
||||
github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
|
@ -19,10 +19,10 @@ github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/leaanthony/debme v1.2.1 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect
|
||||
github.com/leaanthony/slicer v1.5.0 // indirect
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect
|
||||
github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect
|
||||
github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect
|
||||
|
@ -83,16 +83,12 @@ github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQ
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd h1:6m4zZ/esiByaDbzgdvDxjsOaIDgtuG1q2cyhjAi6uAg=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA=
|
||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0=
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js=
|
||||
github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 h1:5iOd93PZbpH4Iir8QkC4coFD+zEQEZSIRcjwjTFZkr0=
|
||||
github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18/go.mod h1:KEbMsKoznsebyGHwLk5LqkFOxL5uXSRdvpP4+avmAMs=
|
||||
github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
|
@ -18,16 +18,15 @@ require (
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/jackmordaunt/icns v1.0.0
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e
|
||||
github.com/leaanthony/clir v1.0.4
|
||||
github.com/leaanthony/debme v1.2.1
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3
|
||||
github.com/leaanthony/go-webview2 v1.0.3-0.20220314105146-f44268990abe
|
||||
github.com/leaanthony/gosod v1.0.3
|
||||
github.com/leaanthony/idgen v1.0.0
|
||||
github.com/leaanthony/slicer v1.5.0
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7
|
||||
github.com/leaanthony/winc v0.0.0-20220323084916-ea5df694ec1f
|
||||
github.com/leaanthony/winicon v1.0.0
|
||||
github.com/matryer/is v1.4.0
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
@ -56,7 +55,6 @@ require (
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
|
@ -88,7 +88,6 @@ github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmA
|
||||
github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
@ -118,8 +117,6 @@ github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQ
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/go-webview2 v1.0.3-0.20220314105146-f44268990abe h1:8MRHsDSWiVHE5FIwyXKBTtdOGMQEOfmPNF1nGcbx3iY=
|
||||
github.com/leaanthony/go-webview2 v1.0.3-0.20220314105146-f44268990abe/go.mod h1:iX54IaVk1FnDqMuHJ47VYLPQOcVqQiOe9SJACt9CAbU=
|
||||
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/idgen v1.0.0 h1:IZreR+JGEzFV4yeVuBZA25gM0keUoFy+RDUldncQ+Jw=
|
||||
@ -128,10 +125,6 @@ github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0H
|
||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0=
|
||||
github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js=
|
||||
github.com/leaanthony/winc v0.0.0-20220208061147-37b059b9dc3b h1:cJ+VfVwX3GkRGSy0SiOyZ7FjSGMPAY/rS/wJzilo23I=
|
||||
github.com/leaanthony/winc v0.0.0-20220208061147-37b059b9dc3b/go.mod h1:OPfk8SNMAKRcSv8Vw1QL0yupmwcRtJyXZUgtMoaHUGc=
|
||||
github.com/leaanthony/winc v0.0.0-20220323084916-ea5df694ec1f h1:RM0TNQXGTt06ZrSysdo+r9E9fk1ObACFBOww+W1zOiU=
|
||||
github.com/leaanthony/winc v0.0.0-20220323084916-ea5df694ec1f/go.mod h1:OPfk8SNMAKRcSv8Vw1QL0yupmwcRtJyXZUgtMoaHUGc=
|
||||
github.com/leaanthony/winicon v1.0.0 h1:ZNt5U5dY71oEoKZ97UVwJRT4e+5xo5o/ieKuHuk8NqQ=
|
||||
github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
@ -253,7 +246,6 @@ golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210218145245-beda7e5e158e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -98,7 +98,7 @@ func generateBindings(bindings *binding.Bindings) error {
|
||||
|
||||
// Write backend method wrappers
|
||||
bindingsFilename := filepath.Join(targetDir, "bindings.js")
|
||||
err = bindings.GenerateBackendJS(bindingsFilename, true)
|
||||
err = bindings.GenerateBackendJS(bindingsFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ package appng
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/leaanthony/winc/w32"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
|
@ -211,7 +211,7 @@ func generateBindings(bindings *binding.Bindings) error {
|
||||
|
||||
// Write backend method wrappers
|
||||
bindingsFilename := filepath.Join(targetDir, "bindings.js")
|
||||
err = bindings.GenerateBackendJS(bindingsFilename, false)
|
||||
err = bindings.GenerateBackendJS(bindingsFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
//go:embed assets/package.json
|
||||
var packageJSON []byte
|
||||
|
||||
func (b *Bindings) GenerateBackendJS(targetfile string, isDevBindings bool) error {
|
||||
func (b *Bindings) GenerateBackendJS(targetfile string) error {
|
||||
|
||||
store := b.db.store
|
||||
var output bytes.Buffer
|
||||
@ -26,15 +26,6 @@ func (b *Bindings) GenerateBackendJS(targetfile string, isDevBindings bool) erro
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
`)
|
||||
|
||||
if isDevBindings {
|
||||
json, err := b.ToJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
output.WriteString("window.wailsbindings = " + json + ";")
|
||||
output.WriteString("\n")
|
||||
}
|
||||
|
||||
output.WriteString(`const go = {`)
|
||||
output.WriteString("\n")
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package wv2runtime
|
||||
|
||||
import (
|
||||
"github.com/leaanthony/go-webview2/webviewloader"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webviewloader"
|
||||
)
|
||||
|
||||
const MinimumRuntimeVersion string = "91.0.992.28"
|
||||
|
@ -14,13 +14,13 @@ import (
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/leaanthony/go-webview2/pkg/edge"
|
||||
"github.com/leaanthony/winc"
|
||||
"github.com/leaanthony/winc/w32"
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/common"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
22
v2/internal/frontend/desktop/windows/go-webview2/LICENSE
Normal file
22
v2/internal/frontend/desktop/windows/go-webview2/LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 John Chadwick
|
||||
Some portions Copyright (c) 2017 Serge Zaitsev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
27
v2/internal/frontend/desktop/windows/go-webview2/README.md
Normal file
27
v2/internal/frontend/desktop/windows/go-webview2/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# go-webview2
|
||||
|
||||
This is a proof of concept for embedding Webview2 into Go without CGo. It is based
|
||||
on [webview/webview](https://github.com/webview/webview) and provides a compatible API.
|
||||
|
||||
## Notice
|
||||
|
||||
Because this version doesn't currently have an EdgeHTML fallback, it will not work unless you have a Webview2 runtime
|
||||
installed. In addition, it requires the Webview2Loader DLL in order to function. Adding an EdgeHTML fallback should be
|
||||
technically possible but will likely require much worse hacks since the API is not strictly COM to my knowledge.
|
||||
|
||||
## Demo
|
||||
|
||||
For now, you'll need to install the Webview2 runtime, as it does not ship with Windows.
|
||||
|
||||
[WebView2 runtime](https://developer.microsoft.com/en-us/microsoft-edge/webview2/)
|
||||
|
||||
After that, you should be able to run go-webview2 directly:
|
||||
|
||||
```
|
||||
go run github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/cmd/demo
|
||||
```
|
||||
|
||||
This will use go-winloader to load an embedded copy of WebView2Loader.dll.
|
||||
|
||||
If this does not work, please try running from a directory that has an appropriate copy of `WebView2Loader.dll` for your
|
||||
GOARCH. If _that_ worked, *please* file a bug so we can figure out what's wrong with go-winloader :)
|
@ -0,0 +1,154 @@
|
||||
package w32
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
ole32 = windows.NewLazySystemDLL("ole32")
|
||||
Ole32CoInitializeEx = ole32.NewProc("CoInitializeEx")
|
||||
|
||||
kernel32 = windows.NewLazySystemDLL("kernel32")
|
||||
Kernel32GetCurrentThreadID = kernel32.NewProc("GetCurrentThreadId")
|
||||
|
||||
shlwapi = windows.NewLazySystemDLL("shlwapi")
|
||||
shlwapiSHCreateMemStream = shlwapi.NewProc("SHCreateMemStream")
|
||||
|
||||
user32 = windows.NewLazySystemDLL("user32")
|
||||
User32LoadImageW = user32.NewProc("LoadImageW")
|
||||
User32GetSystemMetrics = user32.NewProc("GetSystemMetrics")
|
||||
User32RegisterClassExW = user32.NewProc("RegisterClassExW")
|
||||
User32CreateWindowExW = user32.NewProc("CreateWindowExW")
|
||||
User32DestroyWindow = user32.NewProc("DestroyWindow")
|
||||
User32ShowWindow = user32.NewProc("ShowWindow")
|
||||
User32UpdateWindow = user32.NewProc("UpdateWindow")
|
||||
User32SetFocus = user32.NewProc("SetFocus")
|
||||
User32GetMessageW = user32.NewProc("GetMessageW")
|
||||
User32TranslateMessage = user32.NewProc("TranslateMessage")
|
||||
User32DispatchMessageW = user32.NewProc("DispatchMessageW")
|
||||
User32DefWindowProcW = user32.NewProc("DefWindowProcW")
|
||||
User32GetClientRect = user32.NewProc("GetClientRect")
|
||||
User32PostQuitMessage = user32.NewProc("PostQuitMessage")
|
||||
User32SetWindowTextW = user32.NewProc("SetWindowTextW")
|
||||
User32PostThreadMessageW = user32.NewProc("PostThreadMessageW")
|
||||
User32GetWindowLongPtrW = user32.NewProc("GetWindowLongPtrW")
|
||||
User32SetWindowLongPtrW = user32.NewProc("SetWindowLongPtrW")
|
||||
User32AdjustWindowRect = user32.NewProc("AdjustWindowRect")
|
||||
User32SetWindowPos = user32.NewProc("SetWindowPos")
|
||||
)
|
||||
|
||||
const (
|
||||
SystemMetricsCxIcon = 11
|
||||
SystemMetricsCyIcon = 12
|
||||
)
|
||||
|
||||
const (
|
||||
SWShow = 5
|
||||
)
|
||||
|
||||
const (
|
||||
SWPNoZOrder = 0x0004
|
||||
SWPNoActivate = 0x0010
|
||||
SWPNoMove = 0x0002
|
||||
SWPFrameChanged = 0x0020
|
||||
)
|
||||
|
||||
const (
|
||||
WMDestroy = 0x0002
|
||||
WMMove = 0x0003
|
||||
WMSize = 0x0005
|
||||
WMClose = 0x0010
|
||||
WMQuit = 0x0012
|
||||
WMGetMinMaxInfo = 0x0024
|
||||
WMNCLButtonDown = 0x00A1
|
||||
WMMoving = 0x0216
|
||||
WMApp = 0x8000
|
||||
)
|
||||
|
||||
const (
|
||||
GWLStyle = -16
|
||||
)
|
||||
|
||||
const (
|
||||
WSOverlapped = 0x00000000
|
||||
WSMaximizeBox = 0x00020000
|
||||
WSThickFrame = 0x00040000
|
||||
WSCaption = 0x00C00000
|
||||
WSSysMenu = 0x00080000
|
||||
WSMinimizeBox = 0x00020000
|
||||
WSOverlappedWindow = (WSOverlapped | WSCaption | WSSysMenu | WSThickFrame | WSMinimizeBox | WSMaximizeBox)
|
||||
)
|
||||
|
||||
type WndClassExW struct {
|
||||
CbSize uint32
|
||||
Style uint32
|
||||
LpfnWndProc uintptr
|
||||
CnClsExtra int32
|
||||
CbWndExtra int32
|
||||
HInstance windows.Handle
|
||||
HIcon windows.Handle
|
||||
HCursor windows.Handle
|
||||
HbrBackground windows.Handle
|
||||
LpszMenuName *uint16
|
||||
LpszClassName *uint16
|
||||
HIconSm windows.Handle
|
||||
}
|
||||
|
||||
type Rect struct {
|
||||
Left int32
|
||||
Top int32
|
||||
Right int32
|
||||
Bottom int32
|
||||
}
|
||||
|
||||
type MinMaxInfo struct {
|
||||
PtReserved Point
|
||||
PtMaxSize Point
|
||||
PtMaxPosition Point
|
||||
PtMinTrackSize Point
|
||||
PtMaxTrackSize Point
|
||||
}
|
||||
|
||||
type Point struct {
|
||||
X, Y int32
|
||||
}
|
||||
|
||||
type Msg struct {
|
||||
Hwnd syscall.Handle
|
||||
Message uint32
|
||||
WParam uintptr
|
||||
LParam uintptr
|
||||
Time uint32
|
||||
Pt Point
|
||||
LPrivate uint32
|
||||
}
|
||||
|
||||
func Utf16PtrToString(p *uint16) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
// Find NUL terminator.
|
||||
end := unsafe.Pointer(p)
|
||||
n := 0
|
||||
for *(*uint16)(end) != 0 {
|
||||
end = unsafe.Pointer(uintptr(end) + unsafe.Sizeof(*p))
|
||||
n++
|
||||
}
|
||||
s := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:n:n]
|
||||
return string(utf16.Decode(s))
|
||||
}
|
||||
|
||||
func SHCreateMemStream(data []byte) (uintptr, error) {
|
||||
ret, _, err := shlwapiSHCreateMemStream.Call(
|
||||
uintptr(unsafe.Pointer(&data[0])),
|
||||
uintptr(len(data)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package edge
|
||||
|
||||
type COREWEBVIEW2_COLOR struct {
|
||||
A uint8
|
||||
R uint8
|
||||
G uint8
|
||||
B uint8
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package edge
|
||||
|
||||
type COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND uint32
|
||||
|
||||
const (
|
||||
COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY = iota
|
||||
COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_ALLOW
|
||||
COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package edge
|
||||
|
||||
type COREWEBVIEW2_KEY_EVENT_KIND uint32
|
||||
|
||||
const (
|
||||
COREWEBVIEW2_KEY_EVENT_KIND_KEY_DOWN = 0
|
||||
COREWEBVIEW2_KEY_EVENT_KIND_KEY_UP = 1
|
||||
COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_DOWN = 2
|
||||
COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_UP = 3
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package edge
|
||||
|
||||
type COREWEBVIEW2_MOVE_FOCUS_REASON uint32
|
||||
|
||||
const (
|
||||
COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC = 0
|
||||
COREWEBVIEW2_MOVE_FOCUS_REASON_NEXT = 1
|
||||
COREWEBVIEW2_MOVE_FOCUS_REASON_PREVIOUS = 2
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package edge
|
||||
|
||||
type COREWEBVIEW2_PHYSICAL_KEY_STATUS struct {
|
||||
RepeatCount uint32
|
||||
ScanCode uint32
|
||||
IsExtendedKey bool
|
||||
IsMenuKeyDown bool
|
||||
WasKeyDown bool
|
||||
IsKeyReleased bool
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package edge
|
||||
|
||||
type COREWEBVIEW2_WEB_RESOURCE_CONTEXT uint32
|
||||
|
||||
const (
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL = 0
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT = 1
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_STYLESHEET = 2
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_IMAGE = 3
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_MEDIA = 4
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_FONT = 5
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_SCRIPT = 6
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_XML_HTTP_REQUEST = 7
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_FETCH = 8
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_TEXT_TRACK = 9
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_EVENT_SOURCE = 10
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_WEBSOCKET = 11
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_MANIFEST = 12
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_SIGNED_EXCHANGE = 13
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_PING = 14
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_CSP_VIOLATION_REPORT = 15
|
||||
COREWEBVIEW2_WEB_RESOURCE_CONTEXT_OTHER = 16
|
||||
)
|
@ -0,0 +1,76 @@
|
||||
package edge
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type _ICoreWebView2AcceleratorKeyPressedEventArgsVtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetKeyEventKind ComProc
|
||||
GetVirtualKey ComProc
|
||||
GetKeyEventLParam ComProc
|
||||
GetPhysicalKeyStatus ComProc
|
||||
GetHandled ComProc
|
||||
PutHandled ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2AcceleratorKeyPressedEventArgs struct {
|
||||
vtbl *_ICoreWebView2AcceleratorKeyPressedEventArgsVtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetKeyEventKind() (COREWEBVIEW2_KEY_EVENT_KIND, error) {
|
||||
var err error
|
||||
var keyEventKind COREWEBVIEW2_KEY_EVENT_KIND
|
||||
_, _, err = i.vtbl.GetKeyEventKind.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&keyEventKind)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return 0, err
|
||||
}
|
||||
return keyEventKind, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetVirtualKey() (uint, error) {
|
||||
var err error
|
||||
var virtualKey uint
|
||||
_, _, err = i.vtbl.GetVirtualKey.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&virtualKey)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return 0, err
|
||||
}
|
||||
return virtualKey, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetPhysicalKeyStatus() (COREWEBVIEW2_PHYSICAL_KEY_STATUS, error) {
|
||||
var err error
|
||||
var physicalKeyStatus COREWEBVIEW2_PHYSICAL_KEY_STATUS
|
||||
_, _, err = i.vtbl.GetPhysicalKeyStatus.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&physicalKeyStatus)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return COREWEBVIEW2_PHYSICAL_KEY_STATUS{}, err
|
||||
}
|
||||
return physicalKeyStatus, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) PutHandled(handled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutHandled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(handled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package edge
|
||||
|
||||
type _ICoreWebView2AcceleratorKeyPressedEventHandlerVtbl struct {
|
||||
_IUnknownVtbl
|
||||
Invoke ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2AcceleratorKeyPressedEventHandler struct {
|
||||
vtbl *_ICoreWebView2AcceleratorKeyPressedEventHandlerVtbl
|
||||
impl _ICoreWebView2AcceleratorKeyPressedEventHandlerImpl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2AcceleratorKeyPressedEventHandler) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
func _ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownQueryInterface(this *ICoreWebView2AcceleratorKeyPressedEventHandler, refiid, object uintptr) uintptr {
|
||||
return this.impl.QueryInterface(refiid, object)
|
||||
}
|
||||
|
||||
func _ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownAddRef(this *ICoreWebView2AcceleratorKeyPressedEventHandler) uintptr {
|
||||
return this.impl.AddRef()
|
||||
}
|
||||
|
||||
func _ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownRelease(this *ICoreWebView2AcceleratorKeyPressedEventHandler) uintptr {
|
||||
return this.impl.Release()
|
||||
}
|
||||
|
||||
func _ICoreWebView2AcceleratorKeyPressedEventHandlerInvoke(this *ICoreWebView2AcceleratorKeyPressedEventHandler, sender *ICoreWebView2Controller, args *ICoreWebView2AcceleratorKeyPressedEventArgs) uintptr {
|
||||
return this.impl.AcceleratorKeyPressed(sender, args)
|
||||
}
|
||||
|
||||
type _ICoreWebView2AcceleratorKeyPressedEventHandlerImpl interface {
|
||||
_IUnknownImpl
|
||||
AcceleratorKeyPressed(sender *ICoreWebView2Controller, args *ICoreWebView2AcceleratorKeyPressedEventArgs) uintptr
|
||||
}
|
||||
|
||||
var _ICoreWebView2AcceleratorKeyPressedEventHandlerFn = _ICoreWebView2AcceleratorKeyPressedEventHandlerVtbl{
|
||||
_IUnknownVtbl{
|
||||
NewComProc(_ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownQueryInterface),
|
||||
NewComProc(_ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownAddRef),
|
||||
NewComProc(_ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownRelease),
|
||||
},
|
||||
NewComProc(_ICoreWebView2AcceleratorKeyPressedEventHandlerInvoke),
|
||||
}
|
||||
|
||||
func newICoreWebView2AcceleratorKeyPressedEventHandler(impl _ICoreWebView2AcceleratorKeyPressedEventHandlerImpl) *ICoreWebView2AcceleratorKeyPressedEventHandler {
|
||||
return &ICoreWebView2AcceleratorKeyPressedEventHandler{
|
||||
vtbl: &_ICoreWebView2AcceleratorKeyPressedEventHandlerFn,
|
||||
impl: impl,
|
||||
}
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
package edge
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type _ICoreWebView2ControllerVtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetIsVisible ComProc
|
||||
PutIsVisible ComProc
|
||||
GetBounds ComProc
|
||||
PutBounds ComProc
|
||||
GetZoomFactor ComProc
|
||||
PutZoomFactor ComProc
|
||||
AddZoomFactorChanged ComProc
|
||||
RemoveZoomFactorChanged ComProc
|
||||
SetBoundsAndZoomFactor ComProc
|
||||
MoveFocus ComProc
|
||||
AddMoveFocusRequested ComProc
|
||||
RemoveMoveFocusRequested ComProc
|
||||
AddGotFocus ComProc
|
||||
RemoveGotFocus ComProc
|
||||
AddLostFocus ComProc
|
||||
RemoveLostFocus ComProc
|
||||
AddAcceleratorKeyPressed ComProc
|
||||
RemoveAcceleratorKeyPressed ComProc
|
||||
GetParentWindow ComProc
|
||||
PutParentWindow ComProc
|
||||
NotifyParentWindowPositionChanged ComProc
|
||||
Close ComProc
|
||||
GetCoreWebView2 ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2Controller struct {
|
||||
vtbl *_ICoreWebView2ControllerVtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller) GetBounds() (*w32.Rect, error) {
|
||||
var err error
|
||||
var bounds w32.Rect
|
||||
_, _, err = i.vtbl.GetBounds.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&bounds)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return nil, err
|
||||
}
|
||||
return &bounds, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller) PutBounds(bounds w32.Rect) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutBounds.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&bounds)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller) MoveFocus(reason COREWEBVIEW2_MOVE_FOCUS_REASON) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.MoveFocus.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(reason),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller) AddAcceleratorKeyPressed(eventHandler *ICoreWebView2AcceleratorKeyPressedEventHandler, token *_EventRegistrationToken) error {
|
||||
var err error
|
||||
_, _, err = i.vtbl.AddAcceleratorKeyPressed.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(eventHandler)),
|
||||
uintptr(unsafe.Pointer(&token)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller) PutIsVisible(isVisible bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsVisible.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(isVisible)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller) GetICoreWebView2Controller2() *ICoreWebView2Controller2 {
|
||||
|
||||
var result *ICoreWebView2Controller2
|
||||
|
||||
iidICoreWebView2Controller2 := NewGUID("{c979903e-d4ca-4228-92eb-47ee3fa96eab}")
|
||||
i.vtbl.QueryInterface.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(iidICoreWebView2Controller2)),
|
||||
uintptr(unsafe.Pointer(&result)))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller) NotifyParentWindowPositionChanged() error {
|
||||
var err error
|
||||
_, _, err = i.vtbl.NotifyParentWindowPositionChanged.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package edge
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type _ICoreWebView2Controller2Vtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetIsVisible ComProc
|
||||
PutIsVisible ComProc
|
||||
GetBounds ComProc
|
||||
PutBounds ComProc
|
||||
GetZoomFactor ComProc
|
||||
PutZoomFactor ComProc
|
||||
AddZoomFactorChanged ComProc
|
||||
RemoveZoomFactorChanged ComProc
|
||||
SetBoundsAndZoomFactor ComProc
|
||||
MoveFocus ComProc
|
||||
AddMoveFocusRequested ComProc
|
||||
RemoveMoveFocusRequested ComProc
|
||||
AddGotFocus ComProc
|
||||
RemoveGotFocus ComProc
|
||||
AddLostFocus ComProc
|
||||
RemoveLostFocus ComProc
|
||||
AddAcceleratorKeyPressed ComProc
|
||||
RemoveAcceleratorKeyPressed ComProc
|
||||
GetParentWindow ComProc
|
||||
PutParentWindow ComProc
|
||||
NotifyParentWindowPositionChanged ComProc
|
||||
Close ComProc
|
||||
GetCoreWebView2 ComProc
|
||||
GetDefaultBackgroundColor ComProc
|
||||
PutDefaultBackgroundColor ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2Controller2 struct {
|
||||
vtbl *_ICoreWebView2Controller2Vtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller2) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller2) GetDefaultBackgroundColor() (*COREWEBVIEW2_COLOR, error) {
|
||||
var err error
|
||||
var backgroundColor *COREWEBVIEW2_COLOR
|
||||
_, _, err = i.vtbl.GetDefaultBackgroundColor.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&backgroundColor)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return nil, err
|
||||
}
|
||||
return backgroundColor, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Controller2) PutDefaultBackgroundColor(backgroundColor COREWEBVIEW2_COLOR) error {
|
||||
var err error
|
||||
|
||||
// Cast to a uint32 as that's what the call is expecting
|
||||
col := *(*uint32)(unsafe.Pointer(&backgroundColor))
|
||||
|
||||
_, _, err = i.vtbl.PutDefaultBackgroundColor.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(col),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package edge
|
||||
|
||||
type _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl struct {
|
||||
_IUnknownVtbl
|
||||
Invoke ComProc
|
||||
}
|
||||
|
||||
type iCoreWebView2CreateCoreWebView2ControllerCompletedHandler struct {
|
||||
vtbl *_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl
|
||||
impl _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerImpl
|
||||
}
|
||||
|
||||
func (i *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
func _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownQueryInterface(this *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler, refiid, object uintptr) uintptr {
|
||||
return this.impl.QueryInterface(refiid, object)
|
||||
}
|
||||
|
||||
func _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownAddRef(this *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler) uintptr {
|
||||
return this.impl.AddRef()
|
||||
}
|
||||
|
||||
func _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownRelease(this *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler) uintptr {
|
||||
return this.impl.Release()
|
||||
}
|
||||
|
||||
func _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerInvoke(this *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler, errorCode uintptr, createdController *ICoreWebView2Controller) uintptr {
|
||||
return this.impl.CreateCoreWebView2ControllerCompleted(errorCode, createdController)
|
||||
}
|
||||
|
||||
type _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerImpl interface {
|
||||
_IUnknownImpl
|
||||
CreateCoreWebView2ControllerCompleted(errorCode uintptr, createdController *ICoreWebView2Controller) uintptr
|
||||
}
|
||||
|
||||
var _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerFn = _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl{
|
||||
_IUnknownVtbl{
|
||||
NewComProc(_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownQueryInterface),
|
||||
NewComProc(_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownAddRef),
|
||||
NewComProc(_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownRelease),
|
||||
},
|
||||
NewComProc(_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerInvoke),
|
||||
}
|
||||
|
||||
func newICoreWebView2CreateCoreWebView2ControllerCompletedHandler(impl _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerImpl) *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler {
|
||||
return &iCoreWebView2CreateCoreWebView2ControllerCompletedHandler{
|
||||
vtbl: &_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerFn,
|
||||
impl: impl,
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package edge
|
||||
|
||||
type _ICoreWebView2NavigationCompletedEventArgsVtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetIsSuccess ComProc
|
||||
GetWebErrorStatus ComProc
|
||||
GetNavigationId ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2NavigationCompletedEventArgs struct {
|
||||
vtbl *_ICoreWebView2NavigationCompletedEventArgsVtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2NavigationCompletedEventArgs) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package edge
|
||||
|
||||
type _ICoreWebView2NavigationCompletedEventHandlerVtbl struct {
|
||||
_IUnknownVtbl
|
||||
Invoke ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2NavigationCompletedEventHandler struct {
|
||||
vtbl *_ICoreWebView2NavigationCompletedEventHandlerVtbl
|
||||
impl _ICoreWebView2NavigationCompletedEventHandlerImpl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2NavigationCompletedEventHandler) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
func _ICoreWebView2NavigationCompletedEventHandlerIUnknownQueryInterface(this *ICoreWebView2NavigationCompletedEventHandler, refiid, object uintptr) uintptr {
|
||||
return this.impl.QueryInterface(refiid, object)
|
||||
}
|
||||
|
||||
func _ICoreWebView2NavigationCompletedEventHandlerIUnknownAddRef(this *ICoreWebView2NavigationCompletedEventHandler) uintptr {
|
||||
return this.impl.AddRef()
|
||||
}
|
||||
|
||||
func _ICoreWebView2NavigationCompletedEventHandlerIUnknownRelease(this *ICoreWebView2NavigationCompletedEventHandler) uintptr {
|
||||
return this.impl.Release()
|
||||
}
|
||||
|
||||
func _ICoreWebView2NavigationCompletedEventHandlerInvoke(this *ICoreWebView2NavigationCompletedEventHandler, sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) uintptr {
|
||||
return this.impl.NavigationCompleted(sender, args)
|
||||
}
|
||||
|
||||
type _ICoreWebView2NavigationCompletedEventHandlerImpl interface {
|
||||
_IUnknownImpl
|
||||
NavigationCompleted(sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) uintptr
|
||||
}
|
||||
|
||||
var _ICoreWebView2NavigationCompletedEventHandlerFn = _ICoreWebView2NavigationCompletedEventHandlerVtbl{
|
||||
_IUnknownVtbl{
|
||||
NewComProc(_ICoreWebView2NavigationCompletedEventHandlerIUnknownQueryInterface),
|
||||
NewComProc(_ICoreWebView2NavigationCompletedEventHandlerIUnknownAddRef),
|
||||
NewComProc(_ICoreWebView2NavigationCompletedEventHandlerIUnknownRelease),
|
||||
},
|
||||
NewComProc(_ICoreWebView2NavigationCompletedEventHandlerInvoke),
|
||||
}
|
||||
|
||||
func newICoreWebView2NavigationCompletedEventHandler(impl _ICoreWebView2NavigationCompletedEventHandlerImpl) *ICoreWebView2NavigationCompletedEventHandler {
|
||||
return &ICoreWebView2NavigationCompletedEventHandler{
|
||||
vtbl: &_ICoreWebView2NavigationCompletedEventHandlerFn,
|
||||
impl: impl,
|
||||
}
|
||||
}
|
@ -0,0 +1,268 @@
|
||||
package edge
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type _ICoreWebView2SettingsVtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetIsScriptEnabled ComProc
|
||||
PutIsScriptEnabled ComProc
|
||||
GetIsWebMessageEnabled ComProc
|
||||
PutIsWebMessageEnabled ComProc
|
||||
GetAreDefaultScriptDialogsEnabled ComProc
|
||||
PutAreDefaultScriptDialogsEnabled ComProc
|
||||
GetIsStatusBarEnabled ComProc
|
||||
PutIsStatusBarEnabled ComProc
|
||||
GetAreDevToolsEnabled ComProc
|
||||
PutAreDevToolsEnabled ComProc
|
||||
GetAreDefaultContextMenusEnabled ComProc
|
||||
PutAreDefaultContextMenusEnabled ComProc
|
||||
GetAreHostObjectsAllowed ComProc
|
||||
PutAreHostObjectsAllowed ComProc
|
||||
GetIsZoomControlEnabled ComProc
|
||||
PutIsZoomControlEnabled ComProc
|
||||
GetIsBuiltInErrorPageEnabled ComProc
|
||||
PutIsBuiltInErrorPageEnabled ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2Settings struct {
|
||||
vtbl *_ICoreWebView2SettingsVtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) GetIsScriptEnabled() (bool, error) {
|
||||
var err error
|
||||
var isScriptEnabled bool
|
||||
_, _, err = i.vtbl.GetIsScriptEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&isScriptEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return isScriptEnabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) PutIsScriptEnabled(isScriptEnabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsScriptEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(isScriptEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) GetIsWebMessageEnabled() (bool, error) {
|
||||
var err error
|
||||
var isWebMessageEnabled bool
|
||||
_, _, err = i.vtbl.GetIsWebMessageEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&isWebMessageEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return isWebMessageEnabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) PutIsWebMessageEnabled(isWebMessageEnabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsWebMessageEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(isWebMessageEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) GetAreDefaultScriptDialogsEnabled() (bool, error) {
|
||||
var err error
|
||||
var areDefaultScriptDialogsEnabled bool
|
||||
_, _, err = i.vtbl.GetAreDefaultScriptDialogsEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&areDefaultScriptDialogsEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return areDefaultScriptDialogsEnabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) PutAreDefaultScriptDialogsEnabled(areDefaultScriptDialogsEnabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutAreDefaultScriptDialogsEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(areDefaultScriptDialogsEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) GetIsStatusBarEnabled() (bool, error) {
|
||||
var err error
|
||||
var isStatusBarEnabled bool
|
||||
_, _, err = i.vtbl.GetIsStatusBarEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&isStatusBarEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return isStatusBarEnabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) PutIsStatusBarEnabled(isStatusBarEnabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsStatusBarEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(isStatusBarEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) GetAreDevToolsEnabled() (bool, error) {
|
||||
var err error
|
||||
var areDevToolsEnabled bool
|
||||
_, _, err = i.vtbl.GetAreDevToolsEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&areDevToolsEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return areDevToolsEnabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) PutAreDevToolsEnabled(areDevToolsEnabled bool) error {
|
||||
var err error
|
||||
_, _, err = i.vtbl.PutAreDevToolsEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(areDevToolsEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) GetAreDefaultContextMenusEnabled() (bool, error) {
|
||||
var err error
|
||||
var enabled bool
|
||||
_, _, err = i.vtbl.GetAreDefaultContextMenusEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) PutAreDefaultContextMenusEnabled(enabled bool) error {
|
||||
var err error
|
||||
_, _, err = i.vtbl.PutAreDefaultContextMenusEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) GetAreHostObjectsAllowed() (bool, error) {
|
||||
var err error
|
||||
var allowed bool
|
||||
_, _, err = i.vtbl.GetAreHostObjectsAllowed.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&allowed)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return allowed, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) PutAreHostObjectsAllowed(allowed bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutAreHostObjectsAllowed.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(allowed)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) GetIsZoomControlEnabled() (bool, error) {
|
||||
var err error
|
||||
var enabled bool
|
||||
_, _, err = i.vtbl.GetIsZoomControlEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) PutIsZoomControlEnabled(enabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsZoomControlEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) GetIsBuiltInErrorPageEnabled() (bool, error) {
|
||||
var err error
|
||||
var enabled bool
|
||||
_, _, err = i.vtbl.GetIsBuiltInErrorPageEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2Settings) PutIsBuiltInErrorPageEnabled(enabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsBuiltInErrorPageEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package edge
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type _ICoreWebView2WebResourceRequestVtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetUri ComProc
|
||||
PutUri ComProc
|
||||
GetMethod ComProc
|
||||
PutMethod ComProc
|
||||
GetContent ComProc
|
||||
PutContent ComProc
|
||||
GetHeaders ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2WebResourceRequest struct {
|
||||
vtbl *_ICoreWebView2WebResourceRequestVtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2WebResourceRequest) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2WebResourceRequest) GetUri() (string, error) {
|
||||
var err error
|
||||
// Create *uint16 to hold result
|
||||
var _uri *uint16
|
||||
_, _, err = i.vtbl.GetUri.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&_uri)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return "", err
|
||||
} // Get result and cleanup
|
||||
uri := windows.UTF16PtrToString(_uri)
|
||||
windows.CoTaskMemFree(unsafe.Pointer(_uri))
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2WebResourceRequest) Release() error {
|
||||
return i.vtbl.CallRelease(unsafe.Pointer(i))
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package edge
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type _ICoreWebView2WebResourceRequestedEventArgsVtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetRequest ComProc
|
||||
GetResponse ComProc
|
||||
PutResponse ComProc
|
||||
GetDeferral ComProc
|
||||
GetResourceContext ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2WebResourceRequestedEventArgs struct {
|
||||
vtbl *_ICoreWebView2WebResourceRequestedEventArgsVtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2WebResourceRequestedEventArgs) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2WebResourceRequestedEventArgs) PutResponse(response *ICoreWebView2WebResourceResponse) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutResponse.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(response)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2WebResourceRequestedEventArgs) GetRequest() (*ICoreWebView2WebResourceRequest, error) {
|
||||
var err error
|
||||
var request *ICoreWebView2WebResourceRequest
|
||||
_, _, err = i.vtbl.GetRequest.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&request)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return nil, err
|
||||
}
|
||||
return request, nil
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package edge
|
||||
|
||||
type _ICoreWebView2WebResourceRequestedEventHandlerVtbl struct {
|
||||
_IUnknownVtbl
|
||||
Invoke ComProc
|
||||
}
|
||||
|
||||
type iCoreWebView2WebResourceRequestedEventHandler struct {
|
||||
vtbl *_ICoreWebView2WebResourceRequestedEventHandlerVtbl
|
||||
impl _ICoreWebView2WebResourceRequestedEventHandlerImpl
|
||||
}
|
||||
|
||||
func _ICoreWebView2WebResourceRequestedEventHandlerIUnknownQueryInterface(this *iCoreWebView2WebResourceRequestedEventHandler, refiid, object uintptr) uintptr {
|
||||
return this.impl.QueryInterface(refiid, object)
|
||||
}
|
||||
|
||||
func _ICoreWebView2WebResourceRequestedEventHandlerIUnknownAddRef(this *iCoreWebView2WebResourceRequestedEventHandler) uintptr {
|
||||
return this.impl.AddRef()
|
||||
}
|
||||
|
||||
func _ICoreWebView2WebResourceRequestedEventHandlerIUnknownRelease(this *iCoreWebView2WebResourceRequestedEventHandler) uintptr {
|
||||
return this.impl.Release()
|
||||
}
|
||||
|
||||
func _ICoreWebView2WebResourceRequestedEventHandlerInvoke(this *iCoreWebView2WebResourceRequestedEventHandler, sender *ICoreWebView2, args *ICoreWebView2WebResourceRequestedEventArgs) uintptr {
|
||||
return this.impl.WebResourceRequested(sender, args)
|
||||
}
|
||||
|
||||
type _ICoreWebView2WebResourceRequestedEventHandlerImpl interface {
|
||||
_IUnknownImpl
|
||||
WebResourceRequested(sender *ICoreWebView2, args *ICoreWebView2WebResourceRequestedEventArgs) uintptr
|
||||
}
|
||||
|
||||
var _ICoreWebView2WebResourceRequestedEventHandlerFn = _ICoreWebView2WebResourceRequestedEventHandlerVtbl{
|
||||
_IUnknownVtbl{
|
||||
NewComProc(_ICoreWebView2WebResourceRequestedEventHandlerIUnknownQueryInterface),
|
||||
NewComProc(_ICoreWebView2WebResourceRequestedEventHandlerIUnknownAddRef),
|
||||
NewComProc(_ICoreWebView2WebResourceRequestedEventHandlerIUnknownRelease),
|
||||
},
|
||||
NewComProc(_ICoreWebView2WebResourceRequestedEventHandlerInvoke),
|
||||
}
|
||||
|
||||
func newICoreWebView2WebResourceRequestedEventHandler(impl _ICoreWebView2WebResourceRequestedEventHandlerImpl) *iCoreWebView2WebResourceRequestedEventHandler {
|
||||
return &iCoreWebView2WebResourceRequestedEventHandler{
|
||||
vtbl: &_ICoreWebView2WebResourceRequestedEventHandlerFn,
|
||||
impl: impl,
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package edge
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type _ICoreWebView2WebResourceResponseVtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetContent ComProc
|
||||
PutContent ComProc
|
||||
GetHeaders ComProc
|
||||
GetStatusCode ComProc
|
||||
PutStatusCode ComProc
|
||||
GetReasonPhrase ComProc
|
||||
PutReasonPhrase ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2WebResourceResponse struct {
|
||||
vtbl *_ICoreWebView2WebResourceResponseVtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2WebResourceResponse) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2WebResourceResponse) Release() error {
|
||||
return i.vtbl.CallRelease(unsafe.Pointer(i))
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package edge
|
||||
|
||||
type iCoreWebView2_2Vtbl struct {
|
||||
iCoreWebView2Vtbl
|
||||
AddWebResourceResponseReceived ComProc
|
||||
RemoveWebResourceResponseReceived ComProc
|
||||
NavigateWithWebResourceRequest ComProc
|
||||
AddDomContentLoaded ComProc
|
||||
RemoveDomContentLoaded ComProc
|
||||
GetCookieManager ComProc
|
||||
GetEnvironment ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2_2 struct {
|
||||
vtbl *iCoreWebView2_2Vtbl
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package edge
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type iCoreWebView2_3Vtbl struct {
|
||||
iCoreWebView2_2Vtbl
|
||||
TrySuspend ComProc
|
||||
Resume ComProc
|
||||
GetIsSuspended ComProc
|
||||
SetVirtualHostNameToFolderMapping ComProc
|
||||
ClearVirtualHostNameToFolderMapping ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2_3 struct {
|
||||
vtbl *iCoreWebView2_3Vtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2_3) SetVirtualHostNameToFolderMapping(hostName, folderPath string, accessKind COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND) error {
|
||||
_hostName, err := windows.UTF16PtrFromString(hostName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_folderPath, err := windows.UTF16PtrFromString(folderPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = i.vtbl.SetVirtualHostNameToFolderMapping.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(_hostName)),
|
||||
uintptr(unsafe.Pointer(_folderPath)),
|
||||
uintptr(accessKind),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2) GetICoreWebView2_3() *ICoreWebView2_3 {
|
||||
var result *ICoreWebView2_3
|
||||
|
||||
iidICoreWebView2_3 := NewGUID("{A0D6DF20-3B92-416D-AA0C-437A9C727857}")
|
||||
_, _, _ = i.vtbl.QueryInterface.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(iidICoreWebView2_3)),
|
||||
uintptr(unsafe.Pointer(&result)))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (e *Chromium) GetICoreWebView2_3() *ICoreWebView2_3 {
|
||||
return e.webview.GetICoreWebView2_3()
|
||||
}
|
@ -0,0 +1,394 @@
|
||||
package edge
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ICoreWebviewSettings is the merged settings class
|
||||
|
||||
type _ICoreWebViewSettingsVtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetIsScriptEnabled ComProc
|
||||
PutIsScriptEnabled ComProc
|
||||
GetIsWebMessageEnabled ComProc
|
||||
PutIsWebMessageEnabled ComProc
|
||||
GetAreDefaultScriptDialogsEnabled ComProc
|
||||
PutAreDefaultScriptDialogsEnabled ComProc
|
||||
GetIsStatusBarEnabled ComProc
|
||||
PutIsStatusBarEnabled ComProc
|
||||
GetAreDevToolsEnabled ComProc
|
||||
PutAreDevToolsEnabled ComProc
|
||||
GetAreDefaultContextMenusEnabled ComProc
|
||||
PutAreDefaultContextMenusEnabled ComProc
|
||||
GetAreHostObjectsAllowed ComProc
|
||||
PutAreHostObjectsAllowed ComProc
|
||||
GetIsZoomControlEnabled ComProc
|
||||
PutIsZoomControlEnabled ComProc
|
||||
GetIsBuiltInErrorPageEnabled ComProc
|
||||
PutIsBuiltInErrorPageEnabled ComProc
|
||||
GetUserAgent ComProc
|
||||
PutUserAgent ComProc
|
||||
GetAreBrowserAcceleratorKeysEnabled ComProc
|
||||
PutAreBrowserAcceleratorKeysEnabled ComProc
|
||||
GetIsPasswordAutosaveEnabled ComProc
|
||||
PutIsPasswordAutosaveEnabled ComProc
|
||||
GetIsGeneralAutofillEnabled ComProc
|
||||
PutIsGeneralAutofillEnabled ComProc
|
||||
GetIsPinchZoomEnabled ComProc
|
||||
PutIsPinchZoomEnabled ComProc
|
||||
GetIsSwipeNavigationEnabled ComProc
|
||||
PutIsSwipeNavigationEnabled ComProc
|
||||
}
|
||||
|
||||
type ICoreWebViewSettings struct {
|
||||
vtbl *_ICoreWebViewSettingsVtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) AddRef() uintptr {
|
||||
return i.AddRef()
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetIsScriptEnabled() (bool, error) {
|
||||
var err error
|
||||
var isScriptEnabled bool
|
||||
_, _, err = i.vtbl.GetIsScriptEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&isScriptEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return isScriptEnabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutIsScriptEnabled(isScriptEnabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsScriptEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(isScriptEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetIsWebMessageEnabled() (bool, error) {
|
||||
var err error
|
||||
var isWebMessageEnabled bool
|
||||
_, _, err = i.vtbl.GetIsWebMessageEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&isWebMessageEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return isWebMessageEnabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutIsWebMessageEnabled(isWebMessageEnabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsWebMessageEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(isWebMessageEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetAreDefaultScriptDialogsEnabled() (bool, error) {
|
||||
var err error
|
||||
var areDefaultScriptDialogsEnabled bool
|
||||
_, _, err = i.vtbl.GetAreDefaultScriptDialogsEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&areDefaultScriptDialogsEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return areDefaultScriptDialogsEnabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutAreDefaultScriptDialogsEnabled(areDefaultScriptDialogsEnabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutAreDefaultScriptDialogsEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(areDefaultScriptDialogsEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetIsStatusBarEnabled() (bool, error) {
|
||||
var err error
|
||||
var isStatusBarEnabled bool
|
||||
_, _, err = i.vtbl.GetIsStatusBarEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&isStatusBarEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return isStatusBarEnabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutIsStatusBarEnabled(isStatusBarEnabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsStatusBarEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(isStatusBarEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetAreDevToolsEnabled() (bool, error) {
|
||||
var err error
|
||||
var areDevToolsEnabled bool
|
||||
_, _, err = i.vtbl.GetAreDevToolsEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&areDevToolsEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return areDevToolsEnabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutAreDevToolsEnabled(areDevToolsEnabled bool) error {
|
||||
var err error
|
||||
_, _, err = i.vtbl.PutAreDevToolsEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(areDevToolsEnabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetAreDefaultContextMenusEnabled() (bool, error) {
|
||||
var err error
|
||||
var enabled bool
|
||||
_, _, err = i.vtbl.GetAreDefaultContextMenusEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutAreDefaultContextMenusEnabled(enabled bool) error {
|
||||
var err error
|
||||
_, _, err = i.vtbl.PutAreDefaultContextMenusEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetAreHostObjectsAllowed() (bool, error) {
|
||||
var err error
|
||||
var allowed bool
|
||||
_, _, err = i.vtbl.GetAreHostObjectsAllowed.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&allowed)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return allowed, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutAreHostObjectsAllowed(allowed bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutAreHostObjectsAllowed.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(allowed)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetIsZoomControlEnabled() (bool, error) {
|
||||
var err error
|
||||
var enabled bool
|
||||
_, _, err = i.vtbl.GetIsZoomControlEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutIsZoomControlEnabled(enabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsZoomControlEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetIsBuiltInErrorPageEnabled() (bool, error) {
|
||||
var err error
|
||||
var enabled bool
|
||||
_, _, err = i.vtbl.GetIsBuiltInErrorPageEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutIsBuiltInErrorPageEnabled(enabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsBuiltInErrorPageEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetUserAgent() (string, error) {
|
||||
var err error
|
||||
// Create *uint16 to hold result
|
||||
var _userAgent *uint16
|
||||
_, _, err = i.vtbl.GetUserAgent.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(_userAgent)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return "", err
|
||||
} // Get result and cleanup
|
||||
userAgent := windows.UTF16PtrToString(_userAgent)
|
||||
windows.CoTaskMemFree(unsafe.Pointer(_userAgent))
|
||||
return userAgent, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutUserAgent(userAgent string) error {
|
||||
var err error
|
||||
// Convert string 'userAgent' to *uint16
|
||||
_userAgent, err := windows.UTF16PtrFromString(userAgent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = i.vtbl.PutUserAgent.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(_userAgent)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetAreBrowserAcceleratorKeysEnabled() (bool, error) {
|
||||
var err error
|
||||
var enabled bool
|
||||
_, _, err = i.vtbl.GetAreBrowserAcceleratorKeysEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutAreBrowserAcceleratorKeysEnabled(enabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutAreBrowserAcceleratorKeysEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetIsPinchZoomEnabled() (bool, error) {
|
||||
var err error
|
||||
var enabled bool
|
||||
_, _, err = i.vtbl.GetIsPinchZoomEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutIsPinchZoomEnabled(enabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsPinchZoomEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) GetIsSwipeNavigationEnabled() (bool, error) {
|
||||
var err error
|
||||
var enabled bool
|
||||
_, _, err = i.vtbl.GetIsSwipeNavigationEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return false, err
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
func (i *ICoreWebViewSettings) PutIsSwipeNavigationEnabled(enabled bool) error {
|
||||
var err error
|
||||
|
||||
_, _, err = i.vtbl.PutIsSwipeNavigationEnabled.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(boolToInt(enabled)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package edge
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type _IStreamVtbl struct {
|
||||
_IUnknownVtbl
|
||||
}
|
||||
|
||||
type IStream struct {
|
||||
vtbl *_IStreamVtbl
|
||||
}
|
||||
|
||||
func (i *IStream) Release() error {
|
||||
return i.vtbl.CallRelease(unsafe.Pointer(i))
|
||||
}
|
@ -0,0 +1,351 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package edge
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type Chromium struct {
|
||||
hwnd uintptr
|
||||
controller *ICoreWebView2Controller
|
||||
webview *ICoreWebView2
|
||||
inited uintptr
|
||||
envCompleted *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler
|
||||
controllerCompleted *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler
|
||||
webMessageReceived *iCoreWebView2WebMessageReceivedEventHandler
|
||||
permissionRequested *iCoreWebView2PermissionRequestedEventHandler
|
||||
webResourceRequested *iCoreWebView2WebResourceRequestedEventHandler
|
||||
acceleratorKeyPressed *ICoreWebView2AcceleratorKeyPressedEventHandler
|
||||
navigationCompleted *ICoreWebView2NavigationCompletedEventHandler
|
||||
|
||||
environment *ICoreWebView2Environment
|
||||
|
||||
// Settings
|
||||
Debug bool
|
||||
DataPath string
|
||||
|
||||
// permissions
|
||||
permissions map[CoreWebView2PermissionKind]CoreWebView2PermissionState
|
||||
globalPermission *CoreWebView2PermissionState
|
||||
|
||||
// Callbacks
|
||||
MessageCallback func(string)
|
||||
WebResourceRequestedCallback func(request *ICoreWebView2WebResourceRequest, args *ICoreWebView2WebResourceRequestedEventArgs)
|
||||
NavigationCompletedCallback func(sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs)
|
||||
AcceleratorKeyCallback func(uint) bool
|
||||
}
|
||||
|
||||
func NewChromium() *Chromium {
|
||||
e := &Chromium{}
|
||||
/*
|
||||
All these handlers are passed to native code through syscalls with 'uintptr(unsafe.Pointer(handler))' and we know
|
||||
that a pointer to those will be kept in the native code. Furthermore these handlers als contain pointer to other Go
|
||||
structs like the vtable.
|
||||
This violates the unsafe.Pointer rule '(4) Conversion of a Pointer to a uintptr when calling syscall.Syscall.' because
|
||||
theres no guarantee that Go doesn't move these objects.
|
||||
AFAIK currently the Go runtime doesn't move HEAP objects, so we should be safe with these handlers. But they don't
|
||||
guarantee it, because in the future Go might use a compacting GC.
|
||||
There's a proposal to add a runtime.Pin function, to prevent moving pinned objects, which would allow to easily fix
|
||||
this issue by just pinning the handlers. The https://go-review.googlesource.com/c/go/+/367296/ should land in Go 1.19.
|
||||
*/
|
||||
e.envCompleted = newICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler(e)
|
||||
e.controllerCompleted = newICoreWebView2CreateCoreWebView2ControllerCompletedHandler(e)
|
||||
e.webMessageReceived = newICoreWebView2WebMessageReceivedEventHandler(e)
|
||||
e.permissionRequested = newICoreWebView2PermissionRequestedEventHandler(e)
|
||||
e.webResourceRequested = newICoreWebView2WebResourceRequestedEventHandler(e)
|
||||
e.acceleratorKeyPressed = newICoreWebView2AcceleratorKeyPressedEventHandler(e)
|
||||
e.navigationCompleted = newICoreWebView2NavigationCompletedEventHandler(e)
|
||||
e.permissions = make(map[CoreWebView2PermissionKind]CoreWebView2PermissionState)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Chromium) Embed(hwnd uintptr) bool {
|
||||
e.hwnd = hwnd
|
||||
|
||||
dataPath := e.DataPath
|
||||
if dataPath == "" {
|
||||
currentExePath := make([]uint16, windows.MAX_PATH)
|
||||
_, err := windows.GetModuleFileName(windows.Handle(0), ¤tExePath[0], windows.MAX_PATH)
|
||||
if err != nil {
|
||||
// What to do here?
|
||||
return false
|
||||
}
|
||||
currentExeName := filepath.Base(windows.UTF16ToString(currentExePath))
|
||||
dataPath = filepath.Join(os.Getenv("AppData"), currentExeName)
|
||||
}
|
||||
|
||||
res, err := createCoreWebView2EnvironmentWithOptions(nil, windows.StringToUTF16Ptr(dataPath), 0, e.envCompleted)
|
||||
if err != nil {
|
||||
log.Printf("Error calling Webview2Loader: %v", err)
|
||||
return false
|
||||
} else if res != 0 {
|
||||
log.Printf("Result: %08x", res)
|
||||
return false
|
||||
}
|
||||
var msg w32.Msg
|
||||
for {
|
||||
if atomic.LoadUintptr(&e.inited) != 0 {
|
||||
break
|
||||
}
|
||||
r, _, _ := w32.User32GetMessageW.Call(
|
||||
uintptr(unsafe.Pointer(&msg)),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if r == 0 {
|
||||
break
|
||||
}
|
||||
w32.User32TranslateMessage.Call(uintptr(unsafe.Pointer(&msg)))
|
||||
w32.User32DispatchMessageW.Call(uintptr(unsafe.Pointer(&msg)))
|
||||
}
|
||||
e.Init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}")
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Chromium) Navigate(url string) {
|
||||
e.webview.vtbl.Navigate.Call(
|
||||
uintptr(unsafe.Pointer(e.webview)),
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(url))),
|
||||
)
|
||||
}
|
||||
|
||||
func (e *Chromium) Init(script string) {
|
||||
e.webview.vtbl.AddScriptToExecuteOnDocumentCreated.Call(
|
||||
uintptr(unsafe.Pointer(e.webview)),
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(script))),
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
func (e *Chromium) Eval(script string) {
|
||||
|
||||
_script, err := windows.UTF16PtrFromString(script)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
e.webview.vtbl.ExecuteScript.Call(
|
||||
uintptr(unsafe.Pointer(e.webview)),
|
||||
uintptr(unsafe.Pointer(_script)),
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
func (e *Chromium) Show() error {
|
||||
return e.controller.PutIsVisible(true)
|
||||
}
|
||||
|
||||
func (e *Chromium) Hide() error {
|
||||
return e.controller.PutIsVisible(false)
|
||||
}
|
||||
|
||||
func (e *Chromium) QueryInterface(_, _ uintptr) uintptr {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *Chromium) AddRef() uintptr {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (e *Chromium) Release() uintptr {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (e *Chromium) EnvironmentCompleted(res uintptr, env *ICoreWebView2Environment) uintptr {
|
||||
if int64(res) < 0 {
|
||||
log.Fatalf("Creating environment failed with %08x", res)
|
||||
}
|
||||
env.vtbl.AddRef.Call(uintptr(unsafe.Pointer(env)))
|
||||
e.environment = env
|
||||
env.vtbl.CreateCoreWebView2Controller.Call(
|
||||
uintptr(unsafe.Pointer(env)),
|
||||
e.hwnd,
|
||||
uintptr(unsafe.Pointer(e.controllerCompleted)),
|
||||
)
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *Chromium) CreateCoreWebView2ControllerCompleted(res uintptr, controller *ICoreWebView2Controller) uintptr {
|
||||
if int64(res) < 0 {
|
||||
log.Fatalf("Creating controller failed with %08x", res)
|
||||
}
|
||||
controller.vtbl.AddRef.Call(uintptr(unsafe.Pointer(controller)))
|
||||
e.controller = controller
|
||||
|
||||
var token _EventRegistrationToken
|
||||
controller.vtbl.GetCoreWebView2.Call(
|
||||
uintptr(unsafe.Pointer(controller)),
|
||||
uintptr(unsafe.Pointer(&e.webview)),
|
||||
)
|
||||
e.webview.vtbl.AddRef.Call(
|
||||
uintptr(unsafe.Pointer(e.webview)),
|
||||
)
|
||||
e.webview.vtbl.AddWebMessageReceived.Call(
|
||||
uintptr(unsafe.Pointer(e.webview)),
|
||||
uintptr(unsafe.Pointer(e.webMessageReceived)),
|
||||
uintptr(unsafe.Pointer(&token)),
|
||||
)
|
||||
e.webview.vtbl.AddPermissionRequested.Call(
|
||||
uintptr(unsafe.Pointer(e.webview)),
|
||||
uintptr(unsafe.Pointer(e.permissionRequested)),
|
||||
uintptr(unsafe.Pointer(&token)),
|
||||
)
|
||||
e.webview.vtbl.AddWebResourceRequested.Call(
|
||||
uintptr(unsafe.Pointer(e.webview)),
|
||||
uintptr(unsafe.Pointer(e.webResourceRequested)),
|
||||
uintptr(unsafe.Pointer(&token)),
|
||||
)
|
||||
e.webview.vtbl.AddNavigationCompleted.Call(
|
||||
uintptr(unsafe.Pointer(e.webview)),
|
||||
uintptr(unsafe.Pointer(e.navigationCompleted)),
|
||||
uintptr(unsafe.Pointer(&token)),
|
||||
)
|
||||
|
||||
e.controller.AddAcceleratorKeyPressed(e.acceleratorKeyPressed, &token)
|
||||
|
||||
atomic.StoreUintptr(&e.inited, 1)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *Chromium) MessageReceived(sender *ICoreWebView2, args *iCoreWebView2WebMessageReceivedEventArgs) uintptr {
|
||||
var message *uint16
|
||||
args.vtbl.TryGetWebMessageAsString.Call(
|
||||
uintptr(unsafe.Pointer(args)),
|
||||
uintptr(unsafe.Pointer(&message)),
|
||||
)
|
||||
if e.MessageCallback != nil {
|
||||
e.MessageCallback(w32.Utf16PtrToString(message))
|
||||
}
|
||||
sender.vtbl.PostWebMessageAsString.Call(
|
||||
uintptr(unsafe.Pointer(sender)),
|
||||
uintptr(unsafe.Pointer(message)),
|
||||
)
|
||||
windows.CoTaskMemFree(unsafe.Pointer(message))
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *Chromium) SetPermission(kind CoreWebView2PermissionKind, state CoreWebView2PermissionState) {
|
||||
e.permissions[kind] = state
|
||||
}
|
||||
|
||||
func (e *Chromium) SetGlobalPermission(state CoreWebView2PermissionState) {
|
||||
e.globalPermission = &state
|
||||
}
|
||||
|
||||
func (e *Chromium) PermissionRequested(_ *ICoreWebView2, args *iCoreWebView2PermissionRequestedEventArgs) uintptr {
|
||||
var kind CoreWebView2PermissionKind
|
||||
args.vtbl.GetPermissionKind.Call(
|
||||
uintptr(unsafe.Pointer(args)),
|
||||
uintptr(kind),
|
||||
)
|
||||
var result CoreWebView2PermissionState
|
||||
if e.globalPermission != nil {
|
||||
result = *e.globalPermission
|
||||
} else {
|
||||
var ok bool
|
||||
result, ok = e.permissions[kind]
|
||||
if !ok {
|
||||
result = CoreWebView2PermissionStateDefault
|
||||
}
|
||||
}
|
||||
args.vtbl.PutState.Call(
|
||||
uintptr(unsafe.Pointer(args)),
|
||||
uintptr(result),
|
||||
)
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *Chromium) WebResourceRequested(sender *ICoreWebView2, args *ICoreWebView2WebResourceRequestedEventArgs) uintptr {
|
||||
req, err := args.GetRequest()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer req.Release()
|
||||
|
||||
if e.WebResourceRequestedCallback != nil {
|
||||
e.WebResourceRequestedCallback(req, args)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *Chromium) AddWebResourceRequestedFilter(filter string, ctx COREWEBVIEW2_WEB_RESOURCE_CONTEXT) {
|
||||
err := e.webview.AddWebResourceRequestedFilter(filter, ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Chromium) Environment() *ICoreWebView2Environment {
|
||||
return e.environment
|
||||
}
|
||||
|
||||
// AcceleratorKeyPressed is called when an accelerator key is pressed.
|
||||
// If the AcceleratorKeyCallback method has been set, it will defer handling of the keypress
|
||||
// to the callback. That callback returns a bool indicating if the event was handled.
|
||||
func (e *Chromium) AcceleratorKeyPressed(sender *ICoreWebView2Controller, args *ICoreWebView2AcceleratorKeyPressedEventArgs) uintptr {
|
||||
if e.AcceleratorKeyCallback == nil {
|
||||
return 0
|
||||
}
|
||||
eventKind, _ := args.GetKeyEventKind()
|
||||
if eventKind == COREWEBVIEW2_KEY_EVENT_KIND_KEY_DOWN ||
|
||||
eventKind == COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_DOWN {
|
||||
virtualKey, _ := args.GetVirtualKey()
|
||||
status, _ := args.GetPhysicalKeyStatus()
|
||||
if !status.WasKeyDown {
|
||||
args.PutHandled(e.AcceleratorKeyCallback(virtualKey))
|
||||
return 0
|
||||
}
|
||||
}
|
||||
args.PutHandled(false)
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *Chromium) GetSettings() (*ICoreWebViewSettings, error) {
|
||||
return e.webview.GetSettings()
|
||||
}
|
||||
|
||||
func (e *Chromium) GetController() *ICoreWebView2Controller {
|
||||
return e.controller
|
||||
}
|
||||
|
||||
func boolToInt(input bool) int {
|
||||
if input {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *Chromium) NavigationCompleted(sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) uintptr {
|
||||
if e.NavigationCompletedCallback != nil {
|
||||
e.NavigationCompletedCallback(sender, args)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *Chromium) NotifyParentWindowPositionChanged() error {
|
||||
//It looks like the wndproc function is called before the controller initialization is complete.
|
||||
//Because of this the controller is nil
|
||||
if e.controller == nil {
|
||||
return nil
|
||||
}
|
||||
return e.controller.NotifyParentWindowPositionChanged()
|
||||
}
|
||||
|
||||
func (e *Chromium) Focus() {
|
||||
err := e.controller.MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package edge
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (e *Chromium) Resize() {
|
||||
if e.controller == nil {
|
||||
return
|
||||
}
|
||||
var bounds w32.Rect
|
||||
w32.User32GetClientRect.Call(e.hwnd, uintptr(unsafe.Pointer(&bounds)))
|
||||
e.controller.vtbl.PutBounds.Call(
|
||||
uintptr(unsafe.Pointer(e.controller)),
|
||||
uintptr(bounds.Left),
|
||||
uintptr(bounds.Top),
|
||||
uintptr(bounds.Right),
|
||||
uintptr(bounds.Bottom),
|
||||
)
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package edge
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (e *Chromium) Resize() {
|
||||
if e.controller == nil {
|
||||
return
|
||||
}
|
||||
var bounds w32.Rect
|
||||
w32.User32GetClientRect.Call(e.hwnd, uintptr(unsafe.Pointer(&bounds)))
|
||||
e.controller.vtbl.PutBounds.Call(
|
||||
uintptr(unsafe.Pointer(e.controller)),
|
||||
uintptr(unsafe.Pointer(&bounds)),
|
||||
)
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package edge
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32"
|
||||
)
|
||||
|
||||
func (e *Chromium) Resize() {
|
||||
if e.controller == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var bounds w32.Rect
|
||||
w32.User32GetClientRect.Call(e.hwnd, uintptr(unsafe.Pointer(&bounds)))
|
||||
|
||||
words := (*[2]uintptr)(unsafe.Pointer(&bounds))
|
||||
e.controller.vtbl.PutBounds.Call(
|
||||
uintptr(unsafe.Pointer(e.controller)),
|
||||
words[0],
|
||||
words[1],
|
||||
)
|
||||
}
|
@ -0,0 +1,480 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package edge
|
||||
|
||||
import (
|
||||
"log"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webviewloader"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
r, _, _ := w32.Ole32CoInitializeEx.Call(0, 2)
|
||||
if int(r) < 0 {
|
||||
log.Printf("Warning: CoInitializeEx call failed: E=%08x", r)
|
||||
}
|
||||
}
|
||||
|
||||
type _EventRegistrationToken struct {
|
||||
value int64
|
||||
}
|
||||
|
||||
type CoreWebView2PermissionKind uint32
|
||||
|
||||
const (
|
||||
CoreWebView2PermissionKindUnknownPermission CoreWebView2PermissionKind = iota
|
||||
CoreWebView2PermissionKindMicrophone
|
||||
CoreWebView2PermissionKindCamera
|
||||
CoreWebView2PermissionKindGeolocation
|
||||
CoreWebView2PermissionKindNotifications
|
||||
CoreWebView2PermissionKindOtherSensors
|
||||
CoreWebView2PermissionKindClipboardRead
|
||||
)
|
||||
|
||||
type CoreWebView2PermissionState uint32
|
||||
|
||||
const (
|
||||
CoreWebView2PermissionStateDefault CoreWebView2PermissionState = iota
|
||||
CoreWebView2PermissionStateAllow
|
||||
CoreWebView2PermissionStateDeny
|
||||
)
|
||||
|
||||
func createCoreWebView2EnvironmentWithOptions(browserExecutableFolder, userDataFolder *uint16, environmentOptions uintptr, environmentCompletedHandle *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) (uintptr, error) {
|
||||
return webviewloader.CreateCoreWebView2EnvironmentWithOptions(
|
||||
browserExecutableFolder,
|
||||
userDataFolder,
|
||||
environmentOptions,
|
||||
uintptr(unsafe.Pointer(environmentCompletedHandle)),
|
||||
)
|
||||
}
|
||||
|
||||
// ComProc stores a COM procedure.
|
||||
type ComProc uintptr
|
||||
|
||||
// NewComProc creates a new COM proc from a Go function.
|
||||
func NewComProc(fn interface{}) ComProc {
|
||||
return ComProc(windows.NewCallback(fn))
|
||||
}
|
||||
|
||||
//go:uintptrescapes
|
||||
// Call calls a COM procedure.
|
||||
func (p ComProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||||
// The magic uintptrescapes comment is needed to prevent moving uintptr(unsafe.Pointer(p)) so calls to .Call() also
|
||||
// satisfy the unsafe.Pointer rule "(4) Conversion of a Pointer to a uintptr when calling syscall.Syscall."
|
||||
// Otherwise it might be that pointers get moved, especially pointer onto the Go stack which might grow dynamically.
|
||||
// See https://pkg.go.dev/unsafe#Pointer and https://github.com/golang/go/issues/34474
|
||||
switch len(a) {
|
||||
case 0:
|
||||
return syscall.Syscall(uintptr(p), 0, 0, 0, 0)
|
||||
case 1:
|
||||
return syscall.Syscall(uintptr(p), 1, a[0], 0, 0)
|
||||
case 2:
|
||||
return syscall.Syscall(uintptr(p), 2, a[0], a[1], 0)
|
||||
case 3:
|
||||
return syscall.Syscall(uintptr(p), 3, a[0], a[1], a[2])
|
||||
case 4:
|
||||
return syscall.Syscall6(uintptr(p), 4, a[0], a[1], a[2], a[3], 0, 0)
|
||||
case 5:
|
||||
return syscall.Syscall6(uintptr(p), 5, a[0], a[1], a[2], a[3], a[4], 0)
|
||||
case 6:
|
||||
return syscall.Syscall6(uintptr(p), 6, a[0], a[1], a[2], a[3], a[4], a[5])
|
||||
case 7:
|
||||
return syscall.Syscall9(uintptr(p), 7, a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
|
||||
case 8:
|
||||
return syscall.Syscall9(uintptr(p), 8, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
|
||||
case 9:
|
||||
return syscall.Syscall9(uintptr(p), 9, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
|
||||
case 10:
|
||||
return syscall.Syscall12(uintptr(p), 10, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0)
|
||||
case 11:
|
||||
return syscall.Syscall12(uintptr(p), 11, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0)
|
||||
case 12:
|
||||
return syscall.Syscall12(uintptr(p), 12, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11])
|
||||
case 13:
|
||||
return syscall.Syscall15(uintptr(p), 13, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0)
|
||||
case 14:
|
||||
return syscall.Syscall15(uintptr(p), 14, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0)
|
||||
case 15:
|
||||
return syscall.Syscall15(uintptr(p), 15, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14])
|
||||
default:
|
||||
panic("too many arguments")
|
||||
}
|
||||
}
|
||||
|
||||
// IUnknown
|
||||
|
||||
type _IUnknownVtbl struct {
|
||||
QueryInterface ComProc
|
||||
AddRef ComProc
|
||||
Release ComProc
|
||||
}
|
||||
|
||||
func (i *_IUnknownVtbl) CallRelease(this unsafe.Pointer) error {
|
||||
_, _, err := i.Release.Call(
|
||||
uintptr(this),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type _IUnknownImpl interface {
|
||||
QueryInterface(refiid, object uintptr) uintptr
|
||||
AddRef() uintptr
|
||||
Release() uintptr
|
||||
}
|
||||
|
||||
// ICoreWebView2
|
||||
|
||||
type iCoreWebView2Vtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetSettings ComProc
|
||||
GetSource ComProc
|
||||
Navigate ComProc
|
||||
NavigateToString ComProc
|
||||
AddNavigationStarting ComProc
|
||||
RemoveNavigationStarting ComProc
|
||||
AddContentLoading ComProc
|
||||
RemoveContentLoading ComProc
|
||||
AddSourceChanged ComProc
|
||||
RemoveSourceChanged ComProc
|
||||
AddHistoryChanged ComProc
|
||||
RemoveHistoryChanged ComProc
|
||||
AddNavigationCompleted ComProc
|
||||
RemoveNavigationCompleted ComProc
|
||||
AddFrameNavigationStarting ComProc
|
||||
RemoveFrameNavigationStarting ComProc
|
||||
AddFrameNavigationCompleted ComProc
|
||||
RemoveFrameNavigationCompleted ComProc
|
||||
AddScriptDialogOpening ComProc
|
||||
RemoveScriptDialogOpening ComProc
|
||||
AddPermissionRequested ComProc
|
||||
RemovePermissionRequested ComProc
|
||||
AddProcessFailed ComProc
|
||||
RemoveProcessFailed ComProc
|
||||
AddScriptToExecuteOnDocumentCreated ComProc
|
||||
RemoveScriptToExecuteOnDocumentCreated ComProc
|
||||
ExecuteScript ComProc
|
||||
CapturePreview ComProc
|
||||
Reload ComProc
|
||||
PostWebMessageAsJSON ComProc
|
||||
PostWebMessageAsString ComProc
|
||||
AddWebMessageReceived ComProc
|
||||
RemoveWebMessageReceived ComProc
|
||||
CallDevToolsProtocolMethod ComProc
|
||||
GetBrowserProcessID ComProc
|
||||
GetCanGoBack ComProc
|
||||
GetCanGoForward ComProc
|
||||
GoBack ComProc
|
||||
GoForward ComProc
|
||||
GetDevToolsProtocolEventReceiver ComProc
|
||||
Stop ComProc
|
||||
AddNewWindowRequested ComProc
|
||||
RemoveNewWindowRequested ComProc
|
||||
AddDocumentTitleChanged ComProc
|
||||
RemoveDocumentTitleChanged ComProc
|
||||
GetDocumentTitle ComProc
|
||||
AddHostObjectToScript ComProc
|
||||
RemoveHostObjectFromScript ComProc
|
||||
OpenDevToolsWindow ComProc
|
||||
AddContainsFullScreenElementChanged ComProc
|
||||
RemoveContainsFullScreenElementChanged ComProc
|
||||
GetContainsFullScreenElement ComProc
|
||||
AddWebResourceRequested ComProc
|
||||
RemoveWebResourceRequested ComProc
|
||||
AddWebResourceRequestedFilter ComProc
|
||||
RemoveWebResourceRequestedFilter ComProc
|
||||
AddWindowCloseRequested ComProc
|
||||
RemoveWindowCloseRequested ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2 struct {
|
||||
vtbl *iCoreWebView2Vtbl
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2) GetSettings() (*ICoreWebViewSettings, error) {
|
||||
var err error
|
||||
var settings *ICoreWebViewSettings
|
||||
_, _, err = i.vtbl.GetSettings.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&settings)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return nil, err
|
||||
}
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
// ICoreWebView2Environment
|
||||
|
||||
type iCoreWebView2EnvironmentVtbl struct {
|
||||
_IUnknownVtbl
|
||||
CreateCoreWebView2Controller ComProc
|
||||
CreateWebResourceResponse ComProc
|
||||
GetBrowserVersionString ComProc
|
||||
AddNewBrowserVersionAvailable ComProc
|
||||
RemoveNewBrowserVersionAvailable ComProc
|
||||
}
|
||||
|
||||
type ICoreWebView2Environment struct {
|
||||
vtbl *iCoreWebView2EnvironmentVtbl
|
||||
}
|
||||
|
||||
// CreateWebResourceResponse creates a new ICoreWebView2WebResourceResponse, it must be released after finishing using it.
|
||||
func (e *ICoreWebView2Environment) CreateWebResourceResponse(content []byte, statusCode int, reasonPhrase string, headers string) (*ICoreWebView2WebResourceResponse, error) {
|
||||
var err error
|
||||
var stream uintptr
|
||||
|
||||
if len(content) > 0 {
|
||||
// Create stream for response
|
||||
stream, err = w32.SHCreateMemStream(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Release the IStream after we are finished, CreateWebResourceResponse Call will increase the reference
|
||||
// count on IStream and therefore it won't be freed until the reference count of the response is 0.
|
||||
defer (*IStream)(unsafe.Pointer(stream)).Release()
|
||||
}
|
||||
|
||||
// Convert string 'uri' to *uint16
|
||||
_reason, err := windows.UTF16PtrFromString(reasonPhrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Convert string 'uri' to *uint16
|
||||
_headers, err := windows.UTF16PtrFromString(headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response *ICoreWebView2WebResourceResponse
|
||||
_, _, err = e.vtbl.CreateWebResourceResponse.Call(
|
||||
uintptr(unsafe.Pointer(e)),
|
||||
stream,
|
||||
uintptr(statusCode),
|
||||
uintptr(unsafe.Pointer(_reason)),
|
||||
uintptr(unsafe.Pointer(_headers)),
|
||||
uintptr(unsafe.Pointer(&response)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
|
||||
}
|
||||
|
||||
// ICoreWebView2WebMessageReceivedEventArgs
|
||||
|
||||
type iCoreWebView2WebMessageReceivedEventArgsVtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetSource ComProc
|
||||
GetWebMessageAsJSON ComProc
|
||||
TryGetWebMessageAsString ComProc
|
||||
}
|
||||
|
||||
type iCoreWebView2WebMessageReceivedEventArgs struct {
|
||||
vtbl *iCoreWebView2WebMessageReceivedEventArgsVtbl
|
||||
}
|
||||
|
||||
// ICoreWebView2PermissionRequestedEventArgs
|
||||
|
||||
type iCoreWebView2PermissionRequestedEventArgsVtbl struct {
|
||||
_IUnknownVtbl
|
||||
GetURI ComProc
|
||||
GetPermissionKind ComProc
|
||||
GetIsUserInitiated ComProc
|
||||
GetState ComProc
|
||||
PutState ComProc
|
||||
GetDeferral ComProc
|
||||
}
|
||||
|
||||
type iCoreWebView2PermissionRequestedEventArgs struct {
|
||||
vtbl *iCoreWebView2PermissionRequestedEventArgsVtbl
|
||||
}
|
||||
|
||||
// ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler
|
||||
|
||||
type iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerImpl interface {
|
||||
_IUnknownImpl
|
||||
EnvironmentCompleted(res uintptr, env *ICoreWebView2Environment) uintptr
|
||||
}
|
||||
|
||||
type iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl struct {
|
||||
_IUnknownVtbl
|
||||
Invoke ComProc
|
||||
}
|
||||
|
||||
type iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler struct {
|
||||
vtbl *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl
|
||||
impl iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerImpl
|
||||
}
|
||||
|
||||
func _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownQueryInterface(this *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, refiid, object uintptr) uintptr {
|
||||
return this.impl.QueryInterface(refiid, object)
|
||||
}
|
||||
|
||||
func _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownAddRef(this *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) uintptr {
|
||||
return this.impl.AddRef()
|
||||
}
|
||||
|
||||
func _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownRelease(this *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) uintptr {
|
||||
return this.impl.Release()
|
||||
}
|
||||
|
||||
func _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerInvoke(this *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, res uintptr, env *ICoreWebView2Environment) uintptr {
|
||||
return this.impl.EnvironmentCompleted(res, env)
|
||||
}
|
||||
|
||||
var iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerFn = iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl{
|
||||
_IUnknownVtbl{
|
||||
NewComProc(_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownQueryInterface),
|
||||
NewComProc(_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownAddRef),
|
||||
NewComProc(_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownRelease),
|
||||
},
|
||||
NewComProc(_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerInvoke),
|
||||
}
|
||||
|
||||
func newICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler(impl iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerImpl) *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler {
|
||||
return &iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler{
|
||||
vtbl: &iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerFn,
|
||||
impl: impl,
|
||||
}
|
||||
}
|
||||
|
||||
// ICoreWebView2WebMessageReceivedEventHandler
|
||||
|
||||
type iCoreWebView2WebMessageReceivedEventHandlerImpl interface {
|
||||
_IUnknownImpl
|
||||
MessageReceived(sender *ICoreWebView2, args *iCoreWebView2WebMessageReceivedEventArgs) uintptr
|
||||
}
|
||||
|
||||
type iCoreWebView2WebMessageReceivedEventHandlerVtbl struct {
|
||||
_IUnknownVtbl
|
||||
Invoke ComProc
|
||||
}
|
||||
|
||||
type iCoreWebView2WebMessageReceivedEventHandler struct {
|
||||
vtbl *iCoreWebView2WebMessageReceivedEventHandlerVtbl
|
||||
impl iCoreWebView2WebMessageReceivedEventHandlerImpl
|
||||
}
|
||||
|
||||
func _ICoreWebView2WebMessageReceivedEventHandlerIUnknownQueryInterface(this *iCoreWebView2WebMessageReceivedEventHandler, refiid, object uintptr) uintptr {
|
||||
return this.impl.QueryInterface(refiid, object)
|
||||
}
|
||||
|
||||
func _ICoreWebView2WebMessageReceivedEventHandlerIUnknownAddRef(this *iCoreWebView2WebMessageReceivedEventHandler) uintptr {
|
||||
return this.impl.AddRef()
|
||||
}
|
||||
|
||||
func _ICoreWebView2WebMessageReceivedEventHandlerIUnknownRelease(this *iCoreWebView2WebMessageReceivedEventHandler) uintptr {
|
||||
return this.impl.Release()
|
||||
}
|
||||
|
||||
func _ICoreWebView2WebMessageReceivedEventHandlerInvoke(this *iCoreWebView2WebMessageReceivedEventHandler, sender *ICoreWebView2, args *iCoreWebView2WebMessageReceivedEventArgs) uintptr {
|
||||
return this.impl.MessageReceived(sender, args)
|
||||
}
|
||||
|
||||
var iCoreWebView2WebMessageReceivedEventHandlerFn = iCoreWebView2WebMessageReceivedEventHandlerVtbl{
|
||||
_IUnknownVtbl{
|
||||
NewComProc(_ICoreWebView2WebMessageReceivedEventHandlerIUnknownQueryInterface),
|
||||
NewComProc(_ICoreWebView2WebMessageReceivedEventHandlerIUnknownAddRef),
|
||||
NewComProc(_ICoreWebView2WebMessageReceivedEventHandlerIUnknownRelease),
|
||||
},
|
||||
NewComProc(_ICoreWebView2WebMessageReceivedEventHandlerInvoke),
|
||||
}
|
||||
|
||||
func newICoreWebView2WebMessageReceivedEventHandler(impl iCoreWebView2WebMessageReceivedEventHandlerImpl) *iCoreWebView2WebMessageReceivedEventHandler {
|
||||
return &iCoreWebView2WebMessageReceivedEventHandler{
|
||||
vtbl: &iCoreWebView2WebMessageReceivedEventHandlerFn,
|
||||
impl: impl,
|
||||
}
|
||||
}
|
||||
|
||||
// ICoreWebView2PermissionRequestedEventHandler
|
||||
|
||||
type iCoreWebView2PermissionRequestedEventHandlerImpl interface {
|
||||
_IUnknownImpl
|
||||
PermissionRequested(sender *ICoreWebView2, args *iCoreWebView2PermissionRequestedEventArgs) uintptr
|
||||
}
|
||||
|
||||
type iCoreWebView2PermissionRequestedEventHandlerVtbl struct {
|
||||
_IUnknownVtbl
|
||||
Invoke ComProc
|
||||
}
|
||||
|
||||
type iCoreWebView2PermissionRequestedEventHandler struct {
|
||||
vtbl *iCoreWebView2PermissionRequestedEventHandlerVtbl
|
||||
impl iCoreWebView2PermissionRequestedEventHandlerImpl
|
||||
}
|
||||
|
||||
func _ICoreWebView2PermissionRequestedEventHandlerIUnknownQueryInterface(this *iCoreWebView2PermissionRequestedEventHandler, refiid, object uintptr) uintptr {
|
||||
return this.impl.QueryInterface(refiid, object)
|
||||
}
|
||||
|
||||
func _ICoreWebView2PermissionRequestedEventHandlerIUnknownAddRef(this *iCoreWebView2PermissionRequestedEventHandler) uintptr {
|
||||
return this.impl.AddRef()
|
||||
}
|
||||
|
||||
func _ICoreWebView2PermissionRequestedEventHandlerIUnknownRelease(this *iCoreWebView2PermissionRequestedEventHandler) uintptr {
|
||||
return this.impl.Release()
|
||||
}
|
||||
|
||||
func _ICoreWebView2PermissionRequestedEventHandlerInvoke(this *iCoreWebView2PermissionRequestedEventHandler, sender *ICoreWebView2, args *iCoreWebView2PermissionRequestedEventArgs) uintptr {
|
||||
return this.impl.PermissionRequested(sender, args)
|
||||
}
|
||||
|
||||
var iCoreWebView2PermissionRequestedEventHandlerFn = iCoreWebView2PermissionRequestedEventHandlerVtbl{
|
||||
_IUnknownVtbl{
|
||||
NewComProc(_ICoreWebView2PermissionRequestedEventHandlerIUnknownQueryInterface),
|
||||
NewComProc(_ICoreWebView2PermissionRequestedEventHandlerIUnknownAddRef),
|
||||
NewComProc(_ICoreWebView2PermissionRequestedEventHandlerIUnknownRelease),
|
||||
},
|
||||
NewComProc(_ICoreWebView2PermissionRequestedEventHandlerInvoke),
|
||||
}
|
||||
|
||||
func newICoreWebView2PermissionRequestedEventHandler(impl iCoreWebView2PermissionRequestedEventHandlerImpl) *iCoreWebView2PermissionRequestedEventHandler {
|
||||
return &iCoreWebView2PermissionRequestedEventHandler{
|
||||
vtbl: &iCoreWebView2PermissionRequestedEventHandlerFn,
|
||||
impl: impl,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ICoreWebView2) AddWebResourceRequestedFilter(uri string, resourceContext COREWEBVIEW2_WEB_RESOURCE_CONTEXT) error {
|
||||
var err error
|
||||
// Convert string 'uri' to *uint16
|
||||
_uri, err := windows.UTF16PtrFromString(uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, err = i.vtbl.AddWebResourceRequestedFilter.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(_uri)),
|
||||
uintptr(resourceContext),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (i *ICoreWebView2) AddNavigationCompleted(eventHandler *ICoreWebView2NavigationCompletedEventHandler, token *_EventRegistrationToken) error {
|
||||
var err error
|
||||
_, _, err = i.vtbl.AddNavigationCompleted.Call(
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(eventHandler)),
|
||||
uintptr(unsafe.Pointer(&token)),
|
||||
)
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,223 @@
|
||||
package edge
|
||||
|
||||
// This code has been adapted from: https://github.com/go-ole/go-ole
|
||||
|
||||
/*
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2013-2017 Yasuhiro Matsumoto, <mattn.jp@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
const hextable = "0123456789ABCDEF"
|
||||
const emptyGUID = "{00000000-0000-0000-0000-000000000000}"
|
||||
|
||||
// GUID is Windows API specific GUID type.
|
||||
//
|
||||
// This exists to match Windows GUID type for direct passing for COM.
|
||||
// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx.
|
||||
type GUID struct {
|
||||
Data1 uint32
|
||||
Data2 uint16
|
||||
Data3 uint16
|
||||
Data4 [8]byte
|
||||
}
|
||||
|
||||
// NewGUID converts the given string into a globally unique identifier that is
|
||||
// compliant with the Windows API.
|
||||
//
|
||||
// The supplied string may be in any of these formats:
|
||||
//
|
||||
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
|
||||
//
|
||||
// The conversion of the supplied string is not case-sensitive.
|
||||
func NewGUID(guid string) *GUID {
|
||||
d := []byte(guid)
|
||||
var d1, d2, d3, d4a, d4b []byte
|
||||
|
||||
switch len(d) {
|
||||
case 38:
|
||||
if d[0] != '{' || d[37] != '}' {
|
||||
return nil
|
||||
}
|
||||
d = d[1:37]
|
||||
fallthrough
|
||||
case 36:
|
||||
if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' {
|
||||
return nil
|
||||
}
|
||||
d1 = d[0:8]
|
||||
d2 = d[9:13]
|
||||
d3 = d[14:18]
|
||||
d4a = d[19:23]
|
||||
d4b = d[24:36]
|
||||
case 32:
|
||||
d1 = d[0:8]
|
||||
d2 = d[8:12]
|
||||
d3 = d[12:16]
|
||||
d4a = d[16:20]
|
||||
d4b = d[20:32]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
var g GUID
|
||||
var ok1, ok2, ok3, ok4 bool
|
||||
g.Data1, ok1 = decodeHexUint32(d1)
|
||||
g.Data2, ok2 = decodeHexUint16(d2)
|
||||
g.Data3, ok3 = decodeHexUint16(d3)
|
||||
g.Data4, ok4 = decodeHexByte64(d4a, d4b)
|
||||
if ok1 && ok2 && ok3 && ok4 {
|
||||
return &g
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeHexUint32(src []byte) (value uint32, ok bool) {
|
||||
var b1, b2, b3, b4 byte
|
||||
var ok1, ok2, ok3, ok4 bool
|
||||
b1, ok1 = decodeHexByte(src[0], src[1])
|
||||
b2, ok2 = decodeHexByte(src[2], src[3])
|
||||
b3, ok3 = decodeHexByte(src[4], src[5])
|
||||
b4, ok4 = decodeHexByte(src[6], src[7])
|
||||
value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4)
|
||||
ok = ok1 && ok2 && ok3 && ok4
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexUint16(src []byte) (value uint16, ok bool) {
|
||||
var b1, b2 byte
|
||||
var ok1, ok2 bool
|
||||
b1, ok1 = decodeHexByte(src[0], src[1])
|
||||
b2, ok2 = decodeHexByte(src[2], src[3])
|
||||
value = (uint16(b1) << 8) | uint16(b2)
|
||||
ok = ok1 && ok2
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) {
|
||||
var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool
|
||||
value[0], ok1 = decodeHexByte(s1[0], s1[1])
|
||||
value[1], ok2 = decodeHexByte(s1[2], s1[3])
|
||||
value[2], ok3 = decodeHexByte(s2[0], s2[1])
|
||||
value[3], ok4 = decodeHexByte(s2[2], s2[3])
|
||||
value[4], ok5 = decodeHexByte(s2[4], s2[5])
|
||||
value[5], ok6 = decodeHexByte(s2[6], s2[7])
|
||||
value[6], ok7 = decodeHexByte(s2[8], s2[9])
|
||||
value[7], ok8 = decodeHexByte(s2[10], s2[11])
|
||||
ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexByte(c1, c2 byte) (value byte, ok bool) {
|
||||
var n1, n2 byte
|
||||
var ok1, ok2 bool
|
||||
n1, ok1 = decodeHexChar(c1)
|
||||
n2, ok2 = decodeHexChar(c2)
|
||||
value = (n1 << 4) | n2
|
||||
ok = ok1 && ok2
|
||||
return
|
||||
}
|
||||
|
||||
func decodeHexChar(c byte) (byte, bool) {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return c - '0', true
|
||||
case 'a' <= c && c <= 'f':
|
||||
return c - 'a' + 10, true
|
||||
case 'A' <= c && c <= 'F':
|
||||
return c - 'A' + 10, true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// String converts the GUID to string form. It will adhere to this pattern:
|
||||
//
|
||||
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
|
||||
//
|
||||
// If the GUID is nil, the string representation of an empty GUID is returned:
|
||||
//
|
||||
// {00000000-0000-0000-0000-000000000000}
|
||||
func (guid *GUID) String() string {
|
||||
if guid == nil {
|
||||
return emptyGUID
|
||||
}
|
||||
|
||||
var c [38]byte
|
||||
c[0] = '{'
|
||||
putUint32Hex(c[1:9], guid.Data1)
|
||||
c[9] = '-'
|
||||
putUint16Hex(c[10:14], guid.Data2)
|
||||
c[14] = '-'
|
||||
putUint16Hex(c[15:19], guid.Data3)
|
||||
c[19] = '-'
|
||||
putByteHex(c[20:24], guid.Data4[0:2])
|
||||
c[24] = '-'
|
||||
putByteHex(c[25:37], guid.Data4[2:8])
|
||||
c[37] = '}'
|
||||
return string(c[:])
|
||||
}
|
||||
|
||||
func putUint32Hex(b []byte, v uint32) {
|
||||
b[0] = hextable[byte(v>>24)>>4]
|
||||
b[1] = hextable[byte(v>>24)&0x0f]
|
||||
b[2] = hextable[byte(v>>16)>>4]
|
||||
b[3] = hextable[byte(v>>16)&0x0f]
|
||||
b[4] = hextable[byte(v>>8)>>4]
|
||||
b[5] = hextable[byte(v>>8)&0x0f]
|
||||
b[6] = hextable[byte(v)>>4]
|
||||
b[7] = hextable[byte(v)&0x0f]
|
||||
}
|
||||
|
||||
func putUint16Hex(b []byte, v uint16) {
|
||||
b[0] = hextable[byte(v>>8)>>4]
|
||||
b[1] = hextable[byte(v>>8)&0x0f]
|
||||
b[2] = hextable[byte(v)>>4]
|
||||
b[3] = hextable[byte(v)&0x0f]
|
||||
}
|
||||
|
||||
func putByteHex(dst, src []byte) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i*2] = hextable[src[i]>>4]
|
||||
dst[i*2+1] = hextable[src[i]&0x0f]
|
||||
}
|
||||
}
|
||||
|
||||
// IsEqualGUID compares two GUID.
|
||||
//
|
||||
// Not constant time comparison.
|
||||
func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool {
|
||||
return guid1.Data1 == guid2.Data1 &&
|
||||
guid1.Data2 == guid2.Data2 &&
|
||||
guid1.Data3 == guid2.Data3 &&
|
||||
guid1.Data4[0] == guid2.Data4[0] &&
|
||||
guid1.Data4[1] == guid2.Data4[1] &&
|
||||
guid1.Data4[2] == guid2.Data4[2] &&
|
||||
guid1.Data4[3] == guid2.Data4[3] &&
|
||||
guid1.Data4[4] == guid2.Data4[4] &&
|
||||
guid1.Data4[5] == guid2.Data4[5] &&
|
||||
guid1.Data4[6] == guid2.Data4[6] &&
|
||||
guid1.Data4[7] == guid2.Data4[7]
|
||||
}
|
Binary file not shown.
@ -0,0 +1,140 @@
|
||||
package webviewloader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/jchv/go-winloader"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
nativeModule = windows.NewLazyDLL("WebView2Loader")
|
||||
nativeCreate = nativeModule.NewProc("CreateCoreWebView2EnvironmentWithOptions")
|
||||
nativeCompareBrowserVersions = nativeModule.NewProc("CompareBrowserVersions")
|
||||
nativeGetAvailableCoreWebView2BrowserVersionString = nativeModule.NewProc("GetAvailableCoreWebView2BrowserVersionString")
|
||||
|
||||
memOnce sync.Once
|
||||
memModule winloader.Module
|
||||
memCreate winloader.Proc
|
||||
memCompareBrowserVersions winloader.Proc
|
||||
memGetAvailableCoreWebView2BrowserVersionString winloader.Proc
|
||||
memErr error
|
||||
)
|
||||
|
||||
// CompareBrowserVersions will compare the 2 given versions and return:
|
||||
// -1 = v1 < v2
|
||||
// 0 = v1 == v2
|
||||
// 1 = v1 > v2
|
||||
func CompareBrowserVersions(v1 string, v2 string) (int, error) {
|
||||
|
||||
_v1, err := windows.UTF16PtrFromString(v1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_v2, err := windows.UTF16PtrFromString(v2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
nativeErr := nativeModule.Load()
|
||||
if nativeErr == nil {
|
||||
nativeErr = nativeCompareBrowserVersions.Find()
|
||||
}
|
||||
var result int
|
||||
if nativeErr != nil {
|
||||
err := loadFromMemory(nativeErr)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Unable to load WebView2Loader.dll from disk: %v -- or from memory: %w", nativeErr, memErr)
|
||||
}
|
||||
_, _, err = memCompareBrowserVersions.Call(
|
||||
uint64(uintptr(unsafe.Pointer(_v1))),
|
||||
uint64(uintptr(unsafe.Pointer(_v2))),
|
||||
uint64(uintptr(unsafe.Pointer(&result))))
|
||||
} else {
|
||||
_, _, err = nativeCompareBrowserVersions.Call(
|
||||
uintptr(unsafe.Pointer(_v1)),
|
||||
uintptr(unsafe.Pointer(_v2)),
|
||||
uintptr(unsafe.Pointer(&result)))
|
||||
}
|
||||
if err != windows.ERROR_SUCCESS {
|
||||
return result, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetInstalledVersion returns the installed version of the webview2 runtime.
|
||||
// If there is no version installed, a blank string is returned.
|
||||
func GetInstalledVersion() (string, error) {
|
||||
nativeErr := nativeModule.Load()
|
||||
if nativeErr == nil {
|
||||
nativeErr = nativeGetAvailableCoreWebView2BrowserVersionString.Find()
|
||||
}
|
||||
var err error
|
||||
var result *uint16
|
||||
if nativeErr != nil {
|
||||
err := loadFromMemory(nativeErr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable to load WebView2Loader.dll from disk: %v -- or from memory: %w", nativeErr, memErr)
|
||||
}
|
||||
_, _, err = memGetAvailableCoreWebView2BrowserVersionString.Call(
|
||||
uint64(uintptr(unsafe.Pointer(nil))),
|
||||
uint64(uintptr(unsafe.Pointer(&result))))
|
||||
} else {
|
||||
_, _, err = nativeCompareBrowserVersions.Call(
|
||||
uintptr(unsafe.Pointer(nil)),
|
||||
uintptr(unsafe.Pointer(&result)))
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
version := windows.UTF16PtrToString(result)
|
||||
windows.CoTaskMemFree(unsafe.Pointer(result))
|
||||
return version, nil
|
||||
}
|
||||
|
||||
// CreateCoreWebView2EnvironmentWithOptions tries to load WebviewLoader2 and
|
||||
// call the CreateCoreWebView2EnvironmentWithOptions routine.
|
||||
func CreateCoreWebView2EnvironmentWithOptions(browserExecutableFolder, userDataFolder *uint16, environmentOptions uintptr, environmentCompletedHandle uintptr) (uintptr, error) {
|
||||
nativeErr := nativeModule.Load()
|
||||
if nativeErr == nil {
|
||||
nativeErr = nativeCreate.Find()
|
||||
}
|
||||
if nativeErr != nil {
|
||||
err := loadFromMemory(nativeErr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
res, _, _ := memCreate.Call(
|
||||
uint64(uintptr(unsafe.Pointer(browserExecutableFolder))),
|
||||
uint64(uintptr(unsafe.Pointer(userDataFolder))),
|
||||
uint64(environmentOptions),
|
||||
uint64(environmentCompletedHandle),
|
||||
)
|
||||
return uintptr(res), nil
|
||||
}
|
||||
res, _, _ := nativeCreate.Call(
|
||||
uintptr(unsafe.Pointer(browserExecutableFolder)),
|
||||
uintptr(unsafe.Pointer(userDataFolder)),
|
||||
environmentOptions,
|
||||
environmentCompletedHandle,
|
||||
)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func loadFromMemory(nativeErr error) error {
|
||||
var err error
|
||||
// DLL is not available natively. Try loading embedded copy.
|
||||
memOnce.Do(func() {
|
||||
memModule, memErr = winloader.LoadFromMemory(WebView2Loader)
|
||||
if memErr != nil {
|
||||
err = fmt.Errorf("Unable to load WebView2Loader.dll from disk: %v -- or from memory: %w", nativeErr, memErr)
|
||||
return
|
||||
}
|
||||
memCreate = memModule.Proc("CreateCoreWebView2EnvironmentWithOptions")
|
||||
memCompareBrowserVersions = memModule.Proc("CompareBrowserVersions")
|
||||
memGetAvailableCoreWebView2BrowserVersionString = memModule.Proc("GetAvailableCoreWebView2BrowserVersionString")
|
||||
})
|
||||
return err
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package webviewloader
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed x86/WebView2Loader.dll
|
||||
var WebView2Loader []byte
|
@ -0,0 +1,6 @@
|
||||
package webviewloader
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed x64/WebView2Loader.dll
|
||||
var WebView2Loader []byte
|
@ -0,0 +1,6 @@
|
||||
package webviewloader
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed arm64/WebView2Loader.dll
|
||||
var WebView2Loader []byte
|
Binary file not shown.
Binary file not shown.
@ -4,7 +4,7 @@
|
||||
package windows
|
||||
|
||||
import (
|
||||
"github.com/leaanthony/winc"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||||
"strings"
|
||||
)
|
||||
|
@ -4,7 +4,7 @@
|
||||
package windows
|
||||
|
||||
import (
|
||||
"github.com/leaanthony/winc"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
)
|
||||
|
||||
|
12
v2/internal/frontend/desktop/windows/winc/.gitignore
vendored
Normal file
12
v2/internal/frontend/desktop/windows/winc/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
12
v2/internal/frontend/desktop/windows/winc/AUTHORS
Normal file
12
v2/internal/frontend/desktop/windows/winc/AUTHORS
Normal file
@ -0,0 +1,12 @@
|
||||
# This is the official list of 'Winc' authors for copyright purposes.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
# Contributors
|
||||
# ============
|
||||
|
||||
Tad Vizbaras <tad@etasoft.com>
|
21
v2/internal/frontend/desktop/windows/winc/LICENSE
Normal file
21
v2/internal/frontend/desktop/windows/winc/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 winc Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
181
v2/internal/frontend/desktop/windows/winc/README.md
Normal file
181
v2/internal/frontend/desktop/windows/winc/README.md
Normal file
@ -0,0 +1,181 @@
|
||||
# winc
|
||||
|
||||
** This is a fork of [tadvi/winc](https://github.com/tadvi/winc) for the sole purpose of integration
|
||||
with [Wails](https://github.com/wailsapp/wails). This repository comes with ***no support*** **
|
||||
|
||||
Common library for Go GUI apps on Windows. It is for Windows OS only. This makes library smaller than some other UI
|
||||
libraries for Go.
|
||||
|
||||
Design goals: minimalism and simplicity.
|
||||
|
||||
## Dependencies
|
||||
|
||||
No other dependencies except Go standard library.
|
||||
|
||||
## Building
|
||||
|
||||
If you want to package icon files and other resources into binary **rsrc** tool is recommended:
|
||||
|
||||
rsrc -manifest app.manifest -ico=app.ico,application_edit.ico,application_error.ico -o rsrc.syso
|
||||
|
||||
Here app.manifest is XML file in format:
|
||||
|
||||
```
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="App" type="win32"/>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
||||
```
|
||||
|
||||
Most Windows applications do not display command prompt. Build your Go project with flag to indicate that it is Windows
|
||||
GUI binary:
|
||||
|
||||
go build -ldflags="-H windowsgui"
|
||||
|
||||
## Samples
|
||||
|
||||
Best way to learn how to use the library is to look at the included **examples** projects.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Make sure you have a working Go installation and build environment, see more for details on page below.
|
||||
http://golang.org/doc/install
|
||||
|
||||
2. go get github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc
|
||||
|
||||
## Icons
|
||||
|
||||
When rsrc is used to pack icons into binary it displays IDs of the packed icons.
|
||||
|
||||
```
|
||||
rsrc -manifest app.manifest -ico=app.ico,lightning.ico,edit.ico,application_error.ico -o rsrc.syso
|
||||
Manifest ID: 1
|
||||
Icon app.ico ID: 10
|
||||
Icon lightning.ico ID: 13
|
||||
Icon edit.ico ID: 16
|
||||
Icon application_error.ico ID: 19
|
||||
```
|
||||
|
||||
Use IDs to reference packed icons.
|
||||
|
||||
```
|
||||
const myIcon = 13
|
||||
|
||||
btn.SetResIcon(myIcon) // Set icon on the button.
|
||||
```
|
||||
|
||||
Included source **examples** use basic building via `release.bat` files. Note that icon IDs are order dependent. So if
|
||||
you change they order in -ico flag then icon IDs will be different. If you want to keep order the same, just add new
|
||||
icons to the end of -ico comma separated list.
|
||||
|
||||
## Layout Manager
|
||||
|
||||
SimpleDock is default layout manager.
|
||||
|
||||
Current design of docking and split views allows building simple apps but if you need to have multiple split views in
|
||||
few different directions you might need to create your own layout manager.
|
||||
|
||||
Important point is to have **one** control inside SimpleDock set to dock as **Fill**. Controls that are not set to any
|
||||
docking get placed using SetPos() function. So you can have Panel set to dock at the Top and then have another dock to
|
||||
arrange controls inside that Panel or have controls placed using SetPos() at fixed positions.
|
||||
|
||||

|
||||
|
||||
This is basic layout. Instead of toolbars and status bar you can have Panel or any other control that can resize. Panel
|
||||
can have its own internal Dock that will arrange other controls inside of it.
|
||||
|
||||

|
||||
|
||||
This is layout with extra control(s) on the left. Left side is usually treeview or listview.
|
||||
|
||||
The rule is simple: you either dock controls using SimpleDock OR use SetPos() to set them at fixed positions. That's it.
|
||||
|
||||
At some point **winc** may get more sophisticated layout manager.
|
||||
|
||||
## Dialog Screens
|
||||
|
||||
Dialog screens are not based on Windows resource files (.rc). They are just windows with controls placed at fixed
|
||||
coordinates. This works fine for dialog screens up to 10-14 controls.
|
||||
|
||||
# Minimal Demo
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
mainWindow := winc.NewForm(nil)
|
||||
mainWindow.SetSize(400, 300) // (width, height)
|
||||
mainWindow.SetText("Hello World Demo")
|
||||
|
||||
edt := winc.NewEdit(mainWindow)
|
||||
edt.SetPos(10, 20)
|
||||
// Most Controls have default size unless SetSize is called.
|
||||
edt.SetText("edit text")
|
||||
|
||||
btn := winc.NewPushButton(mainWindow)
|
||||
btn.SetText("Show or Hide")
|
||||
btn.SetPos(40, 50) // (x, y)
|
||||
btn.SetSize(100, 40) // (width, height)
|
||||
btn.OnClick().Bind(func(e *winc.Event) {
|
||||
if edt.Visible() {
|
||||
edt.Hide()
|
||||
} else {
|
||||
edt.Show()
|
||||
}
|
||||
})
|
||||
|
||||
mainWindow.Center()
|
||||
mainWindow.Show()
|
||||
mainWindow.OnClose().Bind(wndOnClose)
|
||||
|
||||
winc.RunMainLoop() // Must call to start event loop.
|
||||
}
|
||||
|
||||
func wndOnClose(arg *winc.Event) {
|
||||
winc.Exit()
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
Result of running sample_minimal.
|
||||
|
||||
## Create Your Own
|
||||
|
||||
It is good practice to create your own controls based on existing structures and event model. Library contains some of
|
||||
the controls built that way: IconButton (button.go), ErrorPanel (panel.go), MultiEdit (edit.go), etc. Please look at
|
||||
existing controls as examples before building your own.
|
||||
|
||||
When designing your own controls keep in mind that types have to be converted from Go into Win32 API and back. This is
|
||||
usually due to string UTF8 and UTF16 conversions. But there are other types of conversions too.
|
||||
|
||||
When developing your own controls you might also need to:
|
||||
|
||||
import "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
|
||||
w32 has Win32 API low level constants and functions.
|
||||
|
||||
Look at **sample_control** for example of custom built window.
|
||||
|
||||
## Companion Package
|
||||
|
||||
[Go package for Windows Systray icon, menu and notifications](https://github.com/tadvi/systray)
|
||||
|
||||
## Credits
|
||||
|
||||
This library is built on
|
||||
|
||||
[AllenDang/gform Windows GUI framework for Go](https://github.com/AllenDang/gform)
|
||||
|
||||
**winc** takes most design decisions from **gform** and adds many more controls and code samples to it.
|
||||
|
||||
|
108
v2/internal/frontend/desktop/windows/winc/app.go
Normal file
108
v2/internal/frontend/desktop/windows/winc/app.go
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
var (
|
||||
// resource compilation tool assigns app.ico ID of 3
|
||||
// rsrc -manifest app.manifest -ico app.ico -o rsrc.syso
|
||||
AppIconID = 3
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
gAppInstance = w32.GetModuleHandle("")
|
||||
if gAppInstance == 0 {
|
||||
panic("Error occurred in App.Init")
|
||||
}
|
||||
|
||||
// Initialize the common controls
|
||||
var initCtrls w32.INITCOMMONCONTROLSEX
|
||||
initCtrls.DwSize = uint32(unsafe.Sizeof(initCtrls))
|
||||
initCtrls.DwICC =
|
||||
w32.ICC_LISTVIEW_CLASSES | w32.ICC_PROGRESS_CLASS | w32.ICC_TAB_CLASSES |
|
||||
w32.ICC_TREEVIEW_CLASSES | w32.ICC_BAR_CLASSES
|
||||
|
||||
w32.InitCommonControlsEx(&initCtrls)
|
||||
}
|
||||
|
||||
// SetAppIconID sets recource icon ID for the apps windows.
|
||||
func SetAppIcon(appIconID int) {
|
||||
AppIconID = appIconID
|
||||
}
|
||||
|
||||
func GetAppInstance() w32.HINSTANCE {
|
||||
return gAppInstance
|
||||
}
|
||||
|
||||
func PreTranslateMessage(msg *w32.MSG) bool {
|
||||
// This functions is called by the MessageLoop. It processes the
|
||||
// keyboard accelerator keys and calls Controller.PreTranslateMessage for
|
||||
// keyboard and mouse events.
|
||||
|
||||
processed := false
|
||||
|
||||
if (msg.Message >= w32.WM_KEYFIRST && msg.Message <= w32.WM_KEYLAST) ||
|
||||
(msg.Message >= w32.WM_MOUSEFIRST && msg.Message <= w32.WM_MOUSELAST) {
|
||||
|
||||
if msg.Hwnd != 0 {
|
||||
if controller := GetMsgHandler(msg.Hwnd); controller != nil {
|
||||
// Search the chain of parents for pretranslated messages.
|
||||
for p := controller; p != nil; p = p.Parent() {
|
||||
|
||||
if processed = p.PreTranslateMessage(msg); processed {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return processed
|
||||
}
|
||||
|
||||
// RunMainLoop processes messages in main application loop.
|
||||
func RunMainLoop() int {
|
||||
m := (*w32.MSG)(unsafe.Pointer(w32.GlobalAlloc(0, uint32(unsafe.Sizeof(w32.MSG{})))))
|
||||
defer w32.GlobalFree(w32.HGLOBAL(unsafe.Pointer(m)))
|
||||
|
||||
for w32.GetMessage(m, 0, 0, 0) != 0 {
|
||||
|
||||
if !PreTranslateMessage(m) {
|
||||
w32.TranslateMessage(m)
|
||||
w32.DispatchMessage(m)
|
||||
}
|
||||
}
|
||||
|
||||
w32.GdiplusShutdown()
|
||||
return int(m.WParam)
|
||||
}
|
||||
|
||||
// PostMessages processes recent messages. Sometimes helpful for instant window refresh.
|
||||
func PostMessages() {
|
||||
m := (*w32.MSG)(unsafe.Pointer(w32.GlobalAlloc(0, uint32(unsafe.Sizeof(w32.MSG{})))))
|
||||
defer w32.GlobalFree(w32.HGLOBAL(unsafe.Pointer(m)))
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
if w32.GetMessage(m, 0, 0, 0) != 0 {
|
||||
if !PreTranslateMessage(m) {
|
||||
w32.TranslateMessage(m)
|
||||
w32.DispatchMessage(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Exit() {
|
||||
w32.PostQuitMessage(0)
|
||||
}
|
110
v2/internal/frontend/desktop/windows/winc/bitmap.go
Normal file
110
v2/internal/frontend/desktop/windows/winc/bitmap.go
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type Bitmap struct {
|
||||
handle w32.HBITMAP
|
||||
width, height int
|
||||
}
|
||||
|
||||
func assembleBitmapFromHBITMAP(hbitmap w32.HBITMAP) (*Bitmap, error) {
|
||||
var dib w32.DIBSECTION
|
||||
if w32.GetObject(w32.HGDIOBJ(hbitmap), unsafe.Sizeof(dib), unsafe.Pointer(&dib)) == 0 {
|
||||
return nil, errors.New("GetObject for HBITMAP failed")
|
||||
}
|
||||
|
||||
return &Bitmap{
|
||||
handle: hbitmap,
|
||||
width: int(dib.DsBmih.BiWidth),
|
||||
height: int(dib.DsBmih.BiHeight),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewBitmapFromFile(filepath string, background Color) (*Bitmap, error) {
|
||||
var gpBitmap *uintptr
|
||||
var err error
|
||||
|
||||
gpBitmap, err = w32.GdipCreateBitmapFromFile(filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer w32.GdipDisposeImage(gpBitmap)
|
||||
|
||||
var hbitmap w32.HBITMAP
|
||||
// Reverse RGB to BGR to satisfy gdiplus color schema.
|
||||
hbitmap, err = w32.GdipCreateHBITMAPFromBitmap(gpBitmap, uint32(RGB(background.B(), background.G(), background.R())))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return assembleBitmapFromHBITMAP(hbitmap)
|
||||
}
|
||||
|
||||
func NewBitmapFromResource(instance w32.HINSTANCE, resName *uint16, resType *uint16, background Color) (*Bitmap, error) {
|
||||
var gpBitmap *uintptr
|
||||
var err error
|
||||
var hRes w32.HRSRC
|
||||
|
||||
hRes, err = w32.FindResource(w32.HMODULE(instance), resName, resType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resSize := w32.SizeofResource(w32.HMODULE(instance), hRes)
|
||||
pResData := w32.LockResource(w32.LoadResource(w32.HMODULE(instance), hRes))
|
||||
resBuffer := w32.GlobalAlloc(w32.GMEM_MOVEABLE, resSize)
|
||||
pResBuffer := w32.GlobalLock(resBuffer)
|
||||
w32.MoveMemory(pResBuffer, pResData, resSize)
|
||||
|
||||
stream := w32.CreateStreamOnHGlobal(resBuffer, false)
|
||||
|
||||
gpBitmap, err = w32.GdipCreateBitmapFromStream(stream)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stream.Release()
|
||||
defer w32.GlobalUnlock(resBuffer)
|
||||
defer w32.GlobalFree(resBuffer)
|
||||
defer w32.GdipDisposeImage(gpBitmap)
|
||||
|
||||
var hbitmap w32.HBITMAP
|
||||
// Reverse gform.RGB to BGR to satisfy gdiplus color schema.
|
||||
hbitmap, err = w32.GdipCreateHBITMAPFromBitmap(gpBitmap, uint32(RGB(background.B(), background.G(), background.R())))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return assembleBitmapFromHBITMAP(hbitmap)
|
||||
}
|
||||
|
||||
func (bm *Bitmap) Dispose() {
|
||||
if bm.handle != 0 {
|
||||
w32.DeleteObject(w32.HGDIOBJ(bm.handle))
|
||||
bm.handle = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (bm *Bitmap) GetHBITMAP() w32.HBITMAP {
|
||||
return bm.handle
|
||||
}
|
||||
|
||||
func (bm *Bitmap) Size() (int, int) {
|
||||
return bm.width, bm.height
|
||||
}
|
||||
|
||||
func (bm *Bitmap) Height() int {
|
||||
return bm.height
|
||||
}
|
||||
|
||||
func (bm *Bitmap) Width() int {
|
||||
return bm.width
|
||||
}
|
72
v2/internal/frontend/desktop/windows/winc/brush.go
Normal file
72
v2/internal/frontend/desktop/windows/winc/brush.go
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
var DefaultBackgroundBrush = NewSystemColorBrush(w32.COLOR_BTNFACE)
|
||||
|
||||
type Brush struct {
|
||||
hBrush w32.HBRUSH
|
||||
logBrush w32.LOGBRUSH
|
||||
}
|
||||
|
||||
func NewSolidColorBrush(color Color) *Brush {
|
||||
lb := w32.LOGBRUSH{LbStyle: w32.BS_SOLID, LbColor: w32.COLORREF(color)}
|
||||
hBrush := w32.CreateBrushIndirect(&lb)
|
||||
if hBrush == 0 {
|
||||
panic("Faild to create solid color brush")
|
||||
}
|
||||
|
||||
return &Brush{hBrush, lb}
|
||||
}
|
||||
|
||||
func NewSystemColorBrush(colorIndex int) *Brush {
|
||||
//lb := w32.LOGBRUSH{LbStyle: w32.BS_SOLID, LbColor: w32.COLORREF(colorIndex)}
|
||||
lb := w32.LOGBRUSH{LbStyle: w32.BS_NULL}
|
||||
hBrush := w32.GetSysColorBrush(colorIndex)
|
||||
if hBrush == 0 {
|
||||
panic("GetSysColorBrush failed")
|
||||
}
|
||||
return &Brush{hBrush, lb}
|
||||
}
|
||||
|
||||
func NewHatchedColorBrush(color Color) *Brush {
|
||||
lb := w32.LOGBRUSH{LbStyle: w32.BS_HATCHED, LbColor: w32.COLORREF(color)}
|
||||
hBrush := w32.CreateBrushIndirect(&lb)
|
||||
if hBrush == 0 {
|
||||
panic("Faild to create solid color brush")
|
||||
}
|
||||
|
||||
return &Brush{hBrush, lb}
|
||||
}
|
||||
|
||||
func NewNullBrush() *Brush {
|
||||
lb := w32.LOGBRUSH{LbStyle: w32.BS_NULL}
|
||||
hBrush := w32.CreateBrushIndirect(&lb)
|
||||
if hBrush == 0 {
|
||||
panic("Failed to create null brush")
|
||||
}
|
||||
|
||||
return &Brush{hBrush, lb}
|
||||
}
|
||||
|
||||
func (br *Brush) GetHBRUSH() w32.HBRUSH {
|
||||
return br.hBrush
|
||||
}
|
||||
|
||||
func (br *Brush) GetLOGBRUSH() *w32.LOGBRUSH {
|
||||
return &br.logBrush
|
||||
}
|
||||
|
||||
func (br *Brush) Dispose() {
|
||||
if br.hBrush != 0 {
|
||||
w32.DeleteObject(w32.HGDIOBJ(br.hBrush))
|
||||
br.hBrush = 0
|
||||
}
|
||||
}
|
154
v2/internal/frontend/desktop/windows/winc/buttons.go
Normal file
154
v2/internal/frontend/desktop/windows/winc/buttons.go
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type Button struct {
|
||||
ControlBase
|
||||
onClick EventManager
|
||||
}
|
||||
|
||||
func (bt *Button) OnClick() *EventManager {
|
||||
return &bt.onClick
|
||||
}
|
||||
|
||||
func (bt *Button) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_COMMAND:
|
||||
bt.onClick.Fire(NewEvent(bt, nil))
|
||||
/*case w32.WM_LBUTTONDOWN:
|
||||
w32.SetCapture(bt.Handle())
|
||||
case w32.WM_LBUTTONUP:
|
||||
w32.ReleaseCapture()*/
|
||||
/*case win.WM_GETDLGCODE:
|
||||
println("GETDLGCODE")*/
|
||||
}
|
||||
return w32.DefWindowProc(bt.hwnd, msg, wparam, lparam)
|
||||
//return bt.W32Control.WndProc(msg, wparam, lparam)
|
||||
}
|
||||
|
||||
func (bt *Button) Checked() bool {
|
||||
result := w32.SendMessage(bt.hwnd, w32.BM_GETCHECK, 0, 0)
|
||||
return result == w32.BST_CHECKED
|
||||
}
|
||||
|
||||
func (bt *Button) SetChecked(checked bool) {
|
||||
wparam := w32.BST_CHECKED
|
||||
if !checked {
|
||||
wparam = w32.BST_UNCHECKED
|
||||
}
|
||||
w32.SendMessage(bt.hwnd, w32.BM_SETCHECK, uintptr(wparam), 0)
|
||||
}
|
||||
|
||||
// SetIcon sets icon on the button. Recommended icons are 32x32 with 32bit color depth.
|
||||
func (bt *Button) SetIcon(ico *Icon) {
|
||||
w32.SendMessage(bt.hwnd, w32.BM_SETIMAGE, w32.IMAGE_ICON, uintptr(ico.handle))
|
||||
}
|
||||
|
||||
func (bt *Button) SetResIcon(iconID uint16) {
|
||||
if ico, err := NewIconFromResource(GetAppInstance(), iconID); err == nil {
|
||||
bt.SetIcon(ico)
|
||||
return
|
||||
}
|
||||
panic(fmt.Sprintf("missing icon with icon ID: %d", iconID))
|
||||
}
|
||||
|
||||
type PushButton struct {
|
||||
Button
|
||||
}
|
||||
|
||||
func NewPushButton(parent Controller) *PushButton {
|
||||
pb := new(PushButton)
|
||||
|
||||
pb.InitControl("BUTTON", parent, 0, w32.BS_PUSHBUTTON|w32.WS_TABSTOP|w32.WS_VISIBLE|w32.WS_CHILD)
|
||||
RegMsgHandler(pb)
|
||||
|
||||
pb.SetFont(DefaultFont)
|
||||
pb.SetText("Button")
|
||||
pb.SetSize(100, 22)
|
||||
|
||||
return pb
|
||||
}
|
||||
|
||||
// SetDefault is used for dialogs to set default button.
|
||||
func (pb *PushButton) SetDefault() {
|
||||
pb.SetAndClearStyleBits(w32.BS_DEFPUSHBUTTON, w32.BS_PUSHBUTTON)
|
||||
}
|
||||
|
||||
// IconButton does not display text, requires SetResIcon call.
|
||||
type IconButton struct {
|
||||
Button
|
||||
}
|
||||
|
||||
func NewIconButton(parent Controller) *IconButton {
|
||||
pb := new(IconButton)
|
||||
|
||||
pb.InitControl("BUTTON", parent, 0, w32.BS_ICON|w32.WS_TABSTOP|w32.WS_VISIBLE|w32.WS_CHILD)
|
||||
RegMsgHandler(pb)
|
||||
|
||||
pb.SetFont(DefaultFont)
|
||||
// even if text would be set it would not be displayed
|
||||
pb.SetText("")
|
||||
pb.SetSize(100, 22)
|
||||
|
||||
return pb
|
||||
}
|
||||
|
||||
type CheckBox struct {
|
||||
Button
|
||||
}
|
||||
|
||||
func NewCheckBox(parent Controller) *CheckBox {
|
||||
cb := new(CheckBox)
|
||||
|
||||
cb.InitControl("BUTTON", parent, 0, w32.WS_TABSTOP|w32.WS_VISIBLE|w32.WS_CHILD|w32.BS_AUTOCHECKBOX)
|
||||
RegMsgHandler(cb)
|
||||
|
||||
cb.SetFont(DefaultFont)
|
||||
cb.SetText("CheckBox")
|
||||
cb.SetSize(100, 22)
|
||||
|
||||
return cb
|
||||
}
|
||||
|
||||
type RadioButton struct {
|
||||
Button
|
||||
}
|
||||
|
||||
func NewRadioButton(parent Controller) *RadioButton {
|
||||
rb := new(RadioButton)
|
||||
|
||||
rb.InitControl("BUTTON", parent, 0, w32.WS_TABSTOP|w32.WS_VISIBLE|w32.WS_CHILD|w32.BS_AUTORADIOBUTTON)
|
||||
RegMsgHandler(rb)
|
||||
|
||||
rb.SetFont(DefaultFont)
|
||||
rb.SetText("RadioButton")
|
||||
rb.SetSize(100, 22)
|
||||
|
||||
return rb
|
||||
}
|
||||
|
||||
type GroupBox struct {
|
||||
Button
|
||||
}
|
||||
|
||||
func NewGroupBox(parent Controller) *GroupBox {
|
||||
gb := new(GroupBox)
|
||||
|
||||
gb.InitControl("BUTTON", parent, 0, w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_GROUP|w32.BS_GROUPBOX)
|
||||
RegMsgHandler(gb)
|
||||
|
||||
gb.SetFont(DefaultFont)
|
||||
gb.SetText("GroupBox")
|
||||
gb.SetSize(100, 100)
|
||||
|
||||
return gb
|
||||
}
|
157
v2/internal/frontend/desktop/windows/winc/canvas.go
Normal file
157
v2/internal/frontend/desktop/windows/winc/canvas.go
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type Canvas struct {
|
||||
hwnd w32.HWND
|
||||
hdc w32.HDC
|
||||
doNotDispose bool
|
||||
}
|
||||
|
||||
var nullBrush = NewNullBrush()
|
||||
|
||||
func NewCanvasFromHwnd(hwnd w32.HWND) *Canvas {
|
||||
hdc := w32.GetDC(hwnd)
|
||||
if hdc == 0 {
|
||||
panic(fmt.Sprintf("Create canvas from %v failed.", hwnd))
|
||||
}
|
||||
|
||||
return &Canvas{hwnd: hwnd, hdc: hdc, doNotDispose: false}
|
||||
}
|
||||
|
||||
func NewCanvasFromHDC(hdc w32.HDC) *Canvas {
|
||||
if hdc == 0 {
|
||||
panic("Cannot create canvas from invalid HDC.")
|
||||
}
|
||||
|
||||
return &Canvas{hdc: hdc, doNotDispose: true}
|
||||
}
|
||||
|
||||
func (ca *Canvas) Dispose() {
|
||||
if !ca.doNotDispose && ca.hdc != 0 {
|
||||
if ca.hwnd == 0 {
|
||||
w32.DeleteDC(ca.hdc)
|
||||
} else {
|
||||
w32.ReleaseDC(ca.hwnd, ca.hdc)
|
||||
}
|
||||
|
||||
ca.hdc = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (ca *Canvas) DrawBitmap(bmp *Bitmap, x, y int) {
|
||||
cdc := w32.CreateCompatibleDC(0)
|
||||
defer w32.DeleteDC(cdc)
|
||||
|
||||
hbmpOld := w32.SelectObject(cdc, w32.HGDIOBJ(bmp.GetHBITMAP()))
|
||||
defer w32.SelectObject(cdc, w32.HGDIOBJ(hbmpOld))
|
||||
|
||||
w, h := bmp.Size()
|
||||
|
||||
w32.BitBlt(ca.hdc, x, y, w, h, cdc, 0, 0, w32.SRCCOPY)
|
||||
}
|
||||
|
||||
func (ca *Canvas) DrawStretchedBitmap(bmp *Bitmap, rect *Rect) {
|
||||
cdc := w32.CreateCompatibleDC(0)
|
||||
defer w32.DeleteDC(cdc)
|
||||
|
||||
hbmpOld := w32.SelectObject(cdc, w32.HGDIOBJ(bmp.GetHBITMAP()))
|
||||
defer w32.SelectObject(cdc, w32.HGDIOBJ(hbmpOld))
|
||||
|
||||
w, h := bmp.Size()
|
||||
|
||||
rc := rect.GetW32Rect()
|
||||
w32.StretchBlt(ca.hdc, int(rc.Left), int(rc.Top), int(rc.Right), int(rc.Bottom), cdc, 0, 0, w, h, w32.SRCCOPY)
|
||||
}
|
||||
|
||||
func (ca *Canvas) DrawIcon(ico *Icon, x, y int) bool {
|
||||
return w32.DrawIcon(ca.hdc, x, y, ico.Handle())
|
||||
}
|
||||
|
||||
// DrawFillRect draw and fill rectangle with color.
|
||||
func (ca *Canvas) DrawFillRect(rect *Rect, pen *Pen, brush *Brush) {
|
||||
w32Rect := rect.GetW32Rect()
|
||||
|
||||
previousPen := w32.SelectObject(ca.hdc, w32.HGDIOBJ(pen.GetHPEN()))
|
||||
defer w32.SelectObject(ca.hdc, previousPen)
|
||||
|
||||
previousBrush := w32.SelectObject(ca.hdc, w32.HGDIOBJ(brush.GetHBRUSH()))
|
||||
defer w32.SelectObject(ca.hdc, previousBrush)
|
||||
|
||||
w32.Rectangle(ca.hdc, w32Rect.Left, w32Rect.Top, w32Rect.Right, w32Rect.Bottom)
|
||||
}
|
||||
|
||||
func (ca *Canvas) DrawRect(rect *Rect, pen *Pen) {
|
||||
w32Rect := rect.GetW32Rect()
|
||||
|
||||
previousPen := w32.SelectObject(ca.hdc, w32.HGDIOBJ(pen.GetHPEN()))
|
||||
defer w32.SelectObject(ca.hdc, previousPen)
|
||||
|
||||
// nullBrush is used to make interior of the rect transparent
|
||||
previousBrush := w32.SelectObject(ca.hdc, w32.HGDIOBJ(nullBrush.GetHBRUSH()))
|
||||
defer w32.SelectObject(ca.hdc, previousBrush)
|
||||
|
||||
w32.Rectangle(ca.hdc, w32Rect.Left, w32Rect.Top, w32Rect.Right, w32Rect.Bottom)
|
||||
}
|
||||
|
||||
func (ca *Canvas) FillRect(rect *Rect, brush *Brush) {
|
||||
w32.FillRect(ca.hdc, rect.GetW32Rect(), brush.GetHBRUSH())
|
||||
}
|
||||
|
||||
func (ca *Canvas) DrawEllipse(rect *Rect, pen *Pen) {
|
||||
w32Rect := rect.GetW32Rect()
|
||||
|
||||
previousPen := w32.SelectObject(ca.hdc, w32.HGDIOBJ(pen.GetHPEN()))
|
||||
defer w32.SelectObject(ca.hdc, previousPen)
|
||||
|
||||
// nullBrush is used to make interior of the rect transparent
|
||||
previousBrush := w32.SelectObject(ca.hdc, w32.HGDIOBJ(nullBrush.GetHBRUSH()))
|
||||
defer w32.SelectObject(ca.hdc, previousBrush)
|
||||
|
||||
w32.Ellipse(ca.hdc, w32Rect.Left, w32Rect.Top, w32Rect.Right, w32Rect.Bottom)
|
||||
}
|
||||
|
||||
// DrawFillEllipse draw and fill ellipse with color.
|
||||
func (ca *Canvas) DrawFillEllipse(rect *Rect, pen *Pen, brush *Brush) {
|
||||
w32Rect := rect.GetW32Rect()
|
||||
|
||||
previousPen := w32.SelectObject(ca.hdc, w32.HGDIOBJ(pen.GetHPEN()))
|
||||
defer w32.SelectObject(ca.hdc, previousPen)
|
||||
|
||||
previousBrush := w32.SelectObject(ca.hdc, w32.HGDIOBJ(brush.GetHBRUSH()))
|
||||
defer w32.SelectObject(ca.hdc, previousBrush)
|
||||
|
||||
w32.Ellipse(ca.hdc, w32Rect.Left, w32Rect.Top, w32Rect.Right, w32Rect.Bottom)
|
||||
}
|
||||
|
||||
func (ca *Canvas) DrawLine(x, y, x2, y2 int, pen *Pen) {
|
||||
w32.MoveToEx(ca.hdc, x, y, nil)
|
||||
|
||||
previousPen := w32.SelectObject(ca.hdc, w32.HGDIOBJ(pen.GetHPEN()))
|
||||
defer w32.SelectObject(ca.hdc, previousPen)
|
||||
|
||||
w32.LineTo(ca.hdc, int32(x2), int32(y2))
|
||||
}
|
||||
|
||||
// Refer win32 DrawText document for uFormat.
|
||||
func (ca *Canvas) DrawText(text string, rect *Rect, format uint, font *Font, textColor Color) {
|
||||
previousFont := w32.SelectObject(ca.hdc, w32.HGDIOBJ(font.GetHFONT()))
|
||||
defer w32.SelectObject(ca.hdc, w32.HGDIOBJ(previousFont))
|
||||
|
||||
previousBkMode := w32.SetBkMode(ca.hdc, w32.TRANSPARENT)
|
||||
defer w32.SetBkMode(ca.hdc, previousBkMode)
|
||||
|
||||
previousTextColor := w32.SetTextColor(ca.hdc, w32.COLORREF(textColor))
|
||||
defer w32.SetTextColor(ca.hdc, previousTextColor)
|
||||
|
||||
w32.DrawText(ca.hdc, text, len(text), rect.GetW32Rect(), format)
|
||||
}
|
24
v2/internal/frontend/desktop/windows/winc/color.go
Normal file
24
v2/internal/frontend/desktop/windows/winc/color.go
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
type Color uint32
|
||||
|
||||
func RGB(r, g, b byte) Color {
|
||||
return Color(uint32(r) | uint32(g)<<8 | uint32(b)<<16)
|
||||
}
|
||||
|
||||
func (c Color) R() byte {
|
||||
return byte(c & 0xff)
|
||||
}
|
||||
|
||||
func (c Color) G() byte {
|
||||
return byte((c >> 8) & 0xff)
|
||||
}
|
||||
|
||||
func (c Color) B() byte {
|
||||
return byte((c >> 16) & 0xff)
|
||||
}
|
68
v2/internal/frontend/desktop/windows/winc/combobox.go
Normal file
68
v2/internal/frontend/desktop/windows/winc/combobox.go
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type ComboBox struct {
|
||||
ControlBase
|
||||
onSelectedChange EventManager
|
||||
}
|
||||
|
||||
func NewComboBox(parent Controller) *ComboBox {
|
||||
cb := new(ComboBox)
|
||||
|
||||
cb.InitControl("COMBOBOX", parent, 0, w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_TABSTOP|w32.WS_VSCROLL|w32.CBS_DROPDOWNLIST)
|
||||
RegMsgHandler(cb)
|
||||
|
||||
cb.SetFont(DefaultFont)
|
||||
cb.SetSize(200, 400)
|
||||
return cb
|
||||
}
|
||||
|
||||
func (cb *ComboBox) DeleteAllItems() bool {
|
||||
return w32.SendMessage(cb.hwnd, w32.CB_RESETCONTENT, 0, 0) == w32.TRUE
|
||||
}
|
||||
|
||||
func (cb *ComboBox) InsertItem(index int, str string) bool {
|
||||
lp := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))
|
||||
return w32.SendMessage(cb.hwnd, w32.CB_INSERTSTRING, uintptr(index), lp) != w32.CB_ERR
|
||||
}
|
||||
|
||||
func (cb *ComboBox) DeleteItem(index int) bool {
|
||||
return w32.SendMessage(cb.hwnd, w32.CB_DELETESTRING, uintptr(index), 0) != w32.CB_ERR
|
||||
}
|
||||
|
||||
func (cb *ComboBox) SelectedItem() int {
|
||||
return int(int32(w32.SendMessage(cb.hwnd, w32.CB_GETCURSEL, 0, 0)))
|
||||
}
|
||||
|
||||
func (cb *ComboBox) SetSelectedItem(value int) bool {
|
||||
return int(int32(w32.SendMessage(cb.hwnd, w32.CB_SETCURSEL, uintptr(value), 0))) == value
|
||||
}
|
||||
|
||||
func (cb *ComboBox) OnSelectedChange() *EventManager {
|
||||
return &cb.onSelectedChange
|
||||
}
|
||||
|
||||
// Message processer
|
||||
func (cb *ComboBox) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_COMMAND:
|
||||
code := w32.HIWORD(uint32(wparam))
|
||||
|
||||
switch code {
|
||||
case w32.CBN_SELCHANGE:
|
||||
cb.onSelectedChange.Fire(NewEvent(cb, nil))
|
||||
}
|
||||
}
|
||||
return w32.DefWindowProc(cb.hwnd, msg, wparam, lparam)
|
||||
//return cb.W32Control.WndProc(msg, wparam, lparam)
|
||||
}
|
123
v2/internal/frontend/desktop/windows/winc/commondlgs.go
Normal file
123
v2/internal/frontend/desktop/windows/winc/commondlgs.go
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
func genOFN(parent Controller, title, filter string, filterIndex uint, initialDir string, buf []uint16) *w32.OPENFILENAME {
|
||||
var ofn w32.OPENFILENAME
|
||||
ofn.StructSize = uint32(unsafe.Sizeof(ofn))
|
||||
ofn.Owner = parent.Handle()
|
||||
|
||||
if filter != "" {
|
||||
filterBuf := make([]uint16, len(filter)+1)
|
||||
copy(filterBuf, syscall.StringToUTF16(filter))
|
||||
// Replace '|' with the expected '\0'
|
||||
for i, c := range filterBuf {
|
||||
if byte(c) == '|' {
|
||||
filterBuf[i] = uint16(0)
|
||||
}
|
||||
}
|
||||
ofn.Filter = &filterBuf[0]
|
||||
ofn.FilterIndex = uint32(filterIndex)
|
||||
}
|
||||
|
||||
ofn.File = &buf[0]
|
||||
ofn.MaxFile = uint32(len(buf))
|
||||
|
||||
if initialDir != "" {
|
||||
ofn.InitialDir = syscall.StringToUTF16Ptr(initialDir)
|
||||
}
|
||||
if title != "" {
|
||||
ofn.Title = syscall.StringToUTF16Ptr(title)
|
||||
}
|
||||
|
||||
ofn.Flags = w32.OFN_FILEMUSTEXIST
|
||||
return &ofn
|
||||
}
|
||||
|
||||
func ShowOpenFileDlg(parent Controller, title, filter string, filterIndex uint, initialDir string) (filePath string, accepted bool) {
|
||||
buf := make([]uint16, 1024)
|
||||
ofn := genOFN(parent, title, filter, filterIndex, initialDir, buf)
|
||||
|
||||
if accepted = w32.GetOpenFileName(ofn); accepted {
|
||||
filePath = syscall.UTF16ToString(buf)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ShowSaveFileDlg(parent Controller, title, filter string, filterIndex uint, initialDir string) (filePath string, accepted bool) {
|
||||
buf := make([]uint16, 1024)
|
||||
ofn := genOFN(parent, title, filter, filterIndex, initialDir, buf)
|
||||
|
||||
if accepted = w32.GetSaveFileName(ofn); accepted {
|
||||
filePath = syscall.UTF16ToString(buf)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ShowBrowseFolderDlg(parent Controller, title string) (folder string, accepted bool) {
|
||||
var bi w32.BROWSEINFO
|
||||
bi.Owner = parent.Handle()
|
||||
bi.Title = syscall.StringToUTF16Ptr(title)
|
||||
bi.Flags = w32.BIF_RETURNONLYFSDIRS | w32.BIF_NEWDIALOGSTYLE
|
||||
|
||||
w32.CoInitialize()
|
||||
ret := w32.SHBrowseForFolder(&bi)
|
||||
w32.CoUninitialize()
|
||||
|
||||
folder = w32.SHGetPathFromIDList(ret)
|
||||
accepted = folder != ""
|
||||
return
|
||||
}
|
||||
|
||||
// MsgBoxOkCancel basic pop up message. Returns 1 for OK and 2 for CANCEL.
|
||||
func MsgBoxOkCancel(parent Controller, title, caption string) int {
|
||||
return MsgBox(parent, title, caption, w32.MB_ICONEXCLAMATION|w32.MB_OKCANCEL)
|
||||
}
|
||||
|
||||
func MsgBoxYesNo(parent Controller, title, caption string) int {
|
||||
return MsgBox(parent, title, caption, w32.MB_ICONEXCLAMATION|w32.MB_YESNO)
|
||||
}
|
||||
|
||||
func MsgBoxOk(parent Controller, title, caption string) {
|
||||
MsgBox(parent, title, caption, w32.MB_ICONINFORMATION|w32.MB_OK)
|
||||
}
|
||||
|
||||
// Warningf is generic warning message with OK and Cancel buttons. Returns 1 for OK.
|
||||
func Warningf(parent Controller, format string, data ...interface{}) int {
|
||||
caption := fmt.Sprintf(format, data...)
|
||||
return MsgBox(parent, "Warning", caption, w32.MB_ICONWARNING|w32.MB_OKCANCEL)
|
||||
}
|
||||
|
||||
// Printf is generic info message with OK button.
|
||||
func Printf(parent Controller, format string, data ...interface{}) {
|
||||
caption := fmt.Sprintf(format, data...)
|
||||
MsgBox(parent, "Information", caption, w32.MB_ICONINFORMATION|w32.MB_OK)
|
||||
}
|
||||
|
||||
// Errorf is generic error message with OK button.
|
||||
func Errorf(parent Controller, format string, data ...interface{}) {
|
||||
caption := fmt.Sprintf(format, data...)
|
||||
MsgBox(parent, "Error", caption, w32.MB_ICONERROR|w32.MB_OK)
|
||||
}
|
||||
|
||||
func MsgBox(parent Controller, title, caption string, flags uint) int {
|
||||
var result int
|
||||
if parent != nil {
|
||||
result = w32.MessageBox(parent.Handle(), caption, title, flags)
|
||||
} else {
|
||||
result = w32.MessageBox(0, caption, title, flags)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
518
v2/internal/frontend/desktop/windows/winc/controlbase.go
Normal file
518
v2/internal/frontend/desktop/windows/winc/controlbase.go
Normal file
@ -0,0 +1,518 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type ControlBase struct {
|
||||
hwnd w32.HWND
|
||||
font *Font
|
||||
parent Controller
|
||||
contextMenu *MenuItem
|
||||
|
||||
isForm bool
|
||||
|
||||
minWidth, minHeight int
|
||||
maxWidth, maxHeight int
|
||||
|
||||
// General events
|
||||
onCreate EventManager
|
||||
onClose EventManager
|
||||
|
||||
// Focus events
|
||||
onKillFocus EventManager
|
||||
onSetFocus EventManager
|
||||
|
||||
// Drag and drop events
|
||||
onDropFiles EventManager
|
||||
|
||||
// Mouse events
|
||||
onLBDown EventManager
|
||||
onLBUp EventManager
|
||||
onLBDbl EventManager
|
||||
onMBDown EventManager
|
||||
onMBUp EventManager
|
||||
onRBDown EventManager
|
||||
onRBUp EventManager
|
||||
onRBDbl EventManager
|
||||
onMouseMove EventManager
|
||||
|
||||
// use MouseControl to capture onMouseHover and onMouseLeave events.
|
||||
onMouseHover EventManager
|
||||
onMouseLeave EventManager
|
||||
|
||||
// Keyboard events
|
||||
onKeyUp EventManager
|
||||
|
||||
// Paint events
|
||||
onPaint EventManager
|
||||
onSize EventManager
|
||||
|
||||
m sync.Mutex
|
||||
dispatchq []func()
|
||||
}
|
||||
|
||||
// initControl is called by controls: edit, button, treeview, listview, and so on.
|
||||
func (cba *ControlBase) InitControl(className string, parent Controller, exstyle, style uint) {
|
||||
cba.hwnd = CreateWindow(className, parent, exstyle, style)
|
||||
if cba.hwnd == 0 {
|
||||
panic("cannot create window for " + className)
|
||||
}
|
||||
cba.parent = parent
|
||||
}
|
||||
|
||||
// InitWindow is called by custom window based controls such as split, panel, etc.
|
||||
func (cba *ControlBase) InitWindow(className string, parent Controller, exstyle, style uint) {
|
||||
RegClassOnlyOnce(className)
|
||||
cba.hwnd = CreateWindow(className, parent, exstyle, style)
|
||||
if cba.hwnd == 0 {
|
||||
panic("cannot create window for " + className)
|
||||
}
|
||||
cba.parent = parent
|
||||
}
|
||||
|
||||
// SetTheme for TreeView and ListView controls.
|
||||
func (cba *ControlBase) SetTheme(appName string) error {
|
||||
if hr := w32.SetWindowTheme(cba.hwnd, syscall.StringToUTF16Ptr(appName), nil); w32.FAILED(hr) {
|
||||
return fmt.Errorf("SetWindowTheme %d", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Handle() w32.HWND {
|
||||
return cba.hwnd
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetHandle(hwnd w32.HWND) {
|
||||
cba.hwnd = hwnd
|
||||
}
|
||||
|
||||
func (cba *ControlBase) GetWindowDPI() (w32.UINT, w32.UINT) {
|
||||
if w32.HasGetDpiForWindowFunc() {
|
||||
// GetDpiForWindow is supported beginning with Windows 10, 1607 and is the most accureate
|
||||
// one, especially it is consistent with the WM_DPICHANGED event.
|
||||
dpi := w32.GetDpiForWindow(cba.hwnd)
|
||||
return dpi, dpi
|
||||
}
|
||||
|
||||
if w32.HasGetDPIForMonitorFunc() {
|
||||
// GetDpiForWindow is supported beginning with Windows 8.1
|
||||
monitor := w32.MonitorFromWindow(cba.hwnd, w32.MONITOR_DEFAULTTONEAREST)
|
||||
if monitor == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
var dpiX, dpiY w32.UINT
|
||||
w32.GetDPIForMonitor(monitor, w32.MDT_EFFECTIVE_DPI, &dpiX, &dpiY)
|
||||
return dpiX, dpiY
|
||||
}
|
||||
|
||||
// If none of the above is supported fallback to the System DPI.
|
||||
screen := w32.GetDC(0)
|
||||
x := w32.GetDeviceCaps(screen, w32.LOGPIXELSX)
|
||||
y := w32.GetDeviceCaps(screen, w32.LOGPIXELSY)
|
||||
w32.ReleaseDC(0, screen)
|
||||
return w32.UINT(x), w32.UINT(y)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetAndClearStyleBits(set, clear uint32) error {
|
||||
style := uint32(w32.GetWindowLong(cba.hwnd, w32.GWL_STYLE))
|
||||
if style == 0 {
|
||||
return fmt.Errorf("GetWindowLong")
|
||||
}
|
||||
|
||||
if newStyle := style&^clear | set; newStyle != style {
|
||||
if w32.SetWindowLong(cba.hwnd, w32.GWL_STYLE, newStyle) == 0 {
|
||||
return fmt.Errorf("SetWindowLong")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetIsForm(isform bool) {
|
||||
cba.isForm = isform
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetText(caption string) {
|
||||
w32.SetWindowText(cba.hwnd, caption)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Text() string {
|
||||
return w32.GetWindowText(cba.hwnd)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Close() {
|
||||
UnRegMsgHandler(cba.hwnd)
|
||||
w32.DestroyWindow(cba.hwnd)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetTranslucentBackground() {
|
||||
var accent = w32.ACCENT_POLICY{
|
||||
AccentState: w32.ACCENT_ENABLE_BLURBEHIND,
|
||||
}
|
||||
var data w32.WINDOWCOMPOSITIONATTRIBDATA
|
||||
data.Attrib = w32.WCA_ACCENT_POLICY
|
||||
data.PvData = unsafe.Pointer(&accent)
|
||||
data.CbData = unsafe.Sizeof(accent)
|
||||
|
||||
w32.SetWindowCompositionAttribute(cba.hwnd, &data)
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (cba *ControlBase) clampSize(width, height int) (int, int) {
|
||||
if cba.minWidth != 0 {
|
||||
width = max(width, cba.minWidth)
|
||||
}
|
||||
if cba.maxWidth != 0 {
|
||||
width = min(width, cba.maxWidth)
|
||||
}
|
||||
if cba.minHeight != 0 {
|
||||
height = max(height, cba.minHeight)
|
||||
}
|
||||
if cba.maxHeight != 0 {
|
||||
height = min(height, cba.maxHeight)
|
||||
}
|
||||
return width, height
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetSize(width, height int) {
|
||||
x, y := cba.Pos()
|
||||
width, height = cba.clampSize(width, height)
|
||||
width, height = cba.scaleWithWindowDPI(width, height)
|
||||
w32.MoveWindow(cba.hwnd, x, y, width, height, true)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetMinSize(width, height int) {
|
||||
cba.minWidth = width
|
||||
cba.minHeight = height
|
||||
|
||||
// Ensure we set max if min > max
|
||||
if cba.maxWidth > 0 {
|
||||
cba.maxWidth = max(cba.minWidth, cba.maxWidth)
|
||||
}
|
||||
if cba.maxHeight > 0 {
|
||||
cba.maxHeight = max(cba.minHeight, cba.maxHeight)
|
||||
}
|
||||
|
||||
x, y := cba.Pos()
|
||||
currentWidth, currentHeight := cba.Size()
|
||||
clampedWidth, clampedHeight := cba.clampSize(currentWidth, currentHeight)
|
||||
if clampedWidth != currentWidth || clampedHeight != currentHeight {
|
||||
w32.MoveWindow(cba.hwnd, x, y, clampedWidth, clampedHeight, true)
|
||||
}
|
||||
}
|
||||
func (cba *ControlBase) SetMaxSize(width, height int) {
|
||||
cba.maxWidth = width
|
||||
cba.maxHeight = height
|
||||
|
||||
// Ensure we set min if max > min
|
||||
if cba.maxWidth > 0 {
|
||||
cba.minWidth = min(cba.maxWidth, cba.minWidth)
|
||||
}
|
||||
if cba.maxHeight > 0 {
|
||||
cba.minHeight = min(cba.maxHeight, cba.minHeight)
|
||||
}
|
||||
|
||||
x, y := cba.Pos()
|
||||
currentWidth, currentHeight := cba.Size()
|
||||
clampedWidth, clampedHeight := cba.clampSize(currentWidth, currentHeight)
|
||||
if clampedWidth != currentWidth || clampedHeight != currentHeight {
|
||||
w32.MoveWindow(cba.hwnd, x, y, clampedWidth, clampedHeight, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Size() (width, height int) {
|
||||
rect := w32.GetWindowRect(cba.hwnd)
|
||||
width = int(rect.Right - rect.Left)
|
||||
height = int(rect.Bottom - rect.Top)
|
||||
return
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Width() int {
|
||||
rect := w32.GetWindowRect(cba.hwnd)
|
||||
return int(rect.Right - rect.Left)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Height() int {
|
||||
rect := w32.GetWindowRect(cba.hwnd)
|
||||
return int(rect.Bottom - rect.Top)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetPos(x, y int) {
|
||||
info := getMonitorInfo(cba.hwnd)
|
||||
workRect := info.RcWork
|
||||
|
||||
w32.SetWindowPos(cba.hwnd, w32.HWND_TOP, int(workRect.Left)+x, int(workRect.Top)+y, 0, 0, w32.SWP_NOSIZE)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Pos() (x, y int) {
|
||||
rect := w32.GetWindowRect(cba.hwnd)
|
||||
x = int(rect.Left)
|
||||
y = int(rect.Top)
|
||||
if !cba.isForm && cba.parent != nil {
|
||||
x, y, _ = w32.ScreenToClient(cba.parent.Handle(), x, y)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Visible() bool {
|
||||
return w32.IsWindowVisible(cba.hwnd)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) ToggleVisible() bool {
|
||||
visible := w32.IsWindowVisible(cba.hwnd)
|
||||
if visible {
|
||||
cba.Hide()
|
||||
} else {
|
||||
cba.Show()
|
||||
}
|
||||
return !visible
|
||||
}
|
||||
|
||||
func (cba *ControlBase) ContextMenu() *MenuItem {
|
||||
return cba.contextMenu
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetContextMenu(menu *MenuItem) {
|
||||
cba.contextMenu = menu
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Bounds() *Rect {
|
||||
rect := w32.GetWindowRect(cba.hwnd)
|
||||
if cba.isForm {
|
||||
return &Rect{*rect}
|
||||
}
|
||||
|
||||
return ScreenToClientRect(cba.hwnd, rect)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) ClientRect() *Rect {
|
||||
rect := w32.GetClientRect(cba.hwnd)
|
||||
return ScreenToClientRect(cba.hwnd, rect)
|
||||
}
|
||||
func (cba *ControlBase) ClientWidth() int {
|
||||
rect := w32.GetClientRect(cba.hwnd)
|
||||
return int(rect.Right - rect.Left)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) ClientHeight() int {
|
||||
rect := w32.GetClientRect(cba.hwnd)
|
||||
return int(rect.Bottom - rect.Top)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Show() {
|
||||
w32.ShowWindow(cba.hwnd, w32.SW_SHOWDEFAULT)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Hide() {
|
||||
w32.ShowWindow(cba.hwnd, w32.SW_HIDE)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Enabled() bool {
|
||||
return w32.IsWindowEnabled(cba.hwnd)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetEnabled(b bool) {
|
||||
w32.EnableWindow(cba.hwnd, b)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetFocus() {
|
||||
w32.SetFocus(cba.hwnd)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Invalidate(erase bool) {
|
||||
// pRect := w32.GetClientRect(cba.hwnd)
|
||||
// if cba.isForm {
|
||||
// w32.InvalidateRect(cba.hwnd, pRect, erase)
|
||||
// } else {
|
||||
// rc := ScreenToClientRect(cba.hwnd, pRect)
|
||||
// w32.InvalidateRect(cba.hwnd, rc.GetW32Rect(), erase)
|
||||
// }
|
||||
w32.InvalidateRect(cba.hwnd, nil, erase)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Parent() Controller {
|
||||
return cba.parent
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetParent(parent Controller) {
|
||||
cba.parent = parent
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Font() *Font {
|
||||
return cba.font
|
||||
}
|
||||
|
||||
func (cba *ControlBase) SetFont(font *Font) {
|
||||
w32.SendMessage(cba.hwnd, w32.WM_SETFONT, uintptr(font.hfont), 1)
|
||||
cba.font = font
|
||||
}
|
||||
|
||||
func (cba *ControlBase) EnableDragAcceptFiles(b bool) {
|
||||
w32.DragAcceptFiles(cba.hwnd, b)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) InvokeRequired() bool {
|
||||
if cba.hwnd == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
windowThreadId, _ := w32.GetWindowThreadProcessId(cba.hwnd)
|
||||
currentThreadId := w32.GetCurrentThreadId()
|
||||
|
||||
return windowThreadId != currentThreadId
|
||||
}
|
||||
|
||||
func (cba *ControlBase) Invoke(f func()) {
|
||||
if cba.tryInvokeOnCurrentGoRoutine(f) {
|
||||
return
|
||||
}
|
||||
|
||||
cba.m.Lock()
|
||||
cba.dispatchq = append(cba.dispatchq, f)
|
||||
cba.m.Unlock()
|
||||
w32.PostMessage(cba.hwnd, wmInvokeCallback, 0, 0)
|
||||
}
|
||||
|
||||
func (cba *ControlBase) PreTranslateMessage(msg *w32.MSG) bool {
|
||||
if msg.Message == w32.WM_GETDLGCODE {
|
||||
println("pretranslate, WM_GETDLGCODE")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//Events
|
||||
func (cba *ControlBase) OnCreate() *EventManager {
|
||||
return &cba.onCreate
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnClose() *EventManager {
|
||||
return &cba.onClose
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnKillFocus() *EventManager {
|
||||
return &cba.onKillFocus
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnSetFocus() *EventManager {
|
||||
return &cba.onSetFocus
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnDropFiles() *EventManager {
|
||||
return &cba.onDropFiles
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnLBDown() *EventManager {
|
||||
return &cba.onLBDown
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnLBUp() *EventManager {
|
||||
return &cba.onLBUp
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnLBDbl() *EventManager {
|
||||
return &cba.onLBDbl
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnMBDown() *EventManager {
|
||||
return &cba.onMBDown
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnMBUp() *EventManager {
|
||||
return &cba.onMBUp
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnRBDown() *EventManager {
|
||||
return &cba.onRBDown
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnRBUp() *EventManager {
|
||||
return &cba.onRBUp
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnRBDbl() *EventManager {
|
||||
return &cba.onRBDbl
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnMouseMove() *EventManager {
|
||||
return &cba.onMouseMove
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnMouseHover() *EventManager {
|
||||
return &cba.onMouseHover
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnMouseLeave() *EventManager {
|
||||
return &cba.onMouseLeave
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnPaint() *EventManager {
|
||||
return &cba.onPaint
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnSize() *EventManager {
|
||||
return &cba.onSize
|
||||
}
|
||||
|
||||
func (cba *ControlBase) OnKeyUp() *EventManager {
|
||||
return &cba.onKeyUp
|
||||
}
|
||||
|
||||
func (cba *ControlBase) scaleWithWindowDPI(width, height int) (int, int) {
|
||||
dpix, dpiy := cba.GetWindowDPI()
|
||||
scaledWidth := ScaleWithDPI(width, dpix)
|
||||
scaledHeight := ScaleWithDPI(height, dpiy)
|
||||
|
||||
return scaledWidth, scaledHeight
|
||||
}
|
||||
|
||||
func (cba *ControlBase) tryInvokeOnCurrentGoRoutine(f func()) bool {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
if cba.InvokeRequired() {
|
||||
return false
|
||||
}
|
||||
f()
|
||||
return true
|
||||
}
|
||||
|
||||
func (cba *ControlBase) invokeCallbacks() {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
if cba.InvokeRequired() {
|
||||
panic("InvokeCallbacks must always be called on the window thread")
|
||||
}
|
||||
|
||||
cba.m.Lock()
|
||||
q := append([]func(){}, cba.dispatchq...)
|
||||
cba.dispatchq = []func(){}
|
||||
cba.m.Unlock()
|
||||
for _, v := range q {
|
||||
v()
|
||||
}
|
||||
}
|
83
v2/internal/frontend/desktop/windows/winc/controller.go
Normal file
83
v2/internal/frontend/desktop/windows/winc/controller.go
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type Controller interface {
|
||||
Text() string
|
||||
|
||||
Enabled() bool
|
||||
SetFocus()
|
||||
|
||||
Handle() w32.HWND
|
||||
Invalidate(erase bool)
|
||||
Parent() Controller
|
||||
|
||||
Pos() (x, y int)
|
||||
Size() (w, h int)
|
||||
Height() int
|
||||
Width() int
|
||||
Visible() bool
|
||||
Bounds() *Rect
|
||||
ClientRect() *Rect
|
||||
|
||||
SetText(s string)
|
||||
SetEnabled(b bool)
|
||||
SetPos(x, y int)
|
||||
SetSize(w, h int)
|
||||
EnableDragAcceptFiles(b bool)
|
||||
Show()
|
||||
Hide()
|
||||
|
||||
ContextMenu() *MenuItem
|
||||
SetContextMenu(menu *MenuItem)
|
||||
|
||||
Font() *Font
|
||||
SetFont(font *Font)
|
||||
InvokeRequired() bool
|
||||
Invoke(func())
|
||||
PreTranslateMessage(msg *w32.MSG) bool
|
||||
WndProc(msg uint32, wparam, lparam uintptr) uintptr
|
||||
|
||||
//General events
|
||||
OnCreate() *EventManager
|
||||
OnClose() *EventManager
|
||||
|
||||
// Focus events
|
||||
OnKillFocus() *EventManager
|
||||
OnSetFocus() *EventManager
|
||||
|
||||
//Drag and drop events
|
||||
OnDropFiles() *EventManager
|
||||
|
||||
//Mouse events
|
||||
OnLBDown() *EventManager
|
||||
OnLBUp() *EventManager
|
||||
OnLBDbl() *EventManager
|
||||
OnMBDown() *EventManager
|
||||
OnMBUp() *EventManager
|
||||
OnRBDown() *EventManager
|
||||
OnRBUp() *EventManager
|
||||
OnRBDbl() *EventManager
|
||||
OnMouseMove() *EventManager
|
||||
|
||||
// OnMouseLeave and OnMouseHover does not fire unless control called internalTrackMouseEvent.
|
||||
// Use MouseControl for a how to example.
|
||||
OnMouseHover() *EventManager
|
||||
OnMouseLeave() *EventManager
|
||||
|
||||
//Keyboard events
|
||||
OnKeyUp() *EventManager
|
||||
|
||||
//Paint events
|
||||
OnPaint() *EventManager
|
||||
OnSize() *EventManager
|
||||
|
||||
invokeCallbacks()
|
||||
}
|
134
v2/internal/frontend/desktop/windows/winc/dialog.go
Normal file
134
v2/internal/frontend/desktop/windows/winc/dialog.go
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
|
||||
// Dialog displayed as z-order top window until closed.
|
||||
// It also disables parent window so it can not be clicked.
|
||||
type Dialog struct {
|
||||
Form
|
||||
isModal bool
|
||||
|
||||
btnOk *PushButton
|
||||
btnCancel *PushButton
|
||||
|
||||
onLoad EventManager
|
||||
onOk EventManager
|
||||
onCancel EventManager
|
||||
}
|
||||
|
||||
func NewDialog(parent Controller) *Dialog {
|
||||
dlg := new(Dialog)
|
||||
|
||||
dlg.isForm = true
|
||||
dlg.isModal = true
|
||||
RegClassOnlyOnce("winc_Dialog")
|
||||
|
||||
dlg.hwnd = CreateWindow("winc_Dialog", parent, w32.WS_EX_CONTROLPARENT, /* IMPORTANT */
|
||||
w32.WS_SYSMENU|w32.WS_CAPTION|w32.WS_THICKFRAME /*|w32.WS_BORDER|w32.WS_POPUP*/)
|
||||
dlg.parent = parent
|
||||
|
||||
// dlg might fail if icon resource is not embedded in the binary
|
||||
if ico, err := NewIconFromResource(GetAppInstance(), uint16(AppIconID)); err == nil {
|
||||
dlg.SetIcon(0, ico)
|
||||
}
|
||||
|
||||
// Dlg forces display of focus rectangles, as soon as the user starts to type.
|
||||
w32.SendMessage(dlg.hwnd, w32.WM_CHANGEUISTATE, w32.UIS_INITIALIZE, 0)
|
||||
RegMsgHandler(dlg)
|
||||
|
||||
dlg.SetFont(DefaultFont)
|
||||
dlg.SetText("Form")
|
||||
dlg.SetSize(200, 100)
|
||||
return dlg
|
||||
}
|
||||
|
||||
func (dlg *Dialog) SetModal(modal bool) {
|
||||
dlg.isModal = modal
|
||||
}
|
||||
|
||||
// SetButtons wires up dialog events to buttons. btnCancel can be nil.
|
||||
func (dlg *Dialog) SetButtons(btnOk *PushButton, btnCancel *PushButton) {
|
||||
dlg.btnOk = btnOk
|
||||
dlg.btnOk.SetDefault()
|
||||
dlg.btnCancel = btnCancel
|
||||
}
|
||||
|
||||
// Events
|
||||
func (dlg *Dialog) OnLoad() *EventManager {
|
||||
return &dlg.onLoad
|
||||
}
|
||||
|
||||
func (dlg *Dialog) OnOk() *EventManager {
|
||||
return &dlg.onOk
|
||||
}
|
||||
|
||||
func (dlg *Dialog) OnCancel() *EventManager {
|
||||
return &dlg.onCancel
|
||||
}
|
||||
|
||||
// PreTranslateMessage handles dialog specific messages. IMPORTANT.
|
||||
func (dlg *Dialog) PreTranslateMessage(msg *w32.MSG) bool {
|
||||
if msg.Message >= w32.WM_KEYFIRST && msg.Message <= w32.WM_KEYLAST {
|
||||
if w32.IsDialogMessage(dlg.hwnd, msg) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Show dialog performs special setup for dialog windows.
|
||||
func (dlg *Dialog) Show() {
|
||||
if dlg.isModal {
|
||||
dlg.Parent().SetEnabled(false)
|
||||
}
|
||||
dlg.onLoad.Fire(NewEvent(dlg, nil))
|
||||
dlg.Form.Show()
|
||||
}
|
||||
|
||||
// Close dialog when you done with it.
|
||||
func (dlg *Dialog) Close() {
|
||||
if dlg.isModal {
|
||||
dlg.Parent().SetEnabled(true)
|
||||
}
|
||||
dlg.ControlBase.Close()
|
||||
}
|
||||
|
||||
func (dlg *Dialog) cancel() {
|
||||
if dlg.btnCancel != nil {
|
||||
dlg.btnCancel.onClick.Fire(NewEvent(dlg.btnCancel, nil))
|
||||
}
|
||||
dlg.onCancel.Fire(NewEvent(dlg, nil))
|
||||
}
|
||||
|
||||
func (dlg *Dialog) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_COMMAND:
|
||||
switch w32.LOWORD(uint32(wparam)) {
|
||||
case w32.IDOK:
|
||||
if dlg.btnOk != nil {
|
||||
dlg.btnOk.onClick.Fire(NewEvent(dlg.btnOk, nil))
|
||||
}
|
||||
dlg.onOk.Fire(NewEvent(dlg, nil))
|
||||
return w32.TRUE
|
||||
|
||||
case w32.IDCANCEL:
|
||||
dlg.cancel()
|
||||
return w32.TRUE
|
||||
}
|
||||
|
||||
case w32.WM_CLOSE:
|
||||
dlg.cancel() // use onCancel or dlg.btnCancel.OnClick to close
|
||||
return 0
|
||||
|
||||
case w32.WM_DESTROY:
|
||||
if dlg.isModal {
|
||||
dlg.Parent().SetEnabled(true)
|
||||
}
|
||||
}
|
||||
return w32.DefWindowProc(dlg.hwnd, msg, wparam, lparam)
|
||||
}
|
BIN
v2/internal/frontend/desktop/windows/winc/dock_topbottom.png
Normal file
BIN
v2/internal/frontend/desktop/windows/winc/dock_topbottom.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
BIN
v2/internal/frontend/desktop/windows/winc/dock_topleft.png
Normal file
BIN
v2/internal/frontend/desktop/windows/winc/dock_topleft.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
111
v2/internal/frontend/desktop/windows/winc/edit.go
Normal file
111
v2/internal/frontend/desktop/windows/winc/edit.go
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
|
||||
type Edit struct {
|
||||
ControlBase
|
||||
onChange EventManager
|
||||
}
|
||||
|
||||
const passwordChar = '*'
|
||||
const nopasswordChar = ' '
|
||||
|
||||
func NewEdit(parent Controller) *Edit {
|
||||
edt := new(Edit)
|
||||
|
||||
edt.InitControl("EDIT", parent, w32.WS_EX_CLIENTEDGE, w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_TABSTOP|w32.ES_LEFT|
|
||||
w32.ES_AUTOHSCROLL)
|
||||
RegMsgHandler(edt)
|
||||
|
||||
edt.SetFont(DefaultFont)
|
||||
edt.SetSize(200, 22)
|
||||
return edt
|
||||
}
|
||||
|
||||
// Events.
|
||||
func (ed *Edit) OnChange() *EventManager {
|
||||
return &ed.onChange
|
||||
}
|
||||
|
||||
// Public methods.
|
||||
func (ed *Edit) SetReadOnly(isReadOnly bool) {
|
||||
w32.SendMessage(ed.hwnd, w32.EM_SETREADONLY, uintptr(w32.BoolToBOOL(isReadOnly)), 0)
|
||||
}
|
||||
|
||||
//Public methods
|
||||
func (ed *Edit) SetPassword(isPassword bool) {
|
||||
if isPassword {
|
||||
w32.SendMessage(ed.hwnd, w32.EM_SETPASSWORDCHAR, uintptr(passwordChar), 0)
|
||||
} else {
|
||||
w32.SendMessage(ed.hwnd, w32.EM_SETPASSWORDCHAR, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (ed *Edit) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_COMMAND:
|
||||
switch w32.HIWORD(uint32(wparam)) {
|
||||
case w32.EN_CHANGE:
|
||||
ed.onChange.Fire(NewEvent(ed, nil))
|
||||
}
|
||||
/*case w32.WM_GETDLGCODE:
|
||||
println("Edit")
|
||||
if wparam == w32.VK_RETURN {
|
||||
return w32.DLGC_WANTALLKEYS
|
||||
}*/
|
||||
}
|
||||
return w32.DefWindowProc(ed.hwnd, msg, wparam, lparam)
|
||||
}
|
||||
|
||||
// MultiEdit is multiline text edit.
|
||||
type MultiEdit struct {
|
||||
ControlBase
|
||||
onChange EventManager
|
||||
}
|
||||
|
||||
func NewMultiEdit(parent Controller) *MultiEdit {
|
||||
med := new(MultiEdit)
|
||||
|
||||
med.InitControl("EDIT", parent, w32.WS_EX_CLIENTEDGE, w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_TABSTOP|w32.ES_LEFT|
|
||||
w32.WS_VSCROLL|w32.WS_HSCROLL|w32.ES_MULTILINE|w32.ES_WANTRETURN|w32.ES_AUTOHSCROLL|w32.ES_AUTOVSCROLL)
|
||||
RegMsgHandler(med)
|
||||
|
||||
med.SetFont(DefaultFont)
|
||||
med.SetSize(200, 400)
|
||||
return med
|
||||
}
|
||||
|
||||
// Events
|
||||
func (med *MultiEdit) OnChange() *EventManager {
|
||||
return &med.onChange
|
||||
}
|
||||
|
||||
// Public methods
|
||||
func (med *MultiEdit) SetReadOnly(isReadOnly bool) {
|
||||
w32.SendMessage(med.hwnd, w32.EM_SETREADONLY, uintptr(w32.BoolToBOOL(isReadOnly)), 0)
|
||||
}
|
||||
|
||||
func (med *MultiEdit) AddLine(text string) {
|
||||
if len(med.Text()) == 0 {
|
||||
med.SetText(text)
|
||||
} else {
|
||||
med.SetText(med.Text() + "\r\n" + text)
|
||||
}
|
||||
}
|
||||
|
||||
func (med *MultiEdit) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
|
||||
case w32.WM_COMMAND:
|
||||
switch w32.HIWORD(uint32(wparam)) {
|
||||
case w32.EN_CHANGE:
|
||||
med.onChange.Fire(NewEvent(med, nil))
|
||||
}
|
||||
}
|
||||
return w32.DefWindowProc(med.hwnd, msg, wparam, lparam)
|
||||
}
|
15
v2/internal/frontend/desktop/windows/winc/event.go
Normal file
15
v2/internal/frontend/desktop/windows/winc/event.go
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
type Event struct {
|
||||
Sender Controller
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func NewEvent(sender Controller, data interface{}) *Event {
|
||||
return &Event{Sender: sender, Data: data}
|
||||
}
|
50
v2/internal/frontend/desktop/windows/winc/eventdata.go
Normal file
50
v2/internal/frontend/desktop/windows/winc/eventdata.go
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type RawMsg struct {
|
||||
Hwnd w32.HWND
|
||||
Msg uint32
|
||||
WParam, LParam uintptr
|
||||
}
|
||||
|
||||
type MouseEventData struct {
|
||||
X, Y int
|
||||
Button int
|
||||
Wheel int
|
||||
}
|
||||
|
||||
type DropFilesEventData struct {
|
||||
X, Y int
|
||||
Files []string
|
||||
}
|
||||
|
||||
type PaintEventData struct {
|
||||
Canvas *Canvas
|
||||
}
|
||||
|
||||
type LabelEditEventData struct {
|
||||
Item ListItem
|
||||
Text string
|
||||
//PszText *uint16
|
||||
}
|
||||
|
||||
/*type LVDBLClickEventData struct {
|
||||
NmItem *w32.NMITEMACTIVATE
|
||||
}*/
|
||||
|
||||
type KeyUpEventData struct {
|
||||
VKey, Code int
|
||||
}
|
||||
|
||||
type SizeEventData struct {
|
||||
Type uint
|
||||
X, Y int
|
||||
}
|
22
v2/internal/frontend/desktop/windows/winc/eventmanager.go
Normal file
22
v2/internal/frontend/desktop/windows/winc/eventmanager.go
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
type EventHandler func(arg *Event)
|
||||
|
||||
type EventManager struct {
|
||||
handler EventHandler
|
||||
}
|
||||
|
||||
func (evm *EventManager) Fire(arg *Event) {
|
||||
if evm.handler != nil {
|
||||
evm.handler(arg)
|
||||
}
|
||||
}
|
||||
|
||||
func (evm *EventManager) Bind(handler EventHandler) {
|
||||
evm.handler = handler
|
||||
}
|
119
v2/internal/frontend/desktop/windows/winc/font.go
Normal file
119
v2/internal/frontend/desktop/windows/winc/font.go
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
const (
|
||||
FontBold byte = 0x01
|
||||
FontItalic byte = 0x02
|
||||
FontUnderline byte = 0x04
|
||||
FontStrikeOut byte = 0x08
|
||||
)
|
||||
|
||||
func init() {
|
||||
DefaultFont = NewFont("MS Shell Dlg 2", 8, 0)
|
||||
}
|
||||
|
||||
type Font struct {
|
||||
hfont w32.HFONT
|
||||
family string
|
||||
pointSize int
|
||||
style byte
|
||||
}
|
||||
|
||||
func NewFont(family string, pointSize int, style byte) *Font {
|
||||
if style > FontBold|FontItalic|FontUnderline|FontStrikeOut {
|
||||
panic("Invalid font style")
|
||||
}
|
||||
|
||||
//Retrive screen DPI
|
||||
hDC := w32.GetDC(0)
|
||||
defer w32.ReleaseDC(0, hDC)
|
||||
screenDPIY := w32.GetDeviceCaps(hDC, w32.LOGPIXELSY)
|
||||
|
||||
font := Font{
|
||||
family: family,
|
||||
pointSize: pointSize,
|
||||
style: style,
|
||||
}
|
||||
|
||||
font.hfont = font.createForDPI(screenDPIY)
|
||||
if font.hfont == 0 {
|
||||
panic("CreateFontIndirect failed")
|
||||
}
|
||||
|
||||
return &font
|
||||
}
|
||||
|
||||
func (fnt *Font) createForDPI(dpi int) w32.HFONT {
|
||||
var lf w32.LOGFONT
|
||||
|
||||
lf.Height = int32(-w32.MulDiv(fnt.pointSize, dpi, 72))
|
||||
if fnt.style&FontBold > 0 {
|
||||
lf.Weight = w32.FW_BOLD
|
||||
} else {
|
||||
lf.Weight = w32.FW_NORMAL
|
||||
}
|
||||
if fnt.style&FontItalic > 0 {
|
||||
lf.Italic = 1
|
||||
}
|
||||
if fnt.style&FontUnderline > 0 {
|
||||
lf.Underline = 1
|
||||
}
|
||||
if fnt.style&FontStrikeOut > 0 {
|
||||
lf.StrikeOut = 1
|
||||
}
|
||||
lf.CharSet = w32.DEFAULT_CHARSET
|
||||
lf.OutPrecision = w32.OUT_TT_PRECIS
|
||||
lf.ClipPrecision = w32.CLIP_DEFAULT_PRECIS
|
||||
lf.Quality = w32.CLEARTYPE_QUALITY
|
||||
lf.PitchAndFamily = w32.VARIABLE_PITCH | w32.FF_SWISS
|
||||
|
||||
src := syscall.StringToUTF16(fnt.family)
|
||||
dest := lf.FaceName[:]
|
||||
copy(dest, src)
|
||||
|
||||
return w32.CreateFontIndirect(&lf)
|
||||
}
|
||||
|
||||
func (fnt *Font) GetHFONT() w32.HFONT {
|
||||
return fnt.hfont
|
||||
}
|
||||
|
||||
func (fnt *Font) Bold() bool {
|
||||
return fnt.style&FontBold > 0
|
||||
}
|
||||
|
||||
func (fnt *Font) Dispose() {
|
||||
if fnt.hfont != 0 {
|
||||
w32.DeleteObject(w32.HGDIOBJ(fnt.hfont))
|
||||
}
|
||||
}
|
||||
|
||||
func (fnt *Font) Family() string {
|
||||
return fnt.family
|
||||
}
|
||||
|
||||
func (fnt *Font) Italic() bool {
|
||||
return fnt.style&FontItalic > 0
|
||||
}
|
||||
|
||||
func (fnt *Font) StrikeOut() bool {
|
||||
return fnt.style&FontStrikeOut > 0
|
||||
}
|
||||
|
||||
func (fnt *Font) Underline() bool {
|
||||
return fnt.style&FontUnderline > 0
|
||||
}
|
||||
|
||||
func (fnt *Font) Style() byte {
|
||||
return fnt.style
|
||||
}
|
300
v2/internal/frontend/desktop/windows/winc/form.go
Normal file
300
v2/internal/frontend/desktop/windows/winc/form.go
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type LayoutManager interface {
|
||||
Update()
|
||||
}
|
||||
|
||||
// A Form is main window of the application.
|
||||
type Form struct {
|
||||
ControlBase
|
||||
|
||||
layoutMng LayoutManager
|
||||
|
||||
// Fullscreen / Unfullscreen
|
||||
isFullscreen bool
|
||||
previousWindowStyle uint32
|
||||
previousWindowPlacement w32.WINDOWPLACEMENT
|
||||
}
|
||||
|
||||
func NewCustomForm(parent Controller, exStyle int, dwStyle uint) *Form {
|
||||
fm := new(Form)
|
||||
|
||||
RegClassOnlyOnce("winc_Form")
|
||||
|
||||
fm.isForm = true
|
||||
|
||||
if exStyle == 0 {
|
||||
exStyle = w32.WS_EX_CONTROLPARENT | w32.WS_EX_APPWINDOW
|
||||
}
|
||||
|
||||
if dwStyle == 0 {
|
||||
dwStyle = w32.WS_OVERLAPPEDWINDOW
|
||||
}
|
||||
|
||||
fm.hwnd = CreateWindow("winc_Form", parent, uint(exStyle), dwStyle)
|
||||
fm.parent = parent
|
||||
|
||||
// this might fail if icon resource is not embedded in the binary
|
||||
if ico, err := NewIconFromResource(GetAppInstance(), uint16(AppIconID)); err == nil {
|
||||
fm.SetIcon(0, ico)
|
||||
}
|
||||
|
||||
// This forces display of focus rectangles, as soon as the user starts to type.
|
||||
w32.SendMessage(fm.hwnd, w32.WM_CHANGEUISTATE, w32.UIS_INITIALIZE, 0)
|
||||
|
||||
RegMsgHandler(fm)
|
||||
|
||||
fm.SetFont(DefaultFont)
|
||||
fm.SetText("Form")
|
||||
return fm
|
||||
}
|
||||
|
||||
func NewForm(parent Controller) *Form {
|
||||
fm := new(Form)
|
||||
|
||||
RegClassOnlyOnce("winc_Form")
|
||||
|
||||
fm.isForm = true
|
||||
fm.hwnd = CreateWindow("winc_Form", parent, w32.WS_EX_CONTROLPARENT|w32.WS_EX_APPWINDOW, w32.WS_OVERLAPPEDWINDOW)
|
||||
fm.parent = parent
|
||||
|
||||
// this might fail if icon resource is not embedded in the binary
|
||||
if ico, err := NewIconFromResource(GetAppInstance(), uint16(AppIconID)); err == nil {
|
||||
fm.SetIcon(0, ico)
|
||||
}
|
||||
|
||||
// This forces display of focus rectangles, as soon as the user starts to type.
|
||||
w32.SendMessage(fm.hwnd, w32.WM_CHANGEUISTATE, w32.UIS_INITIALIZE, 0)
|
||||
|
||||
RegMsgHandler(fm)
|
||||
|
||||
fm.SetFont(DefaultFont)
|
||||
fm.SetText("Form")
|
||||
return fm
|
||||
}
|
||||
|
||||
func (fm *Form) SetLayout(mng LayoutManager) {
|
||||
fm.layoutMng = mng
|
||||
}
|
||||
|
||||
// UpdateLayout refresh layout.
|
||||
func (fm *Form) UpdateLayout() {
|
||||
if fm.layoutMng != nil {
|
||||
fm.layoutMng.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func (fm *Form) NewMenu() *Menu {
|
||||
hMenu := w32.CreateMenu()
|
||||
if hMenu == 0 {
|
||||
panic("failed CreateMenu")
|
||||
}
|
||||
m := &Menu{hMenu: hMenu, hwnd: fm.hwnd}
|
||||
if !w32.SetMenu(fm.hwnd, hMenu) {
|
||||
panic("failed SetMenu")
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (fm *Form) DisableIcon() {
|
||||
windowInfo := getWindowInfo(fm.hwnd)
|
||||
frameless := windowInfo.IsPopup()
|
||||
if frameless {
|
||||
return
|
||||
}
|
||||
exStyle := w32.GetWindowLong(fm.hwnd, w32.GWL_EXSTYLE)
|
||||
w32.SetWindowLong(fm.hwnd, w32.GWL_EXSTYLE, uint32(exStyle|w32.WS_EX_DLGMODALFRAME))
|
||||
w32.SetWindowPos(fm.hwnd, 0, 0, 0, 0, 0,
|
||||
uint(
|
||||
w32.SWP_FRAMECHANGED|
|
||||
w32.SWP_NOMOVE|
|
||||
w32.SWP_NOSIZE|
|
||||
w32.SWP_NOZORDER),
|
||||
)
|
||||
}
|
||||
|
||||
func (fm *Form) Maximise() {
|
||||
w32.ShowWindow(fm.hwnd, w32.SW_MAXIMIZE)
|
||||
}
|
||||
|
||||
func (fm *Form) Minimise() {
|
||||
w32.ShowWindow(fm.hwnd, w32.SW_MINIMIZE)
|
||||
}
|
||||
|
||||
func (fm *Form) Restore() {
|
||||
w32.ShowWindow(fm.hwnd, w32.SW_RESTORE)
|
||||
}
|
||||
|
||||
// Public methods
|
||||
func (fm *Form) Center() {
|
||||
|
||||
windowInfo := getWindowInfo(fm.hwnd)
|
||||
frameless := windowInfo.IsPopup()
|
||||
|
||||
info := getMonitorInfo(fm.hwnd)
|
||||
workRect := info.RcWork
|
||||
screenMiddleW := workRect.Left + (workRect.Right-workRect.Left)/2
|
||||
screenMiddleH := workRect.Top + (workRect.Bottom-workRect.Top)/2
|
||||
var winRect *w32.RECT
|
||||
if !frameless {
|
||||
winRect = w32.GetWindowRect(fm.hwnd)
|
||||
} else {
|
||||
winRect = w32.GetClientRect(fm.hwnd)
|
||||
}
|
||||
winWidth := winRect.Right - winRect.Left
|
||||
winHeight := winRect.Bottom - winRect.Top
|
||||
windowX := screenMiddleW - (winWidth / 2)
|
||||
windowY := screenMiddleH - (winHeight / 2)
|
||||
w32.SetWindowPos(fm.hwnd, w32.HWND_TOP, int(windowX), int(windowY), int(winWidth), int(winHeight), w32.SWP_NOSIZE)
|
||||
}
|
||||
|
||||
func (fm *Form) Fullscreen() {
|
||||
if fm.isFullscreen {
|
||||
return
|
||||
}
|
||||
|
||||
fm.previousWindowStyle = uint32(w32.GetWindowLongPtr(fm.hwnd, w32.GWL_STYLE))
|
||||
monitor := w32.MonitorFromWindow(fm.hwnd, w32.MONITOR_DEFAULTTOPRIMARY)
|
||||
var monitorInfo w32.MONITORINFO
|
||||
monitorInfo.CbSize = uint32(unsafe.Sizeof(monitorInfo))
|
||||
if !w32.GetMonitorInfo(monitor, &monitorInfo) {
|
||||
return
|
||||
}
|
||||
if !w32.GetWindowPlacement(fm.hwnd, &fm.previousWindowPlacement) {
|
||||
return
|
||||
}
|
||||
w32.SetWindowLong(fm.hwnd, w32.GWL_STYLE, fm.previousWindowStyle & ^uint32(w32.WS_OVERLAPPEDWINDOW))
|
||||
w32.SetWindowPos(fm.hwnd, w32.HWND_TOP,
|
||||
int(monitorInfo.RcMonitor.Left),
|
||||
int(monitorInfo.RcMonitor.Top),
|
||||
int(monitorInfo.RcMonitor.Right-monitorInfo.RcMonitor.Left),
|
||||
int(monitorInfo.RcMonitor.Bottom-monitorInfo.RcMonitor.Top),
|
||||
w32.SWP_NOOWNERZORDER|w32.SWP_FRAMECHANGED)
|
||||
fm.isFullscreen = true
|
||||
}
|
||||
|
||||
func (fm *Form) UnFullscreen() {
|
||||
if !fm.isFullscreen {
|
||||
return
|
||||
}
|
||||
w32.SetWindowLong(fm.hwnd, w32.GWL_STYLE, fm.previousWindowStyle)
|
||||
w32.SetWindowPlacement(fm.hwnd, &fm.previousWindowPlacement)
|
||||
w32.SetWindowPos(fm.hwnd, 0, 0, 0, 0, 0,
|
||||
w32.SWP_NOMOVE|w32.SWP_NOSIZE|w32.SWP_NOZORDER|w32.SWP_NOOWNERZORDER|w32.SWP_FRAMECHANGED)
|
||||
fm.isFullscreen = false
|
||||
}
|
||||
|
||||
func (fm *Form) IsFullScreen() bool {
|
||||
return fm.isFullscreen
|
||||
}
|
||||
|
||||
// IconType: 1 - ICON_BIG; 0 - ICON_SMALL
|
||||
func (fm *Form) SetIcon(iconType int, icon *Icon) {
|
||||
if iconType > 1 {
|
||||
panic("IconType is invalid")
|
||||
}
|
||||
w32.SendMessage(fm.hwnd, w32.WM_SETICON, uintptr(iconType), uintptr(icon.Handle()))
|
||||
}
|
||||
|
||||
func (fm *Form) EnableMaxButton(b bool) {
|
||||
SetStyle(fm.hwnd, b, w32.WS_MAXIMIZEBOX)
|
||||
}
|
||||
|
||||
func (fm *Form) EnableMinButton(b bool) {
|
||||
SetStyle(fm.hwnd, b, w32.WS_MINIMIZEBOX)
|
||||
}
|
||||
|
||||
func (fm *Form) EnableSizable(b bool) {
|
||||
SetStyle(fm.hwnd, b, w32.WS_THICKFRAME)
|
||||
}
|
||||
|
||||
func (fm *Form) EnableDragMove(_ bool) {
|
||||
//fm.isDragMove = b
|
||||
}
|
||||
|
||||
func (fm *Form) EnableTopMost(b bool) {
|
||||
tag := w32.HWND_NOTOPMOST
|
||||
if b {
|
||||
tag = w32.HWND_TOPMOST
|
||||
}
|
||||
w32.SetWindowPos(fm.hwnd, tag, 0, 0, 0, 0, w32.SWP_NOMOVE|w32.SWP_NOSIZE)
|
||||
}
|
||||
|
||||
func (fm *Form) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
|
||||
switch msg {
|
||||
case w32.WM_COMMAND:
|
||||
if lparam == 0 && w32.HIWORD(uint32(wparam)) == 0 {
|
||||
// Menu support.
|
||||
actionID := uint16(w32.LOWORD(uint32(wparam)))
|
||||
if action, ok := actionsByID[actionID]; ok {
|
||||
action.onClick.Fire(NewEvent(fm, nil))
|
||||
}
|
||||
}
|
||||
case w32.WM_KEYDOWN:
|
||||
// Accelerator support.
|
||||
key := Key(wparam)
|
||||
if uint32(lparam)>>30 == 0 {
|
||||
// Using TranslateAccelerators refused to work, so we handle them
|
||||
// ourselves, at least for now.
|
||||
shortcut := Shortcut{ModifiersDown(), key}
|
||||
if action, ok := shortcut2Action[shortcut]; ok {
|
||||
if action.Enabled() {
|
||||
action.onClick.Fire(NewEvent(fm, nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case w32.WM_CLOSE:
|
||||
return 0
|
||||
case w32.WM_DESTROY:
|
||||
w32.PostQuitMessage(0)
|
||||
return 0
|
||||
|
||||
case w32.WM_SIZE, w32.WM_PAINT:
|
||||
if fm.layoutMng != nil {
|
||||
fm.layoutMng.Update()
|
||||
}
|
||||
case w32.WM_GETMINMAXINFO:
|
||||
mmi := (*w32.MINMAXINFO)(unsafe.Pointer(lparam))
|
||||
hasConstraints := false
|
||||
if fm.minWidth > 0 || fm.minHeight > 0 {
|
||||
hasConstraints = true
|
||||
|
||||
width, height := fm.scaleWithWindowDPI(fm.minWidth, fm.minHeight)
|
||||
if width > 0 {
|
||||
mmi.PtMinTrackSize.X = int32(width)
|
||||
}
|
||||
if height > 0 {
|
||||
mmi.PtMinTrackSize.Y = int32(height)
|
||||
}
|
||||
}
|
||||
if fm.maxWidth > 0 || fm.maxHeight > 0 {
|
||||
hasConstraints = true
|
||||
|
||||
width, height := fm.scaleWithWindowDPI(fm.maxWidth, fm.maxHeight)
|
||||
if width > 0 {
|
||||
mmi.PtMaxTrackSize.X = int32(width)
|
||||
}
|
||||
if height > 0 {
|
||||
mmi.PtMaxTrackSize.Y = int32(height)
|
||||
}
|
||||
}
|
||||
if hasConstraints {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
return w32.DefWindowProc(fm.hwnd, msg, wparam, lparam)
|
||||
}
|
25
v2/internal/frontend/desktop/windows/winc/globalvars.go
Normal file
25
v2/internal/frontend/desktop/windows/winc/globalvars.go
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
//Private global variables.
|
||||
var (
|
||||
gAppInstance w32.HINSTANCE
|
||||
gControllerRegistry map[w32.HWND]Controller
|
||||
gRegisteredClasses []string
|
||||
)
|
||||
|
||||
//Public global variables.
|
||||
var (
|
||||
GeneralWndprocCallBack = syscall.NewCallback(generalWndProc)
|
||||
DefaultFont *Font
|
||||
)
|
53
v2/internal/frontend/desktop/windows/winc/icon.go
Normal file
53
v2/internal/frontend/desktop/windows/winc/icon.go
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type Icon struct {
|
||||
handle w32.HICON
|
||||
}
|
||||
|
||||
func NewIconFromFile(path string) (*Icon, error) {
|
||||
ico := new(Icon)
|
||||
var err error
|
||||
if ico.handle = w32.LoadIcon(0, syscall.StringToUTF16Ptr(path)); ico.handle == 0 {
|
||||
err = errors.New(fmt.Sprintf("Cannot load icon from %s", path))
|
||||
}
|
||||
return ico, err
|
||||
}
|
||||
|
||||
func NewIconFromResource(instance w32.HINSTANCE, resId uint16) (*Icon, error) {
|
||||
ico := new(Icon)
|
||||
var err error
|
||||
if ico.handle = w32.LoadIcon(instance, w32.MakeIntResource(resId)); ico.handle == 0 {
|
||||
err = errors.New(fmt.Sprintf("Cannot load icon from resource with id %v", resId))
|
||||
}
|
||||
return ico, err
|
||||
}
|
||||
|
||||
func ExtractIcon(fileName string, index int) (*Icon, error) {
|
||||
ico := new(Icon)
|
||||
var err error
|
||||
if ico.handle = w32.ExtractIcon(fileName, index); ico.handle == 0 || ico.handle == 1 {
|
||||
err = errors.New(fmt.Sprintf("Cannot extract icon from %s at index %v", fileName, index))
|
||||
}
|
||||
return ico, err
|
||||
}
|
||||
|
||||
func (ic *Icon) Destroy() bool {
|
||||
return w32.DestroyIcon(ic.handle)
|
||||
}
|
||||
|
||||
func (ic *Icon) Handle() w32.HICON {
|
||||
return ic.handle
|
||||
}
|
62
v2/internal/frontend/desktop/windows/winc/imagelist.go
Normal file
62
v2/internal/frontend/desktop/windows/winc/imagelist.go
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type ImageList struct {
|
||||
handle w32.HIMAGELIST
|
||||
}
|
||||
|
||||
func NewImageList(cx, cy int) *ImageList {
|
||||
return newImageList(cx, cy, w32.ILC_COLOR32, 0, 0)
|
||||
}
|
||||
|
||||
func newImageList(cx, cy int, flags uint, cInitial, cGrow int) *ImageList {
|
||||
imgl := new(ImageList)
|
||||
imgl.handle = w32.ImageList_Create(cx, cy, flags, cInitial, cGrow)
|
||||
return imgl
|
||||
}
|
||||
|
||||
func (im *ImageList) Handle() w32.HIMAGELIST {
|
||||
return im.handle
|
||||
}
|
||||
|
||||
func (im *ImageList) Destroy() bool {
|
||||
return w32.ImageList_Destroy(im.handle)
|
||||
}
|
||||
|
||||
func (im *ImageList) SetImageCount(uNewCount uint) bool {
|
||||
return w32.ImageList_SetImageCount(im.handle, uNewCount)
|
||||
}
|
||||
|
||||
func (im *ImageList) ImageCount() int {
|
||||
return w32.ImageList_GetImageCount(im.handle)
|
||||
}
|
||||
|
||||
func (im *ImageList) AddIcon(icon *Icon) int {
|
||||
return w32.ImageList_AddIcon(im.handle, icon.Handle())
|
||||
}
|
||||
|
||||
func (im *ImageList) AddResIcon(iconID uint16) {
|
||||
if ico, err := NewIconFromResource(GetAppInstance(), iconID); err == nil {
|
||||
im.AddIcon(ico)
|
||||
return
|
||||
}
|
||||
panic(fmt.Sprintf("missing icon with icon ID: %d", iconID))
|
||||
}
|
||||
|
||||
func (im *ImageList) RemoveAll() bool {
|
||||
return w32.ImageList_RemoveAll(im.handle)
|
||||
}
|
||||
|
||||
func (im *ImageList) Remove(i int) bool {
|
||||
return w32.ImageList_Remove(im.handle, i)
|
||||
}
|
57
v2/internal/frontend/desktop/windows/winc/imageview.go
Normal file
57
v2/internal/frontend/desktop/windows/winc/imageview.go
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
|
||||
type ImageView struct {
|
||||
ControlBase
|
||||
|
||||
bmp *Bitmap
|
||||
}
|
||||
|
||||
func NewImageView(parent Controller) *ImageView {
|
||||
iv := new(ImageView)
|
||||
|
||||
iv.InitWindow("winc_ImageView", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE)
|
||||
RegMsgHandler(iv)
|
||||
|
||||
iv.SetFont(DefaultFont)
|
||||
iv.SetText("")
|
||||
iv.SetSize(200, 65)
|
||||
return iv
|
||||
}
|
||||
|
||||
func (iv *ImageView) DrawImageFile(filepath string) error {
|
||||
bmp, err := NewBitmapFromFile(filepath, RGB(255, 255, 0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iv.bmp = bmp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iv *ImageView) DrawImage(bmp *Bitmap) {
|
||||
iv.bmp = bmp
|
||||
}
|
||||
|
||||
func (iv *ImageView) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_SIZE, w32.WM_SIZING:
|
||||
iv.Invalidate(true)
|
||||
|
||||
case w32.WM_ERASEBKGND:
|
||||
return 1 // important
|
||||
|
||||
case w32.WM_PAINT:
|
||||
if iv.bmp != nil {
|
||||
canvas := NewCanvasFromHwnd(iv.hwnd)
|
||||
defer canvas.Dispose()
|
||||
iv.SetSize(iv.bmp.Size())
|
||||
canvas.DrawBitmap(iv.bmp, 0, 0)
|
||||
}
|
||||
}
|
||||
return w32.DefWindowProc(iv.hwnd, msg, wparam, lparam)
|
||||
}
|
340
v2/internal/frontend/desktop/windows/winc/imageviewbox.go
Normal file
340
v2/internal/frontend/desktop/windows/winc/imageviewbox.go
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type direction int
|
||||
|
||||
const (
|
||||
DirNone direction = iota
|
||||
DirX
|
||||
DirY
|
||||
DirX2
|
||||
DirY2
|
||||
)
|
||||
|
||||
var ImageBoxPen = NewPen(w32.PS_GEOMETRIC, 2, NewSolidColorBrush(RGB(140, 140, 220)))
|
||||
var ImageBoxHiPen = NewPen(w32.PS_GEOMETRIC, 2, NewSolidColorBrush(RGB(220, 140, 140)))
|
||||
var ImageBoxMarkBrush = NewSolidColorBrush(RGB(40, 40, 40))
|
||||
var ImageBoxMarkPen = NewPen(w32.PS_GEOMETRIC, 2, ImageBoxMarkBrush)
|
||||
|
||||
type ImageBox struct {
|
||||
Name string
|
||||
Type int
|
||||
X, Y, X2, Y2 int
|
||||
|
||||
underMouse bool // dynamic value
|
||||
}
|
||||
|
||||
func (b *ImageBox) Rect() *Rect {
|
||||
return NewRect(b.X, b.Y, b.X2, b.Y2)
|
||||
}
|
||||
|
||||
// ImageViewBox is image view with boxes.
|
||||
type ImageViewBox struct {
|
||||
ControlBase
|
||||
|
||||
bmp *Bitmap
|
||||
mouseLeft bool
|
||||
modified bool // used by GUI to see if any image box modified
|
||||
|
||||
add bool
|
||||
|
||||
Boxes []*ImageBox // might be persisted to file
|
||||
dragBox *ImageBox
|
||||
selBox *ImageBox
|
||||
|
||||
dragStartX, dragStartY int
|
||||
resize direction
|
||||
|
||||
onSelectedChange EventManager
|
||||
onAdd EventManager
|
||||
onModify EventManager
|
||||
}
|
||||
|
||||
func NewImageViewBox(parent Controller) *ImageViewBox {
|
||||
iv := new(ImageViewBox)
|
||||
|
||||
iv.InitWindow("winc_ImageViewBox", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE)
|
||||
RegMsgHandler(iv)
|
||||
|
||||
iv.SetFont(DefaultFont)
|
||||
iv.SetText("")
|
||||
iv.SetSize(200, 65)
|
||||
|
||||
return iv
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) OnSelectedChange() *EventManager {
|
||||
return &iv.onSelectedChange
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) OnAdd() *EventManager {
|
||||
return &iv.onAdd
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) OnModify() *EventManager {
|
||||
return &iv.onModify
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) IsModified() bool { return iv.modified }
|
||||
func (iv *ImageViewBox) SetModified(modified bool) { iv.modified = modified }
|
||||
func (iv *ImageViewBox) IsLoaded() bool { return iv.bmp != nil }
|
||||
func (iv *ImageViewBox) AddMode() bool { return iv.add }
|
||||
func (iv *ImageViewBox) SetAddMode(add bool) { iv.add = add }
|
||||
func (iv *ImageViewBox) HasSelected() bool { return iv.selBox != nil && iv.bmp != nil }
|
||||
|
||||
func (iv *ImageViewBox) wasModified() {
|
||||
iv.modified = true
|
||||
iv.onModify.Fire(NewEvent(iv, nil))
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) DeleteSelected() {
|
||||
if iv.selBox != nil {
|
||||
for i, b := range iv.Boxes {
|
||||
if b == iv.selBox {
|
||||
iv.Boxes = append(iv.Boxes[:i], iv.Boxes[i+1:]...)
|
||||
iv.selBox = nil
|
||||
iv.Invalidate(true)
|
||||
iv.wasModified()
|
||||
iv.onSelectedChange.Fire(NewEvent(iv, nil))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) NameSelected() string {
|
||||
if iv.selBox != nil {
|
||||
return iv.selBox.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) SetNameSelected(name string) {
|
||||
if iv.selBox != nil {
|
||||
iv.selBox.Name = name
|
||||
iv.wasModified()
|
||||
}
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) TypeSelected() int {
|
||||
if iv.selBox != nil {
|
||||
return iv.selBox.Type
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) SetTypeSelected(typ int) {
|
||||
if iv.selBox != nil {
|
||||
iv.selBox.Type = typ
|
||||
iv.wasModified()
|
||||
}
|
||||
}
|
||||
|
||||
func (ib *ImageViewBox) updateHighlight(x, y int) bool {
|
||||
var changed bool
|
||||
for _, b := range ib.Boxes {
|
||||
under := x >= b.X && y >= b.Y && x <= b.X2 && y <= b.Y2
|
||||
if b.underMouse != under {
|
||||
changed = true
|
||||
}
|
||||
b.underMouse = under
|
||||
/*if sel {
|
||||
break // allow only one to be underMouse
|
||||
}*/
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
func (ib *ImageViewBox) isUnderMouse(x, y int) *ImageBox {
|
||||
for _, b := range ib.Boxes {
|
||||
if x >= b.X && y >= b.Y && x <= b.X2 && y <= b.Y2 {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ib *ImageViewBox) getCursor(x, y int) uint16 {
|
||||
for _, b := range ib.Boxes {
|
||||
switch d := ib.resizingDirection(b, x, y); d {
|
||||
case DirY, DirY2:
|
||||
return w32.IDC_SIZENS
|
||||
case DirX, DirX2:
|
||||
return w32.IDC_SIZEWE
|
||||
}
|
||||
// w32.IDC_SIZEALL or w32.IDC_SIZE for resize
|
||||
}
|
||||
return w32.IDC_ARROW
|
||||
}
|
||||
|
||||
func (ib *ImageViewBox) resizingDirection(b *ImageBox, x, y int) direction {
|
||||
if b == nil {
|
||||
return DirNone
|
||||
}
|
||||
switch {
|
||||
case b.X == x || b.X == x-1 || b.X == x+1:
|
||||
return DirX
|
||||
case b.X2 == x || b.X2 == x-1 || b.X2 == x+1:
|
||||
return DirX2
|
||||
case b.Y == y || b.Y == y-1 || b.Y == y+1:
|
||||
return DirY
|
||||
case b.Y2 == y || b.Y2 == y-1 || b.Y2 == y+1:
|
||||
return DirY2
|
||||
}
|
||||
return DirNone
|
||||
}
|
||||
|
||||
func (ib *ImageViewBox) resizeToDirection(b *ImageBox, x, y int) {
|
||||
switch ib.resize {
|
||||
case DirX:
|
||||
b.X = x
|
||||
case DirY:
|
||||
b.Y = y
|
||||
case DirX2:
|
||||
b.X2 = x
|
||||
case DirY2:
|
||||
b.Y2 = y
|
||||
}
|
||||
}
|
||||
|
||||
func (ib *ImageViewBox) drag(b *ImageBox, x, y int) {
|
||||
w, h := b.X2-b.X, b.Y2-b.Y
|
||||
|
||||
nx := ib.dragStartX - b.X
|
||||
ny := ib.dragStartY - b.Y
|
||||
|
||||
b.X = x - nx
|
||||
b.Y = y - ny
|
||||
b.X2 = b.X + w
|
||||
b.Y2 = b.Y + h
|
||||
|
||||
ib.dragStartX, ib.dragStartY = x, y
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) DrawImageFile(filepath string) (err error) {
|
||||
iv.bmp, err = NewBitmapFromFile(filepath, RGB(255, 255, 0))
|
||||
iv.selBox = nil
|
||||
iv.modified = false
|
||||
iv.onSelectedChange.Fire(NewEvent(iv, nil))
|
||||
iv.onModify.Fire(NewEvent(iv, nil))
|
||||
return
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) DrawImage(bmp *Bitmap) {
|
||||
iv.bmp = bmp
|
||||
iv.selBox = nil
|
||||
iv.modified = false
|
||||
iv.onSelectedChange.Fire(NewEvent(iv, nil))
|
||||
iv.onModify.Fire(NewEvent(iv, nil))
|
||||
}
|
||||
|
||||
func (iv *ImageViewBox) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_SIZE, w32.WM_SIZING:
|
||||
iv.Invalidate(true)
|
||||
|
||||
case w32.WM_ERASEBKGND:
|
||||
return 1 // important
|
||||
|
||||
case w32.WM_CREATE:
|
||||
internalTrackMouseEvent(iv.hwnd)
|
||||
|
||||
case w32.WM_PAINT:
|
||||
if iv.bmp != nil {
|
||||
canvas := NewCanvasFromHwnd(iv.hwnd)
|
||||
defer canvas.Dispose()
|
||||
iv.SetSize(iv.bmp.Size())
|
||||
canvas.DrawBitmap(iv.bmp, 0, 0)
|
||||
|
||||
for _, b := range iv.Boxes {
|
||||
// old code used NewSystemColorBrush(w32.COLOR_BTNFACE) w32.COLOR_WINDOW
|
||||
pen := ImageBoxPen
|
||||
if b.underMouse {
|
||||
pen = ImageBoxHiPen
|
||||
}
|
||||
canvas.DrawRect(b.Rect(), pen)
|
||||
|
||||
if b == iv.selBox {
|
||||
x1 := []int{b.X, b.X2, b.X2, b.X}
|
||||
y1 := []int{b.Y, b.Y, b.Y2, b.Y2}
|
||||
|
||||
for i := 0; i < len(x1); i++ {
|
||||
r := NewRect(x1[i]-2, y1[i]-2, x1[i]+2, y1[i]+2)
|
||||
canvas.DrawFillRect(r, ImageBoxMarkPen, ImageBoxMarkBrush)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case w32.WM_MOUSEMOVE:
|
||||
x, y := genPoint(lparam)
|
||||
|
||||
if iv.dragBox != nil {
|
||||
if iv.resize == DirNone {
|
||||
iv.drag(iv.dragBox, x, y)
|
||||
iv.wasModified()
|
||||
} else {
|
||||
iv.resizeToDirection(iv.dragBox, x, y)
|
||||
iv.wasModified()
|
||||
}
|
||||
iv.Invalidate(true)
|
||||
|
||||
} else {
|
||||
if !iv.add {
|
||||
w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(iv.getCursor(x, y))))
|
||||
}
|
||||
// do not call repaint if underMouse item did not change.
|
||||
if iv.updateHighlight(x, y) {
|
||||
iv.Invalidate(true)
|
||||
}
|
||||
}
|
||||
|
||||
if iv.mouseLeft {
|
||||
internalTrackMouseEvent(iv.hwnd)
|
||||
iv.mouseLeft = false
|
||||
}
|
||||
|
||||
case w32.WM_MOUSELEAVE:
|
||||
iv.dragBox = nil
|
||||
iv.mouseLeft = true
|
||||
iv.updateHighlight(-1, -1)
|
||||
iv.Invalidate(true)
|
||||
|
||||
case w32.WM_LBUTTONUP:
|
||||
iv.dragBox = nil
|
||||
|
||||
case w32.WM_LBUTTONDOWN:
|
||||
x, y := genPoint(lparam)
|
||||
if iv.add {
|
||||
now := time.Now()
|
||||
s := fmt.Sprintf("field%s", now.Format("020405"))
|
||||
b := &ImageBox{Name: s, underMouse: true, X: x, Y: y, X2: x + 150, Y2: y + 30}
|
||||
iv.Boxes = append(iv.Boxes, b)
|
||||
iv.selBox = b
|
||||
iv.wasModified()
|
||||
iv.onAdd.Fire(NewEvent(iv, nil))
|
||||
} else {
|
||||
iv.dragBox = iv.isUnderMouse(x, y)
|
||||
iv.selBox = iv.dragBox
|
||||
iv.dragStartX, iv.dragStartY = x, y
|
||||
iv.resize = iv.resizingDirection(iv.dragBox, x, y)
|
||||
}
|
||||
iv.Invalidate(true)
|
||||
iv.onSelectedChange.Fire(NewEvent(iv, nil))
|
||||
|
||||
case w32.WM_RBUTTONDOWN:
|
||||
|
||||
}
|
||||
return w32.DefWindowProc(iv.hwnd, msg, wparam, lparam)
|
||||
}
|
19
v2/internal/frontend/desktop/windows/winc/init.go
Normal file
19
v2/internal/frontend/desktop/windows/winc/init.go
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gControllerRegistry = make(map[w32.HWND]Controller)
|
||||
gRegisteredClasses = make([]string, 0)
|
||||
|
||||
var si w32.GdiplusStartupInput
|
||||
si.GdiplusVersion = 1
|
||||
w32.GdiplusStartup(&si, nil)
|
||||
}
|
438
v2/internal/frontend/desktop/windows/winc/keyboard.go
Normal file
438
v2/internal/frontend/desktop/windows/winc/keyboard.go
Normal file
@ -0,0 +1,438 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type Key uint16
|
||||
|
||||
func (k Key) String() string {
|
||||
return key2string[k]
|
||||
}
|
||||
|
||||
const (
|
||||
KeyLButton Key = w32.VK_LBUTTON
|
||||
KeyRButton Key = w32.VK_RBUTTON
|
||||
KeyCancel Key = w32.VK_CANCEL
|
||||
KeyMButton Key = w32.VK_MBUTTON
|
||||
KeyXButton1 Key = w32.VK_XBUTTON1
|
||||
KeyXButton2 Key = w32.VK_XBUTTON2
|
||||
KeyBack Key = w32.VK_BACK
|
||||
KeyTab Key = w32.VK_TAB
|
||||
KeyClear Key = w32.VK_CLEAR
|
||||
KeyReturn Key = w32.VK_RETURN
|
||||
KeyShift Key = w32.VK_SHIFT
|
||||
KeyControl Key = w32.VK_CONTROL
|
||||
KeyAlt Key = w32.VK_MENU
|
||||
KeyMenu Key = w32.VK_MENU
|
||||
KeyPause Key = w32.VK_PAUSE
|
||||
KeyCapital Key = w32.VK_CAPITAL
|
||||
KeyKana Key = w32.VK_KANA
|
||||
KeyHangul Key = w32.VK_HANGUL
|
||||
KeyJunja Key = w32.VK_JUNJA
|
||||
KeyFinal Key = w32.VK_FINAL
|
||||
KeyHanja Key = w32.VK_HANJA
|
||||
KeyKanji Key = w32.VK_KANJI
|
||||
KeyEscape Key = w32.VK_ESCAPE
|
||||
KeyConvert Key = w32.VK_CONVERT
|
||||
KeyNonconvert Key = w32.VK_NONCONVERT
|
||||
KeyAccept Key = w32.VK_ACCEPT
|
||||
KeyModeChange Key = w32.VK_MODECHANGE
|
||||
KeySpace Key = w32.VK_SPACE
|
||||
KeyPrior Key = w32.VK_PRIOR
|
||||
KeyNext Key = w32.VK_NEXT
|
||||
KeyEnd Key = w32.VK_END
|
||||
KeyHome Key = w32.VK_HOME
|
||||
KeyLeft Key = w32.VK_LEFT
|
||||
KeyUp Key = w32.VK_UP
|
||||
KeyRight Key = w32.VK_RIGHT
|
||||
KeyDown Key = w32.VK_DOWN
|
||||
KeySelect Key = w32.VK_SELECT
|
||||
KeyPrint Key = w32.VK_PRINT
|
||||
KeyExecute Key = w32.VK_EXECUTE
|
||||
KeySnapshot Key = w32.VK_SNAPSHOT
|
||||
KeyInsert Key = w32.VK_INSERT
|
||||
KeyDelete Key = w32.VK_DELETE
|
||||
KeyHelp Key = w32.VK_HELP
|
||||
Key0 Key = 0x30
|
||||
Key1 Key = 0x31
|
||||
Key2 Key = 0x32
|
||||
Key3 Key = 0x33
|
||||
Key4 Key = 0x34
|
||||
Key5 Key = 0x35
|
||||
Key6 Key = 0x36
|
||||
Key7 Key = 0x37
|
||||
Key8 Key = 0x38
|
||||
Key9 Key = 0x39
|
||||
KeyA Key = 0x41
|
||||
KeyB Key = 0x42
|
||||
KeyC Key = 0x43
|
||||
KeyD Key = 0x44
|
||||
KeyE Key = 0x45
|
||||
KeyF Key = 0x46
|
||||
KeyG Key = 0x47
|
||||
KeyH Key = 0x48
|
||||
KeyI Key = 0x49
|
||||
KeyJ Key = 0x4A
|
||||
KeyK Key = 0x4B
|
||||
KeyL Key = 0x4C
|
||||
KeyM Key = 0x4D
|
||||
KeyN Key = 0x4E
|
||||
KeyO Key = 0x4F
|
||||
KeyP Key = 0x50
|
||||
KeyQ Key = 0x51
|
||||
KeyR Key = 0x52
|
||||
KeyS Key = 0x53
|
||||
KeyT Key = 0x54
|
||||
KeyU Key = 0x55
|
||||
KeyV Key = 0x56
|
||||
KeyW Key = 0x57
|
||||
KeyX Key = 0x58
|
||||
KeyY Key = 0x59
|
||||
KeyZ Key = 0x5A
|
||||
KeyLWIN Key = w32.VK_LWIN
|
||||
KeyRWIN Key = w32.VK_RWIN
|
||||
KeyApps Key = w32.VK_APPS
|
||||
KeySleep Key = w32.VK_SLEEP
|
||||
KeyNumpad0 Key = w32.VK_NUMPAD0
|
||||
KeyNumpad1 Key = w32.VK_NUMPAD1
|
||||
KeyNumpad2 Key = w32.VK_NUMPAD2
|
||||
KeyNumpad3 Key = w32.VK_NUMPAD3
|
||||
KeyNumpad4 Key = w32.VK_NUMPAD4
|
||||
KeyNumpad5 Key = w32.VK_NUMPAD5
|
||||
KeyNumpad6 Key = w32.VK_NUMPAD6
|
||||
KeyNumpad7 Key = w32.VK_NUMPAD7
|
||||
KeyNumpad8 Key = w32.VK_NUMPAD8
|
||||
KeyNumpad9 Key = w32.VK_NUMPAD9
|
||||
KeyMultiply Key = w32.VK_MULTIPLY
|
||||
KeyAdd Key = w32.VK_ADD
|
||||
KeySeparator Key = w32.VK_SEPARATOR
|
||||
KeySubtract Key = w32.VK_SUBTRACT
|
||||
KeyDecimal Key = w32.VK_DECIMAL
|
||||
KeyDivide Key = w32.VK_DIVIDE
|
||||
KeyF1 Key = w32.VK_F1
|
||||
KeyF2 Key = w32.VK_F2
|
||||
KeyF3 Key = w32.VK_F3
|
||||
KeyF4 Key = w32.VK_F4
|
||||
KeyF5 Key = w32.VK_F5
|
||||
KeyF6 Key = w32.VK_F6
|
||||
KeyF7 Key = w32.VK_F7
|
||||
KeyF8 Key = w32.VK_F8
|
||||
KeyF9 Key = w32.VK_F9
|
||||
KeyF10 Key = w32.VK_F10
|
||||
KeyF11 Key = w32.VK_F11
|
||||
KeyF12 Key = w32.VK_F12
|
||||
KeyF13 Key = w32.VK_F13
|
||||
KeyF14 Key = w32.VK_F14
|
||||
KeyF15 Key = w32.VK_F15
|
||||
KeyF16 Key = w32.VK_F16
|
||||
KeyF17 Key = w32.VK_F17
|
||||
KeyF18 Key = w32.VK_F18
|
||||
KeyF19 Key = w32.VK_F19
|
||||
KeyF20 Key = w32.VK_F20
|
||||
KeyF21 Key = w32.VK_F21
|
||||
KeyF22 Key = w32.VK_F22
|
||||
KeyF23 Key = w32.VK_F23
|
||||
KeyF24 Key = w32.VK_F24
|
||||
KeyNumlock Key = w32.VK_NUMLOCK
|
||||
KeyScroll Key = w32.VK_SCROLL
|
||||
KeyLShift Key = w32.VK_LSHIFT
|
||||
KeyRShift Key = w32.VK_RSHIFT
|
||||
KeyLControl Key = w32.VK_LCONTROL
|
||||
KeyRControl Key = w32.VK_RCONTROL
|
||||
KeyLAlt Key = w32.VK_LMENU
|
||||
KeyLMenu Key = w32.VK_LMENU
|
||||
KeyRAlt Key = w32.VK_RMENU
|
||||
KeyRMenu Key = w32.VK_RMENU
|
||||
KeyBrowserBack Key = w32.VK_BROWSER_BACK
|
||||
KeyBrowserForward Key = w32.VK_BROWSER_FORWARD
|
||||
KeyBrowserRefresh Key = w32.VK_BROWSER_REFRESH
|
||||
KeyBrowserStop Key = w32.VK_BROWSER_STOP
|
||||
KeyBrowserSearch Key = w32.VK_BROWSER_SEARCH
|
||||
KeyBrowserFavorites Key = w32.VK_BROWSER_FAVORITES
|
||||
KeyBrowserHome Key = w32.VK_BROWSER_HOME
|
||||
KeyVolumeMute Key = w32.VK_VOLUME_MUTE
|
||||
KeyVolumeDown Key = w32.VK_VOLUME_DOWN
|
||||
KeyVolumeUp Key = w32.VK_VOLUME_UP
|
||||
KeyMediaNextTrack Key = w32.VK_MEDIA_NEXT_TRACK
|
||||
KeyMediaPrevTrack Key = w32.VK_MEDIA_PREV_TRACK
|
||||
KeyMediaStop Key = w32.VK_MEDIA_STOP
|
||||
KeyMediaPlayPause Key = w32.VK_MEDIA_PLAY_PAUSE
|
||||
KeyLaunchMail Key = w32.VK_LAUNCH_MAIL
|
||||
KeyLaunchMediaSelect Key = w32.VK_LAUNCH_MEDIA_SELECT
|
||||
KeyLaunchApp1 Key = w32.VK_LAUNCH_APP1
|
||||
KeyLaunchApp2 Key = w32.VK_LAUNCH_APP2
|
||||
KeyOEM1 Key = w32.VK_OEM_1
|
||||
KeyOEMPlus Key = w32.VK_OEM_PLUS
|
||||
KeyOEMComma Key = w32.VK_OEM_COMMA
|
||||
KeyOEMMinus Key = w32.VK_OEM_MINUS
|
||||
KeyOEMPeriod Key = w32.VK_OEM_PERIOD
|
||||
KeyOEM2 Key = w32.VK_OEM_2
|
||||
KeyOEM3 Key = w32.VK_OEM_3
|
||||
KeyOEM4 Key = w32.VK_OEM_4
|
||||
KeyOEM5 Key = w32.VK_OEM_5
|
||||
KeyOEM6 Key = w32.VK_OEM_6
|
||||
KeyOEM7 Key = w32.VK_OEM_7
|
||||
KeyOEM8 Key = w32.VK_OEM_8
|
||||
KeyOEM102 Key = w32.VK_OEM_102
|
||||
KeyProcessKey Key = w32.VK_PROCESSKEY
|
||||
KeyPacket Key = w32.VK_PACKET
|
||||
KeyAttn Key = w32.VK_ATTN
|
||||
KeyCRSel Key = w32.VK_CRSEL
|
||||
KeyEXSel Key = w32.VK_EXSEL
|
||||
KeyErEOF Key = w32.VK_EREOF
|
||||
KeyPlay Key = w32.VK_PLAY
|
||||
KeyZoom Key = w32.VK_ZOOM
|
||||
KeyNoName Key = w32.VK_NONAME
|
||||
KeyPA1 Key = w32.VK_PA1
|
||||
KeyOEMClear Key = w32.VK_OEM_CLEAR
|
||||
)
|
||||
|
||||
var key2string = map[Key]string{
|
||||
KeyLButton: "LButton",
|
||||
KeyRButton: "RButton",
|
||||
KeyCancel: "Cancel",
|
||||
KeyMButton: "MButton",
|
||||
KeyXButton1: "XButton1",
|
||||
KeyXButton2: "XButton2",
|
||||
KeyBack: "Back",
|
||||
KeyTab: "Tab",
|
||||
KeyClear: "Clear",
|
||||
KeyReturn: "Return",
|
||||
KeyShift: "Shift",
|
||||
KeyControl: "Control",
|
||||
KeyAlt: "Alt / Menu",
|
||||
KeyPause: "Pause",
|
||||
KeyCapital: "Capital",
|
||||
KeyKana: "Kana / Hangul",
|
||||
KeyJunja: "Junja",
|
||||
KeyFinal: "Final",
|
||||
KeyHanja: "Hanja / Kanji",
|
||||
KeyEscape: "Escape",
|
||||
KeyConvert: "Convert",
|
||||
KeyNonconvert: "Nonconvert",
|
||||
KeyAccept: "Accept",
|
||||
KeyModeChange: "ModeChange",
|
||||
KeySpace: "Space",
|
||||
KeyPrior: "Prior",
|
||||
KeyNext: "Next",
|
||||
KeyEnd: "End",
|
||||
KeyHome: "Home",
|
||||
KeyLeft: "Left",
|
||||
KeyUp: "Up",
|
||||
KeyRight: "Right",
|
||||
KeyDown: "Down",
|
||||
KeySelect: "Select",
|
||||
KeyPrint: "Print",
|
||||
KeyExecute: "Execute",
|
||||
KeySnapshot: "Snapshot",
|
||||
KeyInsert: "Insert",
|
||||
KeyDelete: "Delete",
|
||||
KeyHelp: "Help",
|
||||
Key0: "0",
|
||||
Key1: "1",
|
||||
Key2: "2",
|
||||
Key3: "3",
|
||||
Key4: "4",
|
||||
Key5: "5",
|
||||
Key6: "6",
|
||||
Key7: "7",
|
||||
Key8: "8",
|
||||
Key9: "9",
|
||||
KeyA: "A",
|
||||
KeyB: "B",
|
||||
KeyC: "C",
|
||||
KeyD: "D",
|
||||
KeyE: "E",
|
||||
KeyF: "F",
|
||||
KeyG: "G",
|
||||
KeyH: "H",
|
||||
KeyI: "I",
|
||||
KeyJ: "J",
|
||||
KeyK: "K",
|
||||
KeyL: "L",
|
||||
KeyM: "M",
|
||||
KeyN: "N",
|
||||
KeyO: "O",
|
||||
KeyP: "P",
|
||||
KeyQ: "Q",
|
||||
KeyR: "R",
|
||||
KeyS: "S",
|
||||
KeyT: "T",
|
||||
KeyU: "U",
|
||||
KeyV: "V",
|
||||
KeyW: "W",
|
||||
KeyX: "X",
|
||||
KeyY: "Y",
|
||||
KeyZ: "Z",
|
||||
KeyLWIN: "LWIN",
|
||||
KeyRWIN: "RWIN",
|
||||
KeyApps: "Apps",
|
||||
KeySleep: "Sleep",
|
||||
KeyNumpad0: "Numpad0",
|
||||
KeyNumpad1: "Numpad1",
|
||||
KeyNumpad2: "Numpad2",
|
||||
KeyNumpad3: "Numpad3",
|
||||
KeyNumpad4: "Numpad4",
|
||||
KeyNumpad5: "Numpad5",
|
||||
KeyNumpad6: "Numpad6",
|
||||
KeyNumpad7: "Numpad7",
|
||||
KeyNumpad8: "Numpad8",
|
||||
KeyNumpad9: "Numpad9",
|
||||
KeyMultiply: "Multiply",
|
||||
KeyAdd: "Add",
|
||||
KeySeparator: "Separator",
|
||||
KeySubtract: "Subtract",
|
||||
KeyDecimal: "Decimal",
|
||||
KeyDivide: "Divide",
|
||||
KeyF1: "F1",
|
||||
KeyF2: "F2",
|
||||
KeyF3: "F3",
|
||||
KeyF4: "F4",
|
||||
KeyF5: "F5",
|
||||
KeyF6: "F6",
|
||||
KeyF7: "F7",
|
||||
KeyF8: "F8",
|
||||
KeyF9: "F9",
|
||||
KeyF10: "F10",
|
||||
KeyF11: "F11",
|
||||
KeyF12: "F12",
|
||||
KeyF13: "F13",
|
||||
KeyF14: "F14",
|
||||
KeyF15: "F15",
|
||||
KeyF16: "F16",
|
||||
KeyF17: "F17",
|
||||
KeyF18: "F18",
|
||||
KeyF19: "F19",
|
||||
KeyF20: "F20",
|
||||
KeyF21: "F21",
|
||||
KeyF22: "F22",
|
||||
KeyF23: "F23",
|
||||
KeyF24: "F24",
|
||||
KeyNumlock: "Numlock",
|
||||
KeyScroll: "Scroll",
|
||||
KeyLShift: "LShift",
|
||||
KeyRShift: "RShift",
|
||||
KeyLControl: "LControl",
|
||||
KeyRControl: "RControl",
|
||||
KeyLMenu: "LMenu",
|
||||
KeyRMenu: "RMenu",
|
||||
KeyBrowserBack: "BrowserBack",
|
||||
KeyBrowserForward: "BrowserForward",
|
||||
KeyBrowserRefresh: "BrowserRefresh",
|
||||
KeyBrowserStop: "BrowserStop",
|
||||
KeyBrowserSearch: "BrowserSearch",
|
||||
KeyBrowserFavorites: "BrowserFavorites",
|
||||
KeyBrowserHome: "BrowserHome",
|
||||
KeyVolumeMute: "VolumeMute",
|
||||
KeyVolumeDown: "VolumeDown",
|
||||
KeyVolumeUp: "VolumeUp",
|
||||
KeyMediaNextTrack: "MediaNextTrack",
|
||||
KeyMediaPrevTrack: "MediaPrevTrack",
|
||||
KeyMediaStop: "MediaStop",
|
||||
KeyMediaPlayPause: "MediaPlayPause",
|
||||
KeyLaunchMail: "LaunchMail",
|
||||
KeyLaunchMediaSelect: "LaunchMediaSelect",
|
||||
KeyLaunchApp1: "LaunchApp1",
|
||||
KeyLaunchApp2: "LaunchApp2",
|
||||
KeyOEM1: "OEM1",
|
||||
KeyOEMPlus: "OEMPlus",
|
||||
KeyOEMComma: "OEMComma",
|
||||
KeyOEMMinus: "OEMMinus",
|
||||
KeyOEMPeriod: "OEMPeriod",
|
||||
KeyOEM2: "OEM2",
|
||||
KeyOEM3: "OEM3",
|
||||
KeyOEM4: "OEM4",
|
||||
KeyOEM5: "OEM5",
|
||||
KeyOEM6: "OEM6",
|
||||
KeyOEM7: "OEM7",
|
||||
KeyOEM8: "OEM8",
|
||||
KeyOEM102: "OEM102",
|
||||
KeyProcessKey: "ProcessKey",
|
||||
KeyPacket: "Packet",
|
||||
KeyAttn: "Attn",
|
||||
KeyCRSel: "CRSel",
|
||||
KeyEXSel: "EXSel",
|
||||
KeyErEOF: "ErEOF",
|
||||
KeyPlay: "Play",
|
||||
KeyZoom: "Zoom",
|
||||
KeyNoName: "NoName",
|
||||
KeyPA1: "PA1",
|
||||
KeyOEMClear: "OEMClear",
|
||||
}
|
||||
|
||||
type Modifiers byte
|
||||
|
||||
func (m Modifiers) String() string {
|
||||
return modifiers2string[m]
|
||||
}
|
||||
|
||||
var modifiers2string = map[Modifiers]string{
|
||||
ModShift: "Shift",
|
||||
ModControl: "Ctrl",
|
||||
ModControl | ModShift: "Ctrl+Shift",
|
||||
ModAlt: "Alt",
|
||||
ModAlt | ModShift: "Alt+Shift",
|
||||
ModAlt | ModControl | ModShift: "Alt+Ctrl+Shift",
|
||||
}
|
||||
|
||||
const (
|
||||
ModShift Modifiers = 1 << iota
|
||||
ModControl
|
||||
ModAlt
|
||||
)
|
||||
|
||||
func ModifiersDown() Modifiers {
|
||||
var m Modifiers
|
||||
|
||||
if ShiftDown() {
|
||||
m |= ModShift
|
||||
}
|
||||
if ControlDown() {
|
||||
m |= ModControl
|
||||
}
|
||||
if AltDown() {
|
||||
m |= ModAlt
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
type Shortcut struct {
|
||||
Modifiers Modifiers
|
||||
Key Key
|
||||
}
|
||||
|
||||
func (s Shortcut) String() string {
|
||||
m := s.Modifiers.String()
|
||||
if m == "" {
|
||||
return s.Key.String()
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
b.WriteString(m)
|
||||
b.WriteRune('+')
|
||||
b.WriteString(s.Key.String())
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func AltDown() bool {
|
||||
return w32.GetKeyState(int32(KeyAlt))>>15 != 0
|
||||
}
|
||||
|
||||
func ControlDown() bool {
|
||||
return w32.GetKeyState(int32(KeyControl))>>15 != 0
|
||||
}
|
||||
|
||||
func ShiftDown() bool {
|
||||
return w32.GetKeyState(int32(KeyShift))>>15 != 0
|
||||
}
|
29
v2/internal/frontend/desktop/windows/winc/label.go
Normal file
29
v2/internal/frontend/desktop/windows/winc/label.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type Label struct {
|
||||
ControlBase
|
||||
}
|
||||
|
||||
func NewLabel(parent Controller) *Label {
|
||||
lb := new(Label)
|
||||
|
||||
lb.InitControl("STATIC", parent, 0, w32.WS_CHILD|w32.WS_VISIBLE|w32.SS_LEFTNOWORDWRAP)
|
||||
RegMsgHandler(lb)
|
||||
|
||||
lb.SetFont(DefaultFont)
|
||||
lb.SetText("Label")
|
||||
lb.SetSize(100, 25)
|
||||
return lb
|
||||
}
|
||||
|
||||
func (lb *Label) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
return w32.DefWindowProc(lb.hwnd, msg, wparam, lparam)
|
||||
}
|
220
v2/internal/frontend/desktop/windows/winc/layout.go
Normal file
220
v2/internal/frontend/desktop/windows/winc/layout.go
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
// Dockable component must satisfy interface to be docked.
|
||||
type Dockable interface {
|
||||
Handle() w32.HWND
|
||||
|
||||
Pos() (x, y int)
|
||||
Width() int
|
||||
Height() int
|
||||
Visible() bool
|
||||
|
||||
SetPos(x, y int)
|
||||
SetSize(width, height int)
|
||||
|
||||
OnMouseMove() *EventManager
|
||||
OnLBUp() *EventManager
|
||||
}
|
||||
|
||||
// DockAllow is window, panel or other component that satisfies interface.
|
||||
type DockAllow interface {
|
||||
Handle() w32.HWND
|
||||
ClientWidth() int
|
||||
ClientHeight() int
|
||||
SetLayout(mng LayoutManager)
|
||||
}
|
||||
|
||||
// Various layout managers
|
||||
type Direction int
|
||||
|
||||
const (
|
||||
Top Direction = iota
|
||||
Bottom
|
||||
Left
|
||||
Right
|
||||
Fill
|
||||
)
|
||||
|
||||
type LayoutControl struct {
|
||||
child Dockable
|
||||
dir Direction
|
||||
}
|
||||
|
||||
type LayoutControls []*LayoutControl
|
||||
|
||||
type SimpleDock struct {
|
||||
parent DockAllow
|
||||
layoutCtl LayoutControls
|
||||
loadedState bool
|
||||
}
|
||||
|
||||
// DockState gets saved and loaded from json
|
||||
type CtlState struct {
|
||||
X, Y, Width, Height int
|
||||
}
|
||||
|
||||
type LayoutState struct {
|
||||
WindowState string
|
||||
Controls []*CtlState
|
||||
}
|
||||
|
||||
func (lc LayoutControls) Len() int { return len(lc) }
|
||||
func (lc LayoutControls) Swap(i, j int) { lc[i], lc[j] = lc[j], lc[i] }
|
||||
func (lc LayoutControls) Less(i, j int) bool { return lc[i].dir < lc[j].dir }
|
||||
|
||||
func NewSimpleDock(parent DockAllow) *SimpleDock {
|
||||
d := &SimpleDock{parent: parent}
|
||||
parent.SetLayout(d)
|
||||
return d
|
||||
}
|
||||
|
||||
// Layout management for the child controls.
|
||||
func (sd *SimpleDock) Dock(child Dockable, dir Direction) {
|
||||
sd.layoutCtl = append(sd.layoutCtl, &LayoutControl{child, dir})
|
||||
}
|
||||
|
||||
// SaveState of the layout. Only works for Docks with parent set to main form.
|
||||
func (sd *SimpleDock) SaveState(w io.Writer) error {
|
||||
var ls LayoutState
|
||||
|
||||
var wp w32.WINDOWPLACEMENT
|
||||
wp.Length = uint32(unsafe.Sizeof(wp))
|
||||
if !w32.GetWindowPlacement(sd.parent.Handle(), &wp) {
|
||||
return fmt.Errorf("GetWindowPlacement failed")
|
||||
}
|
||||
|
||||
ls.WindowState = fmt.Sprint(
|
||||
wp.Flags, wp.ShowCmd,
|
||||
wp.PtMinPosition.X, wp.PtMinPosition.Y,
|
||||
wp.PtMaxPosition.X, wp.PtMaxPosition.Y,
|
||||
wp.RcNormalPosition.Left, wp.RcNormalPosition.Top,
|
||||
wp.RcNormalPosition.Right, wp.RcNormalPosition.Bottom)
|
||||
|
||||
for _, c := range sd.layoutCtl {
|
||||
x, y := c.child.Pos()
|
||||
w, h := c.child.Width(), c.child.Height()
|
||||
|
||||
ctl := &CtlState{X: x, Y: y, Width: w, Height: h}
|
||||
ls.Controls = append(ls.Controls, ctl)
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(ls); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadState of the layout. Only works for Docks with parent set to main form.
|
||||
func (sd *SimpleDock) LoadState(r io.Reader) error {
|
||||
var ls LayoutState
|
||||
|
||||
if err := json.NewDecoder(r).Decode(&ls); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var wp w32.WINDOWPLACEMENT
|
||||
if _, err := fmt.Sscan(ls.WindowState,
|
||||
&wp.Flags, &wp.ShowCmd,
|
||||
&wp.PtMinPosition.X, &wp.PtMinPosition.Y,
|
||||
&wp.PtMaxPosition.X, &wp.PtMaxPosition.Y,
|
||||
&wp.RcNormalPosition.Left, &wp.RcNormalPosition.Top,
|
||||
&wp.RcNormalPosition.Right, &wp.RcNormalPosition.Bottom); err != nil {
|
||||
return err
|
||||
}
|
||||
wp.Length = uint32(unsafe.Sizeof(wp))
|
||||
|
||||
if !w32.SetWindowPlacement(sd.parent.Handle(), &wp) {
|
||||
return fmt.Errorf("SetWindowPlacement failed")
|
||||
}
|
||||
|
||||
// if number of controls in the saved layout does not match
|
||||
// current number on screen - something changed and we do not reload
|
||||
// rest of control sizes from json
|
||||
if len(sd.layoutCtl) != len(ls.Controls) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, c := range sd.layoutCtl {
|
||||
c.child.SetPos(ls.Controls[i].X, ls.Controls[i].Y)
|
||||
c.child.SetSize(ls.Controls[i].Width, ls.Controls[i].Height)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveStateFile convenience function.
|
||||
func (sd *SimpleDock) SaveStateFile(file string) error {
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sd.SaveState(f)
|
||||
}
|
||||
|
||||
// LoadStateFile loads state ignores error if file is not found.
|
||||
func (sd *SimpleDock) LoadStateFile(file string) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil // if file is not found or not accessible ignore it
|
||||
}
|
||||
return sd.LoadState(f)
|
||||
}
|
||||
|
||||
// Update is called to resize child items based on layout directions.
|
||||
func (sd *SimpleDock) Update() {
|
||||
sort.Stable(sd.layoutCtl)
|
||||
|
||||
x, y := 0, 0
|
||||
w, h := sd.parent.ClientWidth(), sd.parent.ClientHeight()
|
||||
winw, winh := w, h
|
||||
|
||||
for _, c := range sd.layoutCtl {
|
||||
// Non visible controls do not preserve space.
|
||||
if !c.child.Visible() {
|
||||
continue
|
||||
}
|
||||
|
||||
switch c.dir {
|
||||
case Top:
|
||||
c.child.SetPos(x, y)
|
||||
c.child.SetSize(w, c.child.Height())
|
||||
h -= c.child.Height()
|
||||
y += c.child.Height()
|
||||
case Bottom:
|
||||
c.child.SetPos(x, winh-c.child.Height())
|
||||
c.child.SetSize(w, c.child.Height())
|
||||
h -= c.child.Height()
|
||||
winh -= c.child.Height()
|
||||
case Left:
|
||||
c.child.SetPos(x, y)
|
||||
c.child.SetSize(c.child.Width(), h)
|
||||
w -= c.child.Width()
|
||||
x += c.child.Width()
|
||||
case Right:
|
||||
c.child.SetPos(winw-c.child.Width(), y)
|
||||
c.child.SetSize(c.child.Width(), h)
|
||||
w -= c.child.Width()
|
||||
winw -= c.child.Width()
|
||||
case Fill:
|
||||
// fill available space
|
||||
c.child.SetPos(x, y)
|
||||
c.child.SetSize(w, h)
|
||||
}
|
||||
//c.child.Invalidate(true)
|
||||
}
|
||||
}
|
547
v2/internal/frontend/desktop/windows/winc/listview.go
Normal file
547
v2/internal/frontend/desktop/windows/winc/listview.go
Normal file
@ -0,0 +1,547 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
// ListItem represents an item in a ListView widget.
|
||||
type ListItem interface {
|
||||
Text() []string // Text returns the text of the multi-column item.
|
||||
ImageIndex() int // ImageIndex is used only if SetImageList is called on the listview
|
||||
}
|
||||
|
||||
// ListItemChecker is used for checkbox support in ListView.
|
||||
type ListItemChecker interface {
|
||||
Checked() bool
|
||||
SetChecked(checked bool)
|
||||
}
|
||||
|
||||
// ListItemSetter is used in OnEndLabelEdit event.
|
||||
type ListItemSetter interface {
|
||||
SetText(s string) // set first item in the array via LabelEdit event
|
||||
}
|
||||
|
||||
// StringListItem is helper for basic string lists.
|
||||
type StringListItem struct {
|
||||
ID int
|
||||
Data string
|
||||
Check bool
|
||||
}
|
||||
|
||||
func (s StringListItem) Text() []string { return []string{s.Data} }
|
||||
func (s StringListItem) Checked() bool { return s.Check }
|
||||
func (s StringListItem) SetChecked(checked bool) { s.Check = checked }
|
||||
func (s StringListItem) ImageIndex() int { return 0 }
|
||||
|
||||
type ListView struct {
|
||||
ControlBase
|
||||
|
||||
iml *ImageList
|
||||
lastIndex int
|
||||
cols int // count of columns
|
||||
|
||||
item2Handle map[ListItem]uintptr
|
||||
handle2Item map[uintptr]ListItem
|
||||
|
||||
onEndLabelEdit EventManager
|
||||
onDoubleClick EventManager
|
||||
onClick EventManager
|
||||
onKeyDown EventManager
|
||||
onItemChanging EventManager
|
||||
onItemChanged EventManager
|
||||
onCheckChanged EventManager
|
||||
onViewChange EventManager
|
||||
onEndScroll EventManager
|
||||
}
|
||||
|
||||
func NewListView(parent Controller) *ListView {
|
||||
lv := new(ListView)
|
||||
|
||||
lv.InitControl("SysListView32", parent /*w32.WS_EX_CLIENTEDGE*/, 0,
|
||||
w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_TABSTOP|w32.LVS_REPORT|w32.LVS_EDITLABELS|w32.LVS_SHOWSELALWAYS)
|
||||
|
||||
lv.item2Handle = make(map[ListItem]uintptr)
|
||||
lv.handle2Item = make(map[uintptr]ListItem)
|
||||
|
||||
RegMsgHandler(lv)
|
||||
|
||||
lv.SetFont(DefaultFont)
|
||||
lv.SetSize(200, 400)
|
||||
|
||||
if err := lv.SetTheme("Explorer"); err != nil {
|
||||
// theme error is ignored
|
||||
}
|
||||
return lv
|
||||
}
|
||||
|
||||
// FIXME: Changes the state of an item in a list-view control. Refer LVM_SETITEMSTATE message.
|
||||
func (lv *ListView) setItemState(i int, state, mask uint) {
|
||||
var item w32.LVITEM
|
||||
item.State, item.StateMask = uint32(state), uint32(mask)
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SETITEMSTATE, uintptr(i), uintptr(unsafe.Pointer(&item)))
|
||||
}
|
||||
|
||||
func (lv *ListView) EnableSingleSelect(enable bool) {
|
||||
SetStyle(lv.hwnd, enable, w32.LVS_SINGLESEL)
|
||||
}
|
||||
|
||||
func (lv *ListView) EnableSortHeader(enable bool) {
|
||||
SetStyle(lv.hwnd, enable, w32.LVS_NOSORTHEADER)
|
||||
}
|
||||
|
||||
func (lv *ListView) EnableSortAscending(enable bool) {
|
||||
SetStyle(lv.hwnd, enable, w32.LVS_SORTASCENDING)
|
||||
}
|
||||
|
||||
func (lv *ListView) EnableEditLabels(enable bool) {
|
||||
SetStyle(lv.hwnd, enable, w32.LVS_EDITLABELS)
|
||||
}
|
||||
|
||||
func (lv *ListView) EnableFullRowSelect(enable bool) {
|
||||
if enable {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_FULLROWSELECT)
|
||||
} else {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_FULLROWSELECT, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (lv *ListView) EnableDoubleBuffer(enable bool) {
|
||||
if enable {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_DOUBLEBUFFER)
|
||||
} else {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_DOUBLEBUFFER, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (lv *ListView) EnableHotTrack(enable bool) {
|
||||
if enable {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_TRACKSELECT)
|
||||
} else {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_TRACKSELECT, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (lv *ListView) SetItemCount(count int) bool {
|
||||
return w32.SendMessage(lv.hwnd, w32.LVM_SETITEMCOUNT, uintptr(count), 0) != 0
|
||||
}
|
||||
|
||||
func (lv *ListView) ItemCount() int {
|
||||
return int(w32.SendMessage(lv.hwnd, w32.LVM_GETITEMCOUNT, 0, 0))
|
||||
}
|
||||
|
||||
func (lv *ListView) ItemAt(x, y int) ListItem {
|
||||
hti := w32.LVHITTESTINFO{Pt: w32.POINT{int32(x), int32(y)}}
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_HITTEST, 0, uintptr(unsafe.Pointer(&hti)))
|
||||
return lv.findItemByIndex(int(hti.IItem))
|
||||
}
|
||||
|
||||
func (lv *ListView) Items() (list []ListItem) {
|
||||
for item := range lv.item2Handle {
|
||||
list = append(list, item)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (lv *ListView) AddColumn(caption string, width int) {
|
||||
var lc w32.LVCOLUMN
|
||||
lc.Mask = w32.LVCF_TEXT
|
||||
if width != 0 {
|
||||
lc.Mask = lc.Mask | w32.LVCF_WIDTH
|
||||
lc.Cx = int32(width)
|
||||
}
|
||||
lc.PszText = syscall.StringToUTF16Ptr(caption)
|
||||
lv.insertLvColumn(&lc, lv.cols)
|
||||
lv.cols++
|
||||
}
|
||||
|
||||
// StretchLastColumn makes the last column take up all remaining horizontal
|
||||
// space of the *ListView.
|
||||
// The effect of this is not persistent.
|
||||
func (lv *ListView) StretchLastColumn() error {
|
||||
if lv.cols == 0 {
|
||||
return nil
|
||||
}
|
||||
if w32.SendMessage(lv.hwnd, w32.LVM_SETCOLUMNWIDTH, uintptr(lv.cols-1), w32.LVSCW_AUTOSIZE_USEHEADER) == 0 {
|
||||
//panic("LVM_SETCOLUMNWIDTH failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckBoxes returns if the *TableView has check boxes.
|
||||
func (lv *ListView) CheckBoxes() bool {
|
||||
return w32.SendMessage(lv.hwnd, w32.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0)&w32.LVS_EX_CHECKBOXES > 0
|
||||
}
|
||||
|
||||
// SetCheckBoxes sets if the *TableView has check boxes.
|
||||
func (lv *ListView) SetCheckBoxes(value bool) {
|
||||
exStyle := w32.SendMessage(lv.hwnd, w32.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0)
|
||||
oldStyle := exStyle
|
||||
if value {
|
||||
exStyle |= w32.LVS_EX_CHECKBOXES
|
||||
} else {
|
||||
exStyle &^= w32.LVS_EX_CHECKBOXES
|
||||
}
|
||||
if exStyle != oldStyle {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, exStyle)
|
||||
}
|
||||
|
||||
mask := w32.SendMessage(lv.hwnd, w32.LVM_GETCALLBACKMASK, 0, 0)
|
||||
if value {
|
||||
mask |= w32.LVIS_STATEIMAGEMASK
|
||||
} else {
|
||||
mask &^= w32.LVIS_STATEIMAGEMASK
|
||||
}
|
||||
|
||||
if w32.SendMessage(lv.hwnd, w32.LVM_SETCALLBACKMASK, mask, 0) == w32.FALSE {
|
||||
panic("SendMessage(LVM_SETCALLBACKMASK)")
|
||||
}
|
||||
}
|
||||
|
||||
func (lv *ListView) applyImage(lc *w32.LVITEM, imIndex int) {
|
||||
if lv.iml != nil {
|
||||
lc.Mask |= w32.LVIF_IMAGE
|
||||
lc.IImage = int32(imIndex)
|
||||
}
|
||||
}
|
||||
|
||||
func (lv *ListView) AddItem(item ListItem) {
|
||||
lv.InsertItem(item, lv.ItemCount())
|
||||
}
|
||||
|
||||
func (lv *ListView) InsertItem(item ListItem, index int) {
|
||||
text := item.Text()
|
||||
li := &w32.LVITEM{
|
||||
Mask: w32.LVIF_TEXT | w32.LVIF_PARAM,
|
||||
PszText: syscall.StringToUTF16Ptr(text[0]),
|
||||
IItem: int32(index),
|
||||
}
|
||||
|
||||
lv.lastIndex++
|
||||
ix := new(int)
|
||||
*ix = lv.lastIndex
|
||||
li.LParam = uintptr(*ix)
|
||||
lv.handle2Item[li.LParam] = item
|
||||
lv.item2Handle[item] = li.LParam
|
||||
|
||||
lv.applyImage(li, item.ImageIndex())
|
||||
lv.insertLvItem(li)
|
||||
|
||||
for i := 1; i < len(text); i++ {
|
||||
li.Mask = w32.LVIF_TEXT
|
||||
li.PszText = syscall.StringToUTF16Ptr(text[i])
|
||||
li.ISubItem = int32(i)
|
||||
lv.setLvItem(li)
|
||||
}
|
||||
}
|
||||
|
||||
func (lv *ListView) UpdateItem(item ListItem) bool {
|
||||
lparam, ok := lv.item2Handle[item]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
index := lv.findIndexByItem(item)
|
||||
if index == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
text := item.Text()
|
||||
li := &w32.LVITEM{
|
||||
Mask: w32.LVIF_TEXT | w32.LVIF_PARAM,
|
||||
PszText: syscall.StringToUTF16Ptr(text[0]),
|
||||
LParam: lparam,
|
||||
IItem: int32(index),
|
||||
}
|
||||
|
||||
lv.applyImage(li, item.ImageIndex())
|
||||
lv.setLvItem(li)
|
||||
|
||||
for i := 1; i < len(text); i++ {
|
||||
li.Mask = w32.LVIF_TEXT
|
||||
li.PszText = syscall.StringToUTF16Ptr(text[i])
|
||||
li.ISubItem = int32(i)
|
||||
lv.setLvItem(li)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (lv *ListView) insertLvColumn(lvColumn *w32.LVCOLUMN, iCol int) {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_INSERTCOLUMN, uintptr(iCol), uintptr(unsafe.Pointer(lvColumn)))
|
||||
}
|
||||
|
||||
func (lv *ListView) insertLvItem(lvItem *w32.LVITEM) {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_INSERTITEM, 0, uintptr(unsafe.Pointer(lvItem)))
|
||||
}
|
||||
|
||||
func (lv *ListView) setLvItem(lvItem *w32.LVITEM) {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SETITEM, 0, uintptr(unsafe.Pointer(lvItem)))
|
||||
}
|
||||
|
||||
func (lv *ListView) DeleteAllItems() bool {
|
||||
if w32.SendMessage(lv.hwnd, w32.LVM_DELETEALLITEMS, 0, 0) == w32.TRUE {
|
||||
lv.item2Handle = make(map[ListItem]uintptr)
|
||||
lv.handle2Item = make(map[uintptr]ListItem)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (lv *ListView) DeleteItem(item ListItem) error {
|
||||
index := lv.findIndexByItem(item)
|
||||
if index == -1 {
|
||||
return errors.New("item not found")
|
||||
}
|
||||
|
||||
if w32.SendMessage(lv.hwnd, w32.LVM_DELETEITEM, uintptr(index), 0) == 0 {
|
||||
return errors.New("SendMessage(TVM_DELETEITEM) failed")
|
||||
}
|
||||
|
||||
h := lv.item2Handle[item]
|
||||
delete(lv.item2Handle, item)
|
||||
delete(lv.handle2Item, h)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lv *ListView) findIndexByItem(item ListItem) int {
|
||||
lparam, ok := lv.item2Handle[item]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
|
||||
it := &w32.LVFINDINFO{
|
||||
Flags: w32.LVFI_PARAM,
|
||||
LParam: lparam,
|
||||
}
|
||||
var i int = -1
|
||||
return int(w32.SendMessage(lv.hwnd, w32.LVM_FINDITEM, uintptr(i), uintptr(unsafe.Pointer(it))))
|
||||
}
|
||||
|
||||
func (lv *ListView) findItemByIndex(i int) ListItem {
|
||||
it := &w32.LVITEM{
|
||||
Mask: w32.LVIF_PARAM,
|
||||
IItem: int32(i),
|
||||
}
|
||||
|
||||
if w32.SendMessage(lv.hwnd, w32.LVM_GETITEM, 0, uintptr(unsafe.Pointer(it))) == w32.TRUE {
|
||||
if item, ok := lv.handle2Item[it.LParam]; ok {
|
||||
return item
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lv *ListView) EnsureVisible(item ListItem) bool {
|
||||
if i := lv.findIndexByItem(item); i != -1 {
|
||||
return w32.SendMessage(lv.hwnd, w32.LVM_ENSUREVISIBLE, uintptr(i), 1) == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (lv *ListView) SelectedItem() ListItem {
|
||||
if items := lv.SelectedItems(); len(items) > 0 {
|
||||
return items[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lv *ListView) SetSelectedItem(item ListItem) bool {
|
||||
if i := lv.findIndexByItem(item); i > -1 {
|
||||
lv.SetSelectedIndex(i)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// mask is used to set the LVITEM.Mask for ListView.GetItem which indicates which attributes you'd like to receive
|
||||
// of LVITEM.
|
||||
func (lv *ListView) SelectedItems() []ListItem {
|
||||
var items []ListItem
|
||||
|
||||
var i int = -1
|
||||
for {
|
||||
if i = int(w32.SendMessage(lv.hwnd, w32.LVM_GETNEXTITEM, uintptr(i), uintptr(w32.LVNI_SELECTED))); i == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
if item := lv.findItemByIndex(i); item != nil {
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func (lv *ListView) SelectedCount() uint {
|
||||
return uint(w32.SendMessage(lv.hwnd, w32.LVM_GETSELECTEDCOUNT, 0, 0))
|
||||
}
|
||||
|
||||
// GetSelectedIndex first selected item index. Returns -1 if no item is selected.
|
||||
func (lv *ListView) SelectedIndex() int {
|
||||
var i int = -1
|
||||
return int(w32.SendMessage(lv.hwnd, w32.LVM_GETNEXTITEM, uintptr(i), uintptr(w32.LVNI_SELECTED)))
|
||||
}
|
||||
|
||||
// Set i to -1 to select all items.
|
||||
func (lv *ListView) SetSelectedIndex(i int) {
|
||||
lv.setItemState(i, w32.LVIS_SELECTED, w32.LVIS_SELECTED)
|
||||
}
|
||||
|
||||
func (lv *ListView) SetImageList(imageList *ImageList) {
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SETIMAGELIST, w32.LVSIL_SMALL, uintptr(imageList.Handle()))
|
||||
lv.iml = imageList
|
||||
}
|
||||
|
||||
// Event publishers
|
||||
func (lv *ListView) OnEndLabelEdit() *EventManager {
|
||||
return &lv.onEndLabelEdit
|
||||
}
|
||||
|
||||
func (lv *ListView) OnDoubleClick() *EventManager {
|
||||
return &lv.onDoubleClick
|
||||
}
|
||||
|
||||
func (lv *ListView) OnClick() *EventManager {
|
||||
return &lv.onClick
|
||||
}
|
||||
|
||||
func (lv *ListView) OnKeyDown() *EventManager {
|
||||
return &lv.onKeyDown
|
||||
}
|
||||
|
||||
func (lv *ListView) OnItemChanging() *EventManager {
|
||||
return &lv.onItemChanging
|
||||
}
|
||||
|
||||
func (lv *ListView) OnItemChanged() *EventManager {
|
||||
return &lv.onItemChanged
|
||||
}
|
||||
|
||||
func (lv *ListView) OnCheckChanged() *EventManager {
|
||||
return &lv.onCheckChanged
|
||||
}
|
||||
|
||||
func (lv *ListView) OnViewChange() *EventManager {
|
||||
return &lv.onViewChange
|
||||
}
|
||||
|
||||
func (lv *ListView) OnEndScroll() *EventManager {
|
||||
return &lv.onEndScroll
|
||||
}
|
||||
|
||||
// Message processer
|
||||
func (lv *ListView) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
/*case w32.WM_ERASEBKGND:
|
||||
lv.StretchLastColumn()
|
||||
println("case w32.WM_ERASEBKGND")
|
||||
return 1*/
|
||||
|
||||
case w32.WM_NOTIFY:
|
||||
nm := (*w32.NMHDR)(unsafe.Pointer(lparam))
|
||||
code := int32(nm.Code)
|
||||
|
||||
switch code {
|
||||
case w32.LVN_BEGINLABELEDITW:
|
||||
// println("Begin label edit")
|
||||
case w32.LVN_ENDLABELEDITW:
|
||||
nmdi := (*w32.NMLVDISPINFO)(unsafe.Pointer(lparam))
|
||||
if nmdi.Item.PszText != nil {
|
||||
fmt.Println(nmdi.Item.PszText, nmdi.Item)
|
||||
if item, ok := lv.handle2Item[nmdi.Item.LParam]; ok {
|
||||
lv.onEndLabelEdit.Fire(NewEvent(lv,
|
||||
&LabelEditEventData{Item: item,
|
||||
Text: w32.UTF16PtrToString(nmdi.Item.PszText)}))
|
||||
}
|
||||
return w32.TRUE
|
||||
}
|
||||
case w32.NM_DBLCLK:
|
||||
lv.onDoubleClick.Fire(NewEvent(lv, nil))
|
||||
|
||||
case w32.NM_CLICK:
|
||||
ac := (*w32.NMITEMACTIVATE)(unsafe.Pointer(lparam))
|
||||
var hti w32.LVHITTESTINFO
|
||||
hti.Pt = w32.POINT{ac.PtAction.X, ac.PtAction.Y}
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_HITTEST, 0, uintptr(unsafe.Pointer(&hti)))
|
||||
|
||||
if hti.Flags == w32.LVHT_ONITEMSTATEICON {
|
||||
if item := lv.findItemByIndex(int(hti.IItem)); item != nil {
|
||||
if item, ok := item.(ListItemChecker); ok {
|
||||
checked := !item.Checked()
|
||||
item.SetChecked(checked)
|
||||
lv.onCheckChanged.Fire(NewEvent(lv, item))
|
||||
|
||||
if w32.SendMessage(lv.hwnd, w32.LVM_UPDATE, uintptr(hti.IItem), 0) == w32.FALSE {
|
||||
panic("SendMessage(LVM_UPDATE)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hti.Pt = w32.POINT{ac.PtAction.X, ac.PtAction.Y}
|
||||
w32.SendMessage(lv.hwnd, w32.LVM_SUBITEMHITTEST, 0, uintptr(unsafe.Pointer(&hti)))
|
||||
lv.onClick.Fire(NewEvent(lv, hti.ISubItem))
|
||||
|
||||
case w32.LVN_KEYDOWN:
|
||||
nmkey := (*w32.NMLVKEYDOWN)(unsafe.Pointer(lparam))
|
||||
if nmkey.WVKey == w32.VK_SPACE && lv.CheckBoxes() {
|
||||
if item := lv.SelectedItem(); item != nil {
|
||||
if item, ok := item.(ListItemChecker); ok {
|
||||
checked := !item.Checked()
|
||||
item.SetChecked(checked)
|
||||
lv.onCheckChanged.Fire(NewEvent(lv, item))
|
||||
}
|
||||
|
||||
index := lv.findIndexByItem(item)
|
||||
if w32.SendMessage(lv.hwnd, w32.LVM_UPDATE, uintptr(index), 0) == w32.FALSE {
|
||||
panic("SendMessage(LVM_UPDATE)")
|
||||
}
|
||||
}
|
||||
}
|
||||
lv.onKeyDown.Fire(NewEvent(lv, nmkey.WVKey))
|
||||
key := nmkey.WVKey
|
||||
w32.SendMessage(lv.Parent().Handle(), w32.WM_KEYDOWN, uintptr(key), 0)
|
||||
|
||||
case w32.LVN_ITEMCHANGING:
|
||||
// This event also fires when listview has changed via code.
|
||||
nmlv := (*w32.NMLISTVIEW)(unsafe.Pointer(lparam))
|
||||
item := lv.findItemByIndex(int(nmlv.IItem))
|
||||
lv.onItemChanging.Fire(NewEvent(lv, item))
|
||||
|
||||
case w32.LVN_ITEMCHANGED:
|
||||
// This event also fires when listview has changed via code.
|
||||
nmlv := (*w32.NMLISTVIEW)(unsafe.Pointer(lparam))
|
||||
item := lv.findItemByIndex(int(nmlv.IItem))
|
||||
lv.onItemChanged.Fire(NewEvent(lv, item))
|
||||
|
||||
case w32.LVN_GETDISPINFO:
|
||||
nmdi := (*w32.NMLVDISPINFO)(unsafe.Pointer(lparam))
|
||||
if nmdi.Item.StateMask&w32.LVIS_STATEIMAGEMASK > 0 {
|
||||
if item, ok := lv.handle2Item[nmdi.Item.LParam]; ok {
|
||||
if item, ok := item.(ListItemChecker); ok {
|
||||
|
||||
checked := item.Checked()
|
||||
if checked {
|
||||
nmdi.Item.State = 0x2000
|
||||
} else {
|
||||
nmdi.Item.State = 0x1000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lv.onViewChange.Fire(NewEvent(lv, nil))
|
||||
|
||||
case w32.LVN_ENDSCROLL:
|
||||
lv.onEndScroll.Fire(NewEvent(lv, nil))
|
||||
}
|
||||
}
|
||||
return w32.DefWindowProc(lv.hwnd, msg, wparam, lparam)
|
||||
}
|
337
v2/internal/frontend/desktop/windows/winc/menu.go
Normal file
337
v2/internal/frontend/desktop/windows/winc/menu.go
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
var (
|
||||
nextMenuItemID uint16 = 3
|
||||
actionsByID = make(map[uint16]*MenuItem)
|
||||
shortcut2Action = make(map[Shortcut]*MenuItem)
|
||||
menuItems = make(map[w32.HMENU][]*MenuItem)
|
||||
radioGroups = make(map[*MenuItem]*RadioGroup)
|
||||
initialised bool
|
||||
)
|
||||
|
||||
var NoShortcut = Shortcut{}
|
||||
|
||||
// Menu for main window and context menus on controls.
|
||||
// Most methods used for both main window menu and context menu.
|
||||
type Menu struct {
|
||||
hMenu w32.HMENU
|
||||
hwnd w32.HWND // hwnd might be nil if it is context menu.
|
||||
}
|
||||
|
||||
type MenuItem struct {
|
||||
hMenu w32.HMENU
|
||||
hSubMenu w32.HMENU // Non zero if this item is in itself a submenu.
|
||||
|
||||
text string
|
||||
toolTip string
|
||||
image *Bitmap
|
||||
shortcut Shortcut
|
||||
enabled bool
|
||||
|
||||
checkable bool
|
||||
checked bool
|
||||
isRadio bool
|
||||
|
||||
id uint16
|
||||
|
||||
onClick EventManager
|
||||
}
|
||||
|
||||
type RadioGroup struct {
|
||||
members []*MenuItem
|
||||
hwnd w32.HWND
|
||||
}
|
||||
|
||||
func NewContextMenu() *MenuItem {
|
||||
hMenu := w32.CreatePopupMenu()
|
||||
if hMenu == 0 {
|
||||
panic("failed CreateMenu")
|
||||
}
|
||||
|
||||
item := &MenuItem{
|
||||
hMenu: hMenu,
|
||||
hSubMenu: hMenu,
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func (m *Menu) Dispose() {
|
||||
if m.hMenu != 0 {
|
||||
w32.DestroyMenu(m.hMenu)
|
||||
m.hMenu = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Menu) IsDisposed() bool {
|
||||
return m.hMenu == 0
|
||||
}
|
||||
|
||||
func initMenuItemInfoFromAction(mii *w32.MENUITEMINFO, a *MenuItem) {
|
||||
mii.CbSize = uint32(unsafe.Sizeof(*mii))
|
||||
mii.FMask = w32.MIIM_FTYPE | w32.MIIM_ID | w32.MIIM_STATE | w32.MIIM_STRING
|
||||
if a.image != nil {
|
||||
mii.FMask |= w32.MIIM_BITMAP
|
||||
mii.HbmpItem = a.image.handle
|
||||
}
|
||||
if a.IsSeparator() {
|
||||
mii.FType = w32.MFT_SEPARATOR
|
||||
} else {
|
||||
mii.FType = w32.MFT_STRING
|
||||
var text string
|
||||
if s := a.shortcut; s.Key != 0 {
|
||||
text = fmt.Sprintf("%s\t%s", a.text, s.String())
|
||||
shortcut2Action[a.shortcut] = a
|
||||
} else {
|
||||
text = a.text
|
||||
}
|
||||
mii.DwTypeData = syscall.StringToUTF16Ptr(text)
|
||||
mii.Cch = uint32(len([]rune(a.text)))
|
||||
}
|
||||
mii.WID = uint32(a.id)
|
||||
|
||||
if a.Enabled() {
|
||||
mii.FState &^= w32.MFS_DISABLED
|
||||
} else {
|
||||
mii.FState |= w32.MFS_DISABLED
|
||||
}
|
||||
|
||||
if a.Checkable() {
|
||||
mii.FMask |= w32.MIIM_CHECKMARKS
|
||||
}
|
||||
if a.Checked() {
|
||||
mii.FState |= w32.MFS_CHECKED
|
||||
}
|
||||
|
||||
if a.hSubMenu != 0 {
|
||||
mii.FMask |= w32.MIIM_SUBMENU
|
||||
mii.HSubMenu = a.hSubMenu
|
||||
}
|
||||
}
|
||||
|
||||
// Show menu on the main window.
|
||||
func (m *Menu) Show() {
|
||||
initialised = true
|
||||
updateRadioGroups()
|
||||
if !w32.DrawMenuBar(m.hwnd) {
|
||||
panic("DrawMenuBar failed")
|
||||
}
|
||||
}
|
||||
|
||||
// AddSubMenu returns item that is used as submenu to perform AddItem(s).
|
||||
func (m *Menu) AddSubMenu(text string) *MenuItem {
|
||||
hSubMenu := w32.CreateMenu()
|
||||
if hSubMenu == 0 {
|
||||
panic("failed CreateMenu")
|
||||
}
|
||||
return addMenuItem(m.hMenu, hSubMenu, text, Shortcut{}, nil, false)
|
||||
}
|
||||
|
||||
// This method will iterate through the menu items, group radio items together, build a
|
||||
// quick access map and set the initial items
|
||||
func updateRadioGroups() {
|
||||
|
||||
if !initialised {
|
||||
return
|
||||
}
|
||||
|
||||
radioItemsChecked := []*MenuItem{}
|
||||
radioGroups = make(map[*MenuItem]*RadioGroup)
|
||||
var currentRadioGroupMembers []*MenuItem
|
||||
// Iterate the menus
|
||||
for _, menu := range menuItems {
|
||||
menuLength := len(menu)
|
||||
for index, menuItem := range menu {
|
||||
if menuItem.isRadio {
|
||||
currentRadioGroupMembers = append(currentRadioGroupMembers, menuItem)
|
||||
if menuItem.checked {
|
||||
radioItemsChecked = append(radioItemsChecked, menuItem)
|
||||
}
|
||||
|
||||
// If end of menu
|
||||
if index == menuLength-1 {
|
||||
radioGroup := &RadioGroup{
|
||||
members: currentRadioGroupMembers,
|
||||
hwnd: menuItem.hMenu,
|
||||
}
|
||||
// Save the group to each member iin the radiomap
|
||||
for _, member := range currentRadioGroupMembers {
|
||||
radioGroups[member] = radioGroup
|
||||
}
|
||||
currentRadioGroupMembers = []*MenuItem{}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Not a radio item
|
||||
if len(currentRadioGroupMembers) > 0 {
|
||||
radioGroup := &RadioGroup{
|
||||
members: currentRadioGroupMembers,
|
||||
hwnd: menuItem.hMenu,
|
||||
}
|
||||
// Save the group to each member iin the radiomap
|
||||
for _, member := range currentRadioGroupMembers {
|
||||
radioGroups[member] = radioGroup
|
||||
}
|
||||
currentRadioGroupMembers = []*MenuItem{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the checked items
|
||||
for _, item := range radioItemsChecked {
|
||||
radioGroup := radioGroups[item]
|
||||
startID := radioGroup.members[0].id
|
||||
endID := radioGroup.members[len(radioGroup.members)-1].id
|
||||
w32.SelectRadioMenuItem(item.id, startID, endID, radioGroup.hwnd)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (mi *MenuItem) OnClick() *EventManager {
|
||||
return &mi.onClick
|
||||
}
|
||||
|
||||
func (mi *MenuItem) AddSeparator() {
|
||||
addMenuItem(mi.hSubMenu, 0, "-", Shortcut{}, nil, false)
|
||||
}
|
||||
|
||||
// AddItem adds plain menu item.
|
||||
func (mi *MenuItem) AddItem(text string, shortcut Shortcut) *MenuItem {
|
||||
return addMenuItem(mi.hSubMenu, 0, text, shortcut, nil, false)
|
||||
}
|
||||
|
||||
// AddItemCheckable adds plain menu item that can have a checkmark.
|
||||
func (mi *MenuItem) AddItemCheckable(text string, shortcut Shortcut) *MenuItem {
|
||||
return addMenuItem(mi.hSubMenu, 0, text, shortcut, nil, true)
|
||||
}
|
||||
|
||||
// AddItemRadio adds plain menu item that can have a checkmark and is part of a radio group.
|
||||
func (mi *MenuItem) AddItemRadio(text string, shortcut Shortcut) *MenuItem {
|
||||
menuItem := addMenuItem(mi.hSubMenu, 0, text, shortcut, nil, true)
|
||||
menuItem.isRadio = true
|
||||
return menuItem
|
||||
}
|
||||
|
||||
// AddItemWithBitmap adds menu item with shortcut and bitmap.
|
||||
func (mi *MenuItem) AddItemWithBitmap(text string, shortcut Shortcut, image *Bitmap) *MenuItem {
|
||||
return addMenuItem(mi.hSubMenu, 0, text, shortcut, image, false)
|
||||
}
|
||||
|
||||
// AddSubMenu adds a submenu.
|
||||
func (mi *MenuItem) AddSubMenu(text string) *MenuItem {
|
||||
hSubMenu := w32.CreatePopupMenu()
|
||||
if hSubMenu == 0 {
|
||||
panic("failed CreatePopupMenu")
|
||||
}
|
||||
return addMenuItem(mi.hSubMenu, hSubMenu, text, Shortcut{}, nil, false)
|
||||
}
|
||||
|
||||
// AddItem to the menu, set text to "-" for separators.
|
||||
func addMenuItem(hMenu, hSubMenu w32.HMENU, text string, shortcut Shortcut, image *Bitmap, checkable bool) *MenuItem {
|
||||
item := &MenuItem{
|
||||
hMenu: hMenu,
|
||||
hSubMenu: hSubMenu,
|
||||
text: text,
|
||||
shortcut: shortcut,
|
||||
image: image,
|
||||
enabled: true,
|
||||
id: nextMenuItemID,
|
||||
checkable: checkable,
|
||||
isRadio: false,
|
||||
//visible: true,
|
||||
}
|
||||
nextMenuItemID++
|
||||
actionsByID[item.id] = item
|
||||
menuItems[hMenu] = append(menuItems[hMenu], item)
|
||||
|
||||
var mii w32.MENUITEMINFO
|
||||
initMenuItemInfoFromAction(&mii, item)
|
||||
|
||||
index := -1
|
||||
if !w32.InsertMenuItem(hMenu, uint32(index), true, &mii) {
|
||||
panic("InsertMenuItem failed")
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func indexInObserver(a *MenuItem) int {
|
||||
var idx int
|
||||
for _, mi := range menuItems[a.hMenu] {
|
||||
if mi == a {
|
||||
return idx
|
||||
}
|
||||
idx++
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func findMenuItemByID(id int) *MenuItem {
|
||||
return actionsByID[uint16(id)]
|
||||
}
|
||||
|
||||
func (mi *MenuItem) update() {
|
||||
var mii w32.MENUITEMINFO
|
||||
initMenuItemInfoFromAction(&mii, mi)
|
||||
|
||||
if !w32.SetMenuItemInfo(mi.hMenu, uint32(indexInObserver(mi)), true, &mii) {
|
||||
panic("SetMenuItemInfo failed")
|
||||
}
|
||||
if mi.isRadio {
|
||||
mi.updateRadioGroup()
|
||||
}
|
||||
}
|
||||
|
||||
func (mi *MenuItem) IsSeparator() bool { return mi.text == "-" }
|
||||
func (mi *MenuItem) SetSeparator() { mi.text = "-" }
|
||||
|
||||
func (mi *MenuItem) Enabled() bool { return mi.enabled }
|
||||
func (mi *MenuItem) SetEnabled(b bool) { mi.enabled = b; mi.update() }
|
||||
|
||||
func (mi *MenuItem) Checkable() bool { return mi.checkable }
|
||||
func (mi *MenuItem) SetCheckable(b bool) { mi.checkable = b; mi.update() }
|
||||
|
||||
func (mi *MenuItem) Checked() bool { return mi.checked }
|
||||
func (mi *MenuItem) SetChecked(b bool) {
|
||||
if mi.isRadio {
|
||||
radioGroup := radioGroups[mi]
|
||||
if radioGroup != nil {
|
||||
for _, member := range radioGroup.members {
|
||||
member.checked = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
mi.checked = b
|
||||
mi.update()
|
||||
}
|
||||
|
||||
func (mi *MenuItem) Text() string { return mi.text }
|
||||
func (mi *MenuItem) SetText(s string) { mi.text = s; mi.update() }
|
||||
|
||||
func (mi *MenuItem) Image() *Bitmap { return mi.image }
|
||||
func (mi *MenuItem) SetImage(b *Bitmap) { mi.image = b; mi.update() }
|
||||
|
||||
func (mi *MenuItem) ToolTip() string { return mi.toolTip }
|
||||
func (mi *MenuItem) SetToolTip(s string) { mi.toolTip = s; mi.update() }
|
||||
|
||||
func (mi *MenuItem) updateRadioGroup() {
|
||||
radioGroup := radioGroups[mi]
|
||||
if radioGroup == nil {
|
||||
return
|
||||
}
|
||||
startID := radioGroup.members[0].id
|
||||
endID := radioGroup.members[len(radioGroup.members)-1].id
|
||||
w32.SelectRadioMenuItem(mi.id, startID, endID, radioGroup.hwnd)
|
||||
}
|
48
v2/internal/frontend/desktop/windows/winc/mousecontrol.go
Normal file
48
v2/internal/frontend/desktop/windows/winc/mousecontrol.go
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
// MouseControl used for creating custom controls that need mouse hover or mouse leave events.
|
||||
type MouseControl struct {
|
||||
ControlBase
|
||||
isMouseLeft bool
|
||||
}
|
||||
|
||||
func (cc *MouseControl) Init(parent Controller, className string, exStyle, style uint) {
|
||||
RegClassOnlyOnce(className)
|
||||
cc.hwnd = CreateWindow(className, parent, exStyle, style)
|
||||
cc.parent = parent
|
||||
RegMsgHandler(cc)
|
||||
|
||||
cc.isMouseLeft = true
|
||||
cc.SetFont(DefaultFont)
|
||||
}
|
||||
|
||||
func (cc *MouseControl) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
sender := GetMsgHandler(cc.hwnd)
|
||||
switch msg {
|
||||
case w32.WM_CREATE:
|
||||
internalTrackMouseEvent(cc.hwnd)
|
||||
cc.onCreate.Fire(NewEvent(sender, nil))
|
||||
case w32.WM_CLOSE:
|
||||
cc.onClose.Fire(NewEvent(sender, nil))
|
||||
case w32.WM_MOUSEMOVE:
|
||||
//if cc.isMouseLeft {
|
||||
|
||||
cc.onMouseHover.Fire(NewEvent(sender, nil))
|
||||
//internalTrackMouseEvent(cc.hwnd)
|
||||
cc.isMouseLeft = false
|
||||
|
||||
//}
|
||||
case w32.WM_MOUSELEAVE:
|
||||
cc.onMouseLeave.Fire(NewEvent(sender, nil))
|
||||
cc.isMouseLeft = true
|
||||
}
|
||||
return w32.DefWindowProc(cc.hwnd, msg, wparam, lparam)
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
func RegMsgHandler(controller Controller) {
|
||||
gControllerRegistry[controller.Handle()] = controller
|
||||
}
|
||||
|
||||
func UnRegMsgHandler(hwnd w32.HWND) {
|
||||
delete(gControllerRegistry, hwnd)
|
||||
}
|
||||
|
||||
func GetMsgHandler(hwnd w32.HWND) Controller {
|
||||
if controller, isExists := gControllerRegistry[hwnd]; isExists {
|
||||
return controller
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
198
v2/internal/frontend/desktop/windows/winc/panel.go
Normal file
198
v2/internal/frontend/desktop/windows/winc/panel.go
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type Panel struct {
|
||||
ControlBase
|
||||
layoutMng LayoutManager
|
||||
}
|
||||
|
||||
func NewPanel(parent Controller) *Panel {
|
||||
pa := new(Panel)
|
||||
|
||||
RegClassOnlyOnce("winc_Panel")
|
||||
pa.hwnd = CreateWindow("winc_Panel", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE)
|
||||
pa.parent = parent
|
||||
RegMsgHandler(pa)
|
||||
|
||||
pa.SetFont(DefaultFont)
|
||||
pa.SetText("")
|
||||
pa.SetSize(200, 65)
|
||||
return pa
|
||||
}
|
||||
|
||||
// SetLayout panel implements DockAllow interface.
|
||||
func (pa *Panel) SetLayout(mng LayoutManager) {
|
||||
pa.layoutMng = mng
|
||||
}
|
||||
|
||||
func (pa *Panel) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_SIZE, w32.WM_PAINT:
|
||||
if pa.layoutMng != nil {
|
||||
pa.layoutMng.Update()
|
||||
}
|
||||
}
|
||||
return w32.DefWindowProc(pa.hwnd, msg, wparam, lparam)
|
||||
}
|
||||
|
||||
var errorPanelPen = NewPen(w32.PS_GEOMETRIC, 2, NewSolidColorBrush(RGB(255, 128, 128)))
|
||||
var errorPanelOkPen = NewPen(w32.PS_GEOMETRIC, 2, NewSolidColorBrush(RGB(220, 220, 220)))
|
||||
|
||||
// ErrorPanel shows errors or important messages.
|
||||
// It is meant to stand out of other on screen controls.
|
||||
type ErrorPanel struct {
|
||||
ControlBase
|
||||
pen *Pen
|
||||
margin int
|
||||
}
|
||||
|
||||
// NewErrorPanel.
|
||||
func NewErrorPanel(parent Controller) *ErrorPanel {
|
||||
f := new(ErrorPanel)
|
||||
f.init(parent)
|
||||
|
||||
f.SetFont(DefaultFont)
|
||||
f.SetText("No errors")
|
||||
f.SetSize(200, 65)
|
||||
f.margin = 5
|
||||
f.pen = errorPanelOkPen
|
||||
return f
|
||||
}
|
||||
|
||||
func (epa *ErrorPanel) init(parent Controller) {
|
||||
RegClassOnlyOnce("winc_ErrorPanel")
|
||||
|
||||
epa.hwnd = CreateWindow("winc_ErrorPanel", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE)
|
||||
epa.parent = parent
|
||||
|
||||
RegMsgHandler(epa)
|
||||
}
|
||||
|
||||
func (epa *ErrorPanel) SetMargin(margin int) {
|
||||
epa.margin = margin
|
||||
}
|
||||
|
||||
func (epa *ErrorPanel) Printf(format string, v ...interface{}) {
|
||||
epa.SetText(fmt.Sprintf(format, v...))
|
||||
epa.ShowAsError(false)
|
||||
}
|
||||
|
||||
func (epa *ErrorPanel) Errorf(format string, v ...interface{}) {
|
||||
epa.SetText(fmt.Sprintf(format, v...))
|
||||
epa.ShowAsError(true)
|
||||
}
|
||||
|
||||
func (epa *ErrorPanel) ShowAsError(show bool) {
|
||||
if show {
|
||||
epa.pen = errorPanelPen
|
||||
} else {
|
||||
epa.pen = errorPanelOkPen
|
||||
}
|
||||
epa.Invalidate(true)
|
||||
}
|
||||
|
||||
func (epa *ErrorPanel) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_ERASEBKGND:
|
||||
canvas := NewCanvasFromHDC(w32.HDC(wparam))
|
||||
r := epa.Bounds()
|
||||
r.rect.Left += int32(epa.margin)
|
||||
r.rect.Right -= int32(epa.margin)
|
||||
r.rect.Top += int32(epa.margin)
|
||||
r.rect.Bottom -= int32(epa.margin)
|
||||
// old code used NewSystemColorBrush(w32.COLOR_BTNFACE)
|
||||
canvas.DrawFillRect(r, epa.pen, NewSystemColorBrush(w32.COLOR_WINDOW))
|
||||
|
||||
r.rect.Left += 5
|
||||
canvas.DrawText(epa.Text(), r, 0, epa.Font(), RGB(0, 0, 0))
|
||||
canvas.Dispose()
|
||||
return 1
|
||||
}
|
||||
return w32.DefWindowProc(epa.hwnd, msg, wparam, lparam)
|
||||
}
|
||||
|
||||
// MultiPanel contains other panels and only makes one of them visible.
|
||||
type MultiPanel struct {
|
||||
ControlBase
|
||||
current int
|
||||
panels []*Panel
|
||||
}
|
||||
|
||||
func NewMultiPanel(parent Controller) *MultiPanel {
|
||||
mpa := new(MultiPanel)
|
||||
|
||||
RegClassOnlyOnce("winc_MultiPanel")
|
||||
mpa.hwnd = CreateWindow("winc_MultiPanel", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE)
|
||||
mpa.parent = parent
|
||||
RegMsgHandler(mpa)
|
||||
|
||||
mpa.SetFont(DefaultFont)
|
||||
mpa.SetText("")
|
||||
mpa.SetSize(300, 200)
|
||||
mpa.current = -1
|
||||
return mpa
|
||||
}
|
||||
|
||||
func (mpa *MultiPanel) Count() int { return len(mpa.panels) }
|
||||
|
||||
// AddPanel adds panels to the internal list, first panel is visible all others are hidden.
|
||||
func (mpa *MultiPanel) AddPanel(panel *Panel) {
|
||||
if len(mpa.panels) > 0 {
|
||||
panel.Hide()
|
||||
}
|
||||
mpa.current = 0
|
||||
mpa.panels = append(mpa.panels, panel)
|
||||
}
|
||||
|
||||
// ReplacePanel replaces panel, useful for refreshing controls on screen.
|
||||
func (mpa *MultiPanel) ReplacePanel(index int, panel *Panel) {
|
||||
mpa.panels[index] = panel
|
||||
}
|
||||
|
||||
// DeletePanel removed panel.
|
||||
func (mpa *MultiPanel) DeletePanel(index int) {
|
||||
mpa.panels = append(mpa.panels[:index], mpa.panels[index+1:]...)
|
||||
}
|
||||
|
||||
func (mpa *MultiPanel) Current() int {
|
||||
return mpa.current
|
||||
}
|
||||
|
||||
func (mpa *MultiPanel) SetCurrent(index int) {
|
||||
if index >= len(mpa.panels) {
|
||||
panic("index greater than number of panels")
|
||||
}
|
||||
if mpa.current == -1 {
|
||||
panic("no current panel, add panels first")
|
||||
}
|
||||
for i := range mpa.panels {
|
||||
if i != index {
|
||||
mpa.panels[i].Hide()
|
||||
mpa.panels[i].Invalidate(true)
|
||||
}
|
||||
}
|
||||
mpa.panels[index].Show()
|
||||
mpa.panels[index].Invalidate(true)
|
||||
mpa.current = index
|
||||
}
|
||||
|
||||
func (mpa *MultiPanel) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_SIZE:
|
||||
// resize contained panels
|
||||
for _, p := range mpa.panels {
|
||||
p.SetPos(0, 0)
|
||||
p.SetSize(mpa.Size())
|
||||
}
|
||||
}
|
||||
return w32.DefWindowProc(mpa.hwnd, msg, wparam, lparam)
|
||||
}
|
75
v2/internal/frontend/desktop/windows/winc/path.go
Normal file
75
v2/internal/frontend/desktop/windows/winc/path.go
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
func knownFolderPath(id w32.CSIDL) (string, error) {
|
||||
var buf [w32.MAX_PATH]uint16
|
||||
|
||||
if !w32.SHGetSpecialFolderPath(0, &buf[0], id, false) {
|
||||
return "", fmt.Errorf("SHGetSpecialFolderPath failed")
|
||||
}
|
||||
|
||||
return syscall.UTF16ToString(buf[0:]), nil
|
||||
}
|
||||
|
||||
func AppDataPath() (string, error) {
|
||||
return knownFolderPath(w32.CSIDL_APPDATA)
|
||||
}
|
||||
|
||||
func CommonAppDataPath() (string, error) {
|
||||
return knownFolderPath(w32.CSIDL_COMMON_APPDATA)
|
||||
}
|
||||
|
||||
func LocalAppDataPath() (string, error) {
|
||||
return knownFolderPath(w32.CSIDL_LOCAL_APPDATA)
|
||||
}
|
||||
|
||||
// EnsureAppDataPath uses AppDataPath to ensure storage for local settings and databases.
|
||||
func EnsureAppDataPath(company, product string) (string, error) {
|
||||
path, err := AppDataPath()
|
||||
if err != nil {
|
||||
return path, err
|
||||
}
|
||||
p := filepath.Join(path, company, product)
|
||||
|
||||
if _, err := os.Stat(p); os.IsNotExist(err) {
|
||||
// path/to/whatever does not exist
|
||||
if err := os.MkdirAll(p, os.ModePerm); err != nil {
|
||||
return p, err
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func DriveNames() ([]string, error) {
|
||||
bufLen := w32.GetLogicalDriveStrings(0, nil)
|
||||
if bufLen == 0 {
|
||||
return nil, fmt.Errorf("GetLogicalDriveStrings failed")
|
||||
}
|
||||
buf := make([]uint16, bufLen+1)
|
||||
|
||||
bufLen = w32.GetLogicalDriveStrings(bufLen+1, &buf[0])
|
||||
if bufLen == 0 {
|
||||
return nil, fmt.Errorf("GetLogicalDriveStrings failed")
|
||||
}
|
||||
|
||||
var names []string
|
||||
for i := 0; i < len(buf)-2; {
|
||||
name := syscall.UTF16ToString(buf[i:])
|
||||
names = append(names, name)
|
||||
i += len(name) + 1
|
||||
}
|
||||
return names, nil
|
||||
}
|
59
v2/internal/frontend/desktop/windows/winc/pen.go
Normal file
59
v2/internal/frontend/desktop/windows/winc/pen.go
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type Pen struct {
|
||||
hPen w32.HPEN
|
||||
style uint
|
||||
brush *Brush
|
||||
}
|
||||
|
||||
func NewPen(style uint, width uint, brush *Brush) *Pen {
|
||||
if brush == nil {
|
||||
panic("Brush cannot be nil")
|
||||
}
|
||||
|
||||
hPen := w32.ExtCreatePen(style, width, brush.GetLOGBRUSH(), 0, nil)
|
||||
if hPen == 0 {
|
||||
panic("Failed to create pen")
|
||||
}
|
||||
|
||||
return &Pen{hPen, style, brush}
|
||||
}
|
||||
|
||||
func NewNullPen() *Pen {
|
||||
lb := w32.LOGBRUSH{LbStyle: w32.BS_NULL}
|
||||
|
||||
hPen := w32.ExtCreatePen(w32.PS_COSMETIC|w32.PS_NULL, 1, &lb, 0, nil)
|
||||
if hPen == 0 {
|
||||
panic("failed to create null brush")
|
||||
}
|
||||
|
||||
return &Pen{hPen: hPen}
|
||||
}
|
||||
|
||||
func (pen *Pen) Style() uint {
|
||||
return pen.style
|
||||
}
|
||||
|
||||
func (pen *Pen) Brush() *Brush {
|
||||
return pen.brush
|
||||
}
|
||||
|
||||
func (pen *Pen) GetHPEN() w32.HPEN {
|
||||
return pen.hPen
|
||||
}
|
||||
|
||||
func (pen *Pen) Dispose() {
|
||||
if pen.hPen != 0 {
|
||||
w32.DeleteObject(w32.HGDIOBJ(pen.hPen))
|
||||
pen.hPen = 0
|
||||
}
|
||||
}
|
46
v2/internal/frontend/desktop/windows/winc/progressbar.go
Normal file
46
v2/internal/frontend/desktop/windows/winc/progressbar.go
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type ProgressBar struct {
|
||||
ControlBase
|
||||
}
|
||||
|
||||
func NewProgressBar(parent Controller) *ProgressBar {
|
||||
pb := new(ProgressBar)
|
||||
|
||||
pb.InitControl(w32.PROGRESS_CLASS, parent, 0, w32.WS_CHILD|w32.WS_VISIBLE)
|
||||
RegMsgHandler(pb)
|
||||
|
||||
pb.SetSize(200, 22)
|
||||
return pb
|
||||
}
|
||||
|
||||
func (pr *ProgressBar) Value() int {
|
||||
ret := w32.SendMessage(pr.hwnd, w32.PBM_GETPOS, 0, 0)
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func (pr *ProgressBar) SetValue(v int) {
|
||||
w32.SendMessage(pr.hwnd, w32.PBM_SETPOS, uintptr(v), 0)
|
||||
}
|
||||
|
||||
func (pr *ProgressBar) Range() (min, max uint) {
|
||||
min = uint(w32.SendMessage(pr.hwnd, w32.PBM_GETRANGE, uintptr(w32.BoolToBOOL(true)), 0))
|
||||
max = uint(w32.SendMessage(pr.hwnd, w32.PBM_GETRANGE, uintptr(w32.BoolToBOOL(false)), 0))
|
||||
return
|
||||
}
|
||||
|
||||
func (pr *ProgressBar) SetRange(min, max int) {
|
||||
w32.SendMessage(pr.hwnd, w32.PBM_SETRANGE32, uintptr(min), uintptr(max))
|
||||
}
|
||||
|
||||
func (pr *ProgressBar) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
return w32.DefWindowProc(pr.hwnd, msg, wparam, lparam)
|
||||
}
|
85
v2/internal/frontend/desktop/windows/winc/rect.go
Normal file
85
v2/internal/frontend/desktop/windows/winc/rect.go
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
* Copyright (C) 2010-2013 Allen Dang. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type Rect struct {
|
||||
rect w32.RECT
|
||||
}
|
||||
|
||||
func NewEmptyRect() *Rect {
|
||||
var newRect Rect
|
||||
w32.SetRectEmpty(&newRect.rect)
|
||||
|
||||
return &newRect
|
||||
}
|
||||
|
||||
func NewRect(left, top, right, bottom int) *Rect {
|
||||
var newRect Rect
|
||||
w32.SetRectEmpty(&newRect.rect)
|
||||
newRect.Set(left, top, right, bottom)
|
||||
|
||||
return &newRect
|
||||
}
|
||||
|
||||
func (re *Rect) Data() (left, top, right, bottom int32) {
|
||||
left = re.rect.Left
|
||||
top = re.rect.Top
|
||||
right = re.rect.Right
|
||||
bottom = re.rect.Bottom
|
||||
return
|
||||
}
|
||||
|
||||
func (re *Rect) Width() int {
|
||||
return int(re.rect.Right - re.rect.Left)
|
||||
}
|
||||
|
||||
func (re *Rect) Height() int {
|
||||
return int(re.rect.Bottom - re.rect.Top)
|
||||
}
|
||||
|
||||
func (re *Rect) GetW32Rect() *w32.RECT {
|
||||
return &re.rect
|
||||
}
|
||||
|
||||
func (re *Rect) Set(left, top, right, bottom int) {
|
||||
w32.SetRect(&re.rect, left, top, right, bottom)
|
||||
}
|
||||
|
||||
func (re *Rect) IsEqual(rect *Rect) bool {
|
||||
return w32.EqualRect(&re.rect, &rect.rect)
|
||||
}
|
||||
|
||||
func (re *Rect) Inflate(x, y int) {
|
||||
w32.InflateRect(&re.rect, x, y)
|
||||
}
|
||||
|
||||
func (re *Rect) Intersect(src *Rect) {
|
||||
w32.IntersectRect(&re.rect, &re.rect, &src.rect)
|
||||
}
|
||||
|
||||
func (re *Rect) IsEmpty() bool {
|
||||
return w32.IsRectEmpty(&re.rect)
|
||||
}
|
||||
|
||||
func (re *Rect) Offset(x, y int) {
|
||||
w32.OffsetRect(&re.rect, x, y)
|
||||
}
|
||||
|
||||
func (re *Rect) IsPointIn(x, y int) bool {
|
||||
return w32.PtInRect(&re.rect, x, y)
|
||||
}
|
||||
|
||||
func (re *Rect) Substract(src *Rect) {
|
||||
w32.SubtractRect(&re.rect, &re.rect, &src.rect)
|
||||
}
|
||||
|
||||
func (re *Rect) Union(src *Rect) {
|
||||
w32.UnionRect(&re.rect, &re.rect, &src.rect)
|
||||
}
|
214
v2/internal/frontend/desktop/windows/winc/resizer.go
Normal file
214
v2/internal/frontend/desktop/windows/winc/resizer.go
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type VResizer struct {
|
||||
ControlBase
|
||||
|
||||
control1 Dockable
|
||||
control2 Dockable
|
||||
dir Direction
|
||||
|
||||
mouseLeft bool
|
||||
drag bool
|
||||
}
|
||||
|
||||
func NewVResizer(parent Controller) *VResizer {
|
||||
sp := new(VResizer)
|
||||
|
||||
RegClassOnlyOnce("winc_VResizer")
|
||||
sp.hwnd = CreateWindow("winc_VResizer", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE)
|
||||
sp.parent = parent
|
||||
sp.mouseLeft = true
|
||||
RegMsgHandler(sp)
|
||||
|
||||
sp.SetFont(DefaultFont)
|
||||
sp.SetText("")
|
||||
sp.SetSize(20, 100)
|
||||
return sp
|
||||
}
|
||||
|
||||
func (sp *VResizer) SetControl(control1, control2 Dockable, dir Direction, minSize int) {
|
||||
sp.control1 = control1
|
||||
sp.control2 = control2
|
||||
if dir != Left && dir != Right {
|
||||
panic("invalid direction")
|
||||
}
|
||||
sp.dir = dir
|
||||
|
||||
// TODO(vi): ADDED
|
||||
/*internalTrackMouseEvent(control1.Handle())
|
||||
internalTrackMouseEvent(control2.Handle())
|
||||
|
||||
control1.OnMouseMove().Bind(func(e *Event) {
|
||||
if sp.drag {
|
||||
x := e.Data.(*MouseEventData).X
|
||||
sp.update(x)
|
||||
w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_SIZEWE)))
|
||||
|
||||
}
|
||||
fmt.Println("control1.OnMouseMove")
|
||||
})
|
||||
|
||||
control2.OnMouseMove().Bind(func(e *Event) {
|
||||
if sp.drag {
|
||||
x := e.Data.(*MouseEventData).X
|
||||
sp.update(x)
|
||||
w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_SIZEWE)))
|
||||
|
||||
}
|
||||
fmt.Println("control2.OnMouseMove")
|
||||
})
|
||||
|
||||
control1.OnLBUp().Bind(func(e *Event) {
|
||||
sp.drag = false
|
||||
sp.mouseLeft = true
|
||||
fmt.Println("control1.OnLBUp")
|
||||
})
|
||||
|
||||
control2.OnLBUp().Bind(func(e *Event) {
|
||||
sp.drag = false
|
||||
sp.mouseLeft = true
|
||||
fmt.Println("control2.OnLBUp")
|
||||
})*/
|
||||
|
||||
// ---- finish ADDED
|
||||
|
||||
}
|
||||
|
||||
func (sp *VResizer) update(x int) {
|
||||
pos := x - 10
|
||||
|
||||
w1, h1 := sp.control1.Width(), sp.control1.Height()
|
||||
if sp.dir == Left {
|
||||
w1 += pos
|
||||
} else {
|
||||
w1 -= pos
|
||||
}
|
||||
sp.control1.SetSize(w1, h1)
|
||||
fm := sp.parent.(*Form)
|
||||
fm.UpdateLayout()
|
||||
|
||||
w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_ARROW)))
|
||||
}
|
||||
|
||||
func (sp *VResizer) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_CREATE:
|
||||
internalTrackMouseEvent(sp.hwnd)
|
||||
|
||||
case w32.WM_MOUSEMOVE:
|
||||
if sp.drag {
|
||||
x, _ := genPoint(lparam)
|
||||
sp.update(x)
|
||||
} else {
|
||||
w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_SIZEWE)))
|
||||
}
|
||||
|
||||
if sp.mouseLeft {
|
||||
internalTrackMouseEvent(sp.hwnd)
|
||||
sp.mouseLeft = false
|
||||
}
|
||||
|
||||
case w32.WM_MOUSELEAVE:
|
||||
sp.drag = false
|
||||
sp.mouseLeft = true
|
||||
|
||||
case w32.WM_LBUTTONUP:
|
||||
sp.drag = false
|
||||
|
||||
case w32.WM_LBUTTONDOWN:
|
||||
sp.drag = true
|
||||
}
|
||||
return w32.DefWindowProc(sp.hwnd, msg, wparam, lparam)
|
||||
}
|
||||
|
||||
type HResizer struct {
|
||||
ControlBase
|
||||
|
||||
control1 Dockable
|
||||
control2 Dockable
|
||||
dir Direction
|
||||
mouseLeft bool
|
||||
drag bool
|
||||
}
|
||||
|
||||
func NewHResizer(parent Controller) *HResizer {
|
||||
sp := new(HResizer)
|
||||
|
||||
RegClassOnlyOnce("winc_HResizer")
|
||||
sp.hwnd = CreateWindow("winc_HResizer", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE)
|
||||
sp.parent = parent
|
||||
sp.mouseLeft = true
|
||||
RegMsgHandler(sp)
|
||||
|
||||
sp.SetFont(DefaultFont)
|
||||
sp.SetText("")
|
||||
sp.SetSize(100, 20)
|
||||
|
||||
return sp
|
||||
}
|
||||
|
||||
func (sp *HResizer) SetControl(control1, control2 Dockable, dir Direction, minSize int) {
|
||||
sp.control1 = control1
|
||||
sp.control2 = control2
|
||||
if dir != Top && dir != Bottom {
|
||||
panic("invalid direction")
|
||||
}
|
||||
sp.dir = dir
|
||||
|
||||
}
|
||||
|
||||
func (sp *HResizer) update(y int) {
|
||||
pos := y - 10
|
||||
|
||||
w1, h1 := sp.control1.Width(), sp.control1.Height()
|
||||
if sp.dir == Top {
|
||||
h1 += pos
|
||||
} else {
|
||||
h1 -= pos
|
||||
}
|
||||
sp.control1.SetSize(w1, h1)
|
||||
|
||||
fm := sp.parent.(*Form)
|
||||
fm.UpdateLayout()
|
||||
|
||||
w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_ARROW)))
|
||||
}
|
||||
|
||||
func (sp *HResizer) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_CREATE:
|
||||
internalTrackMouseEvent(sp.hwnd)
|
||||
|
||||
case w32.WM_MOUSEMOVE:
|
||||
if sp.drag {
|
||||
_, y := genPoint(lparam)
|
||||
sp.update(y)
|
||||
} else {
|
||||
w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_SIZENS)))
|
||||
}
|
||||
|
||||
if sp.mouseLeft {
|
||||
internalTrackMouseEvent(sp.hwnd)
|
||||
sp.mouseLeft = false
|
||||
}
|
||||
|
||||
case w32.WM_MOUSELEAVE:
|
||||
sp.drag = false
|
||||
sp.mouseLeft = true
|
||||
|
||||
case w32.WM_LBUTTONUP:
|
||||
sp.drag = false
|
||||
|
||||
case w32.WM_LBUTTONDOWN:
|
||||
sp.drag = true
|
||||
}
|
||||
return w32.DefWindowProc(sp.hwnd, msg, wparam, lparam)
|
||||
}
|
119
v2/internal/frontend/desktop/windows/winc/scrollview.go
Normal file
119
v2/internal/frontend/desktop/windows/winc/scrollview.go
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
type ScrollView struct {
|
||||
ControlBase
|
||||
child Dockable
|
||||
}
|
||||
|
||||
func NewScrollView(parent Controller) *ScrollView {
|
||||
sv := new(ScrollView)
|
||||
|
||||
RegClassOnlyOnce("winc_ScrollView")
|
||||
sv.hwnd = CreateWindow("winc_ScrollView", parent, w32.WS_EX_CONTROLPARENT,
|
||||
w32.WS_CHILD|w32.WS_HSCROLL|w32.WS_VISIBLE|w32.WS_VSCROLL)
|
||||
sv.parent = parent
|
||||
RegMsgHandler(sv)
|
||||
|
||||
sv.SetFont(DefaultFont)
|
||||
sv.SetText("")
|
||||
sv.SetSize(200, 200)
|
||||
return sv
|
||||
}
|
||||
|
||||
func (sv *ScrollView) SetChild(child Dockable) {
|
||||
sv.child = child
|
||||
}
|
||||
|
||||
func (sv *ScrollView) UpdateScrollBars() {
|
||||
w, h := sv.child.Width(), sv.child.Height()
|
||||
sw, sh := sv.Size()
|
||||
|
||||
var si w32.SCROLLINFO
|
||||
si.CbSize = uint32(unsafe.Sizeof(si))
|
||||
si.FMask = w32.SIF_PAGE | w32.SIF_RANGE
|
||||
|
||||
si.NMax = int32(w - 1)
|
||||
si.NPage = uint32(sw)
|
||||
w32.SetScrollInfo(sv.hwnd, w32.SB_HORZ, &si, true)
|
||||
x := sv.scroll(w32.SB_HORZ, w32.SB_THUMBPOSITION)
|
||||
|
||||
si.NMax = int32(h)
|
||||
si.NPage = uint32(sh)
|
||||
w32.SetScrollInfo(sv.hwnd, w32.SB_VERT, &si, true)
|
||||
y := sv.scroll(w32.SB_VERT, w32.SB_THUMBPOSITION)
|
||||
|
||||
sv.child.SetPos(x, y)
|
||||
}
|
||||
|
||||
func (sv *ScrollView) scroll(sb int32, cmd uint16) int {
|
||||
var pos int32
|
||||
var si w32.SCROLLINFO
|
||||
si.CbSize = uint32(unsafe.Sizeof(si))
|
||||
si.FMask = w32.SIF_PAGE | w32.SIF_POS | w32.SIF_RANGE | w32.SIF_TRACKPOS
|
||||
|
||||
w32.GetScrollInfo(sv.hwnd, sb, &si)
|
||||
pos = si.NPos
|
||||
|
||||
switch cmd {
|
||||
case w32.SB_LINELEFT: // == win.SB_LINEUP
|
||||
pos -= 20
|
||||
|
||||
case w32.SB_LINERIGHT: // == win.SB_LINEDOWN
|
||||
pos += 20
|
||||
|
||||
case w32.SB_PAGELEFT: // == win.SB_PAGEUP
|
||||
pos -= int32(si.NPage)
|
||||
|
||||
case w32.SB_PAGERIGHT: // == win.SB_PAGEDOWN
|
||||
pos += int32(si.NPage)
|
||||
|
||||
case w32.SB_THUMBTRACK:
|
||||
pos = si.NTrackPos
|
||||
}
|
||||
|
||||
if pos < 0 {
|
||||
pos = 0
|
||||
}
|
||||
if pos > si.NMax+1-int32(si.NPage) {
|
||||
pos = si.NMax + 1 - int32(si.NPage)
|
||||
}
|
||||
|
||||
si.FMask = w32.SIF_POS
|
||||
si.NPos = pos
|
||||
w32.SetScrollInfo(sv.hwnd, sb, &si, true)
|
||||
|
||||
return -int(pos)
|
||||
}
|
||||
|
||||
func (sv *ScrollView) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
if sv.child != nil {
|
||||
switch msg {
|
||||
case w32.WM_PAINT:
|
||||
sv.UpdateScrollBars()
|
||||
|
||||
case w32.WM_HSCROLL:
|
||||
x, y := sv.child.Pos()
|
||||
x = sv.scroll(w32.SB_HORZ, w32.LOWORD(uint32(wparam)))
|
||||
sv.child.SetPos(x, y)
|
||||
|
||||
case w32.WM_VSCROLL:
|
||||
x, y := sv.child.Pos()
|
||||
y = sv.scroll(w32.SB_VERT, w32.LOWORD(uint32(wparam)))
|
||||
sv.child.SetPos(x, y)
|
||||
|
||||
case w32.WM_SIZE, w32.WM_SIZING:
|
||||
sv.UpdateScrollBars()
|
||||
}
|
||||
}
|
||||
return w32.DefWindowProc(sv.hwnd, msg, wparam, lparam)
|
||||
}
|
77
v2/internal/frontend/desktop/windows/winc/slider.go
Normal file
77
v2/internal/frontend/desktop/windows/winc/slider.go
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
|
||||
type Slider struct {
|
||||
ControlBase
|
||||
prevPos int
|
||||
|
||||
onScroll EventManager
|
||||
}
|
||||
|
||||
func NewSlider(parent Controller) *Slider {
|
||||
tb := new(Slider)
|
||||
|
||||
tb.InitControl("msctls_trackbar32", parent, 0, w32.WS_TABSTOP|w32.WS_VISIBLE|w32.WS_CHILD /*|w32.TBS_AUTOTICKS*/)
|
||||
RegMsgHandler(tb)
|
||||
|
||||
tb.SetFont(DefaultFont)
|
||||
tb.SetText("Slider")
|
||||
tb.SetSize(200, 32)
|
||||
|
||||
tb.SetRange(0, 100)
|
||||
tb.SetPage(10)
|
||||
return tb
|
||||
}
|
||||
|
||||
func (tb *Slider) OnScroll() *EventManager {
|
||||
return &tb.onScroll
|
||||
}
|
||||
|
||||
func (tb *Slider) Value() int {
|
||||
ret := w32.SendMessage(tb.hwnd, w32.TBM_GETPOS, 0, 0)
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
func (tb *Slider) SetValue(v int) {
|
||||
tb.prevPos = v
|
||||
w32.SendMessage(tb.hwnd, w32.TBM_SETPOS, uintptr(w32.BoolToBOOL(true)), uintptr(v))
|
||||
}
|
||||
|
||||
func (tb *Slider) Range() (min, max int) {
|
||||
min = int(w32.SendMessage(tb.hwnd, w32.TBM_GETRANGEMIN, 0, 0))
|
||||
max = int(w32.SendMessage(tb.hwnd, w32.TBM_GETRANGEMAX, 0, 0))
|
||||
return min, max
|
||||
}
|
||||
|
||||
func (tb *Slider) SetRange(min, max int) {
|
||||
w32.SendMessage(tb.hwnd, w32.TBM_SETRANGE, uintptr(w32.BoolToBOOL(true)), uintptr(w32.MAKELONG(uint16(min), uint16(max))))
|
||||
}
|
||||
|
||||
func (tb *Slider) SetPage(pagesize int) {
|
||||
w32.SendMessage(tb.hwnd, w32.TBM_SETPAGESIZE, 0, uintptr(pagesize))
|
||||
}
|
||||
|
||||
func (tb *Slider) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
/*
|
||||
// REMOVE:
|
||||
// following code did not work, used workaround below
|
||||
code := w32.LOWORD(uint32(wparam))
|
||||
|
||||
switch code {
|
||||
case w32.TB_ENDTRACK:
|
||||
tb.onScroll.Fire(NewEvent(tb, nil))
|
||||
}*/
|
||||
|
||||
newPos := tb.Value()
|
||||
if newPos != tb.prevPos {
|
||||
tb.onScroll.Fire(NewEvent(tb, nil))
|
||||
tb.prevPos = newPos
|
||||
}
|
||||
|
||||
return w32.DefWindowProc(tb.hwnd, msg, wparam, lparam)
|
||||
}
|
105
v2/internal/frontend/desktop/windows/winc/tabview.go
Normal file
105
v2/internal/frontend/desktop/windows/winc/tabview.go
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Winc Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winc
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
)
|
||||
|
||||
// TabView creates MultiPanel internally and manages tabs as panels.
|
||||
type TabView struct {
|
||||
ControlBase
|
||||
|
||||
panels *MultiPanel
|
||||
onSelectedChange EventManager
|
||||
}
|
||||
|
||||
func NewTabView(parent Controller) *TabView {
|
||||
tv := new(TabView)
|
||||
|
||||
tv.InitControl("SysTabControl32", parent, 0,
|
||||
w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_TABSTOP|w32.WS_CLIPSIBLINGS)
|
||||
RegMsgHandler(tv)
|
||||
|
||||
tv.panels = NewMultiPanel(parent)
|
||||
|
||||
tv.SetFont(DefaultFont)
|
||||
tv.SetSize(200, 24)
|
||||
return tv
|
||||
}
|
||||
|
||||
func (tv *TabView) Panels() *MultiPanel {
|
||||
return tv.panels
|
||||
}
|
||||
|
||||
func (tv *TabView) tcitemFromPage(panel *Panel) *w32.TCITEM {
|
||||
text := syscall.StringToUTF16(panel.Text())
|
||||
item := &w32.TCITEM{
|
||||
Mask: w32.TCIF_TEXT,
|
||||
PszText: &text[0],
|
||||
CchTextMax: int32(len(text)),
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func (tv *TabView) AddPanel(text string) *Panel {
|
||||
panel := NewPanel(tv.panels)
|
||||
panel.SetText(text)
|
||||
|
||||
item := tv.tcitemFromPage(panel)
|
||||
index := tv.panels.Count()
|
||||
idx := int(w32.SendMessage(tv.hwnd, w32.TCM_INSERTITEM, uintptr(index), uintptr(unsafe.Pointer(item))))
|
||||
if idx == -1 {
|
||||
panic("SendMessage(TCM_INSERTITEM) failed")
|
||||
}
|
||||
|
||||
tv.panels.AddPanel(panel)
|
||||
tv.SetCurrent(idx)
|
||||
return panel
|
||||
}
|
||||
|
||||
func (tv *TabView) DeletePanel(index int) {
|
||||
w32.SendMessage(tv.hwnd, w32.TCM_DELETEITEM, uintptr(index), 0)
|
||||
tv.panels.DeletePanel(index)
|
||||
switch {
|
||||
case tv.panels.Count() > index:
|
||||
tv.SetCurrent(index)
|
||||
case tv.panels.Count() == 0:
|
||||
tv.SetCurrent(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (tv *TabView) Current() int {
|
||||
return tv.panels.Current()
|
||||
}
|
||||
|
||||
func (tv *TabView) SetCurrent(index int) {
|
||||
if index < 0 || index >= tv.panels.Count() {
|
||||
panic("invalid index")
|
||||
}
|
||||
if ret := int(w32.SendMessage(tv.hwnd, w32.TCM_SETCURSEL, uintptr(index), 0)); ret == -1 {
|
||||
panic("SendMessage(TCM_SETCURSEL) failed")
|
||||
}
|
||||
tv.panels.SetCurrent(index)
|
||||
}
|
||||
|
||||
func (tv *TabView) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_NOTIFY:
|
||||
nmhdr := (*w32.NMHDR)(unsafe.Pointer(lparam))
|
||||
|
||||
switch int32(nmhdr.Code) {
|
||||
case w32.TCN_SELCHANGE:
|
||||
cur := int(w32.SendMessage(tv.hwnd, w32.TCM_GETCURSEL, 0, 0))
|
||||
tv.SetCurrent(cur)
|
||||
|
||||
tv.onSelectedChange.Fire(NewEvent(tv, nil))
|
||||
}
|
||||
}
|
||||
return w32.DefWindowProc(tv.hwnd, msg, wparam, lparam)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user