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

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

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

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