5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 08:41:41 +08:00

Fix up dev. Pull in go-webview and winc

This commit is contained in:
Lea Anthony 2022-03-29 19:25:50 +11:00
parent c78ee7e49b
commit a5bf76b30f
131 changed files with 17561 additions and 128 deletions

View File

@ -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
}

View File

@ -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=

View File

@ -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}}

View File

@ -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

View File

@ -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=

View File

@ -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

View File

@ -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=

View File

@ -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

View File

@ -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=

View File

@ -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
}

View File

@ -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"
)

View File

@ -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
}

View File

@ -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")

View File

@ -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"

View File

@ -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"
)

View 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.

View 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 :)

View File

@ -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
}

View File

@ -0,0 +1,8 @@
package edge
type COREWEBVIEW2_COLOR struct {
A uint8
R uint8
G uint8
B uint8
}

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -0,0 +1,10 @@
package edge
type COREWEBVIEW2_PHYSICAL_KEY_STATUS struct {
RepeatCount uint32
ScanCode uint32
IsExtendedKey bool
IsMenuKeyDown bool
WasKeyDown bool
IsKeyReleased bool
}

View File

@ -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
)

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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()
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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), &currentExePath[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)
}
}

View File

@ -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),
)
}

View File

@ -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)),
)
}

View File

@ -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],
)
}

View File

@ -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
}

View File

@ -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]
}

View File

@ -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
}

View File

@ -0,0 +1,6 @@
package webviewloader
import _ "embed"
//go:embed x86/WebView2Loader.dll
var WebView2Loader []byte

View File

@ -0,0 +1,6 @@
package webviewloader
import _ "embed"
//go:embed x64/WebView2Loader.dll
var WebView2Loader []byte

View File

@ -0,0 +1,6 @@
package webviewloader
import _ "embed"
//go:embed arm64/WebView2Loader.dll
var WebView2Loader []byte

View File

@ -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"
)

View File

@ -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"
)

View 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

View 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>

View 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.

View 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.
![Example layout with two toolbars and status bar](dock_topbottom.png)
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.
![Example layout with two toolbars and navigation on the left](dock_topleft.png)
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()
}
```
![Hello World](examples/hello.png)
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.

View 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)
}

View 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
}

View 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
}
}

View 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
}

View 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)
}

View 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)
}

View 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)
}

View 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
}

View 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()
}
}

View 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()
}

View 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)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View 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)
}

View 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}
}

View 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
}

View 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
}

View 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
}

View 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)
}

View 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
)

View 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
}

View 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)
}

View 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)
}

View 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)
}

View 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)
}

View 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
}

View 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)
}

View 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)
}
}

View 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)
}

View 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)
}

View 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)
}

View File

@ -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
}

View 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)
}

View 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
}

View 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
}
}

View 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)
}

View 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)
}

View 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)
}

View 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)
}

View 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)
}

View 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