Compare commits

...

66 commits

Author SHA1 Message Date
Misite Bao
7670273d43 feat(website): init the website 2023-05-30 16:57:54 +08:00
Lea Anthony
a15c0a5441 [v3] Website skeleton 2023-05-30 16:56:35 +08:00
Lea Anthony
19be7f61ac [v3 windows] Fix resizing. Add resize debouncing 2023-05-30 16:36:00 +08:00
Misite Bao
bbf0e8cdad chore: split Taskfile (#2654) 2023-05-30 16:35:59 +08:00
Lea Anthony
7e2d2a29a1 [v3 windows] Initial webview2 working 2023-05-30 16:33:34 +08:00
Lea Anthony
671dc2aa3a [v3 windows] Implement start x/y + parent window for dialogs 2023-05-30 16:33:34 +08:00
Lea Anthony
294a2c701e [v3] Update esbuild 2023-05-30 16:33:34 +08:00
Lea Anthony
01f03c552d [v3] Improve invoke 2023-05-30 16:33:33 +08:00
Lea Anthony
dc46154d94 [v3 mac] Support drag 2023-05-30 16:33:33 +08:00
Lea Anthony
87737c23eb [v3 mac] DisableWindowShadow -> DisableShadow 2023-05-30 16:33:33 +08:00
Lea Anthony
6e6f099a99 [v3 mac] Basic common event support. Taskfile refactor 2023-05-30 16:33:32 +08:00
Lea Anthony
4cb3321c39 [v3 mac] Update README 2023-05-30 16:32:02 +08:00
Lea Anthony
6a2343a1a0 [v3 mac] Add DisableWindowShadow option 2023-05-30 16:32:01 +08:00
Lea Anthony
f2d6dba2cf [v3 windows] initial dialog support. Refactor button callback name 2023-05-30 16:32:01 +08:00
stffabi
dd1ef7fae9 [v3 darwin] Add darwin identifier darwin only file 2023-05-30 16:32:01 +08:00
stffabi
527cff9ec3 [v3 darwin] Add darwin identifier to all C files of darwin 2023-05-30 16:32:00 +08:00
stffabi
ba150eccae [v3 windows] Add close handling: HideOnClose, QuitOnLastWindowClosed and DisableQuitOnLastWindowClosed 2023-05-30 16:32:00 +08:00
Travis McLane
fa74986f1e [v3 example] menu text toggle 2023-05-30 16:32:00 +08:00
Travis McLane
d1085b5bea [v3 example] add menuitem.Hidden / setHidden example 2023-05-30 16:31:59 +08:00
Travis McLane
4d0a14d2eb [v3 darwin] menuitem.setHidden implementation 2023-05-30 16:31:59 +08:00
Travis McLane
8c3439b733 [dialogs] remove default title
without this removal a user would have to call `SetTitle("")` in order to
erase the default if they desire a title-less dialog.
2023-05-30 16:31:59 +08:00
stffabi
a0534d527a [v3 application] Fix race conditions between starting a window/systray and starting the application
Make sure a window is never run before the windowCreated hooks have been executed.
2023-05-30 16:31:59 +08:00
Lea Anthony
a5b52f2795 [v3 windows] initial systray support 2023-05-30 16:31:58 +08:00
Lea Anthony
b526ebd679 [v3 mac] Update api. New template icon. 2023-05-30 16:31:58 +08:00
Lea Anthony
51b9315ae9 [v3 windows] update status 2023-05-30 16:31:58 +08:00
Lea Anthony
8edf44dc31 [v3 windows] New icons 2023-05-30 16:31:57 +08:00
Lea Anthony
9b7626e59e [v3 windows] Move icons to own package, systray dark mode icon, window.Focus(), 2023-05-30 16:31:57 +08:00
Lea Anthony
6dd092c7a9 [v3 windows] Rename systray callback handlers 2023-05-30 16:31:57 +08:00
Lea Anthony
46a0d467c0 [v3 windows] Dialogs to use invokeSync 2023-05-30 16:31:56 +08:00
Lea Anthony
e3b164ae93 [v3 windows] Systray callback handlers 2023-05-30 16:31:56 +08:00
stffabi
d8f58ab20f [v3 windows] Fix wndproc default case with WMMessageToString 2023-05-30 16:31:56 +08:00
stffabi
0de2bccd28 [v3 windows] Do not disable WndProc messaging for systray when updating icon 2023-05-30 16:31:55 +08:00
Lea Anthony
2eaf724710 [v3 windows] Fix systray icon size 2023-05-30 16:31:55 +08:00
Lea Anthony
58138ac09b [v3 windows] Initial systray support 2023-05-30 16:31:55 +08:00
Lea Anthony
143f090422 [v3] Use invokeSync for systray methods 2023-05-30 16:31:55 +08:00
Lea Anthony
f8f466ba7e [v3 windows] Support irregular shaped windows. Centered option. 2023-05-30 16:31:54 +08:00
Lea Anthony
cb28de47f8 [v3 windows] Support irregular shaped windows 2023-05-30 16:31:54 +08:00
stffabi
4ad2475ed6 [v3] Add some missing methods for darwin and windows 2023-05-30 16:31:54 +08:00
Travis McLane
0172078536 [w32] move windows specific code to impl file 2023-05-30 16:31:53 +08:00
Travis McLane
0bb1fb512a [w32] add missing build constraint 2023-05-30 16:31:53 +08:00
Lea Anthony
b6940d95a2 [v3 windows] Add frameless resize 2023-05-30 16:31:53 +08:00
stffabi
75f0457375 [v3 windows] Add HiDPI awareness 2023-05-30 16:31:52 +08:00
stffabi
ffe31b6265 [v3 windows] Add frameless support 2023-05-30 16:31:52 +08:00
Lea Anthony
8963610722 [v3 windows] Implement getScreen 2023-05-30 16:31:52 +08:00
Lea Anthony
66bfcf0e36 [v3] Ensure impl calls from WebvieWindow are on the main thread. Support size. 2023-05-30 16:31:52 +08:00
Lea Anthony
0b3559abfe [v3] Fix examples 2023-05-30 16:31:51 +08:00
Lea Anthony
773389ee5e [v3] Update examples to use correct options. 2023-05-30 16:31:51 +08:00
Lea Anthony
4c04991d4d [v3] Change WebviewWindow options to be a value, not a pointer. Support Un/Fullscreen. Remove main thread switching. Use parent options instead of local variables. 2023-05-30 16:31:51 +08:00
Lea Anthony
d56bb59b72 [v3 Windows] Support application hide/show. Add WebviewWindow.IsVisible(). 2023-05-30 16:31:50 +08:00
Lea Anthony
6e92a4f71e [v3 Windows] Support setMin/MaxSize, setPosition 2023-05-30 16:31:50 +08:00
Lea Anthony
1f6217c0d8 [v3 Windows] Add Support for SetTitle, Center, Un/Minimise/Maximise, IsMin/Maximised, IsNormal, Show/Hide 2023-05-30 16:31:50 +08:00
Lea Anthony
17204bebd0 [v3 Breaking Change] Add NativeWindowHandle method to WebviewWindow. 2023-05-30 16:31:49 +08:00
Lea Anthony
728e2019d8 [v3 windows] Moved w32 from internal to pkg so it may be used by applications 2023-05-30 16:31:49 +08:00
Lea Anthony
7f3fdd6977 [v3 windows] Add WndProcInterceptor for custom message processing 2023-05-30 16:31:49 +08:00
Lea Anthony
e8798f8371 [v3 windows] Rename options_windows.go -> options_win.go 2023-05-30 16:31:48 +08:00
Lea Anthony
402b743553 [v3 windows] Add APM Events 2023-05-30 16:31:48 +08:00
Lea Anthony
f08ae2fc62 [v3] Update application.On and window.On to return functions that unregister the listener. WebviewWindow.onApplicationEvent is a helper which will manage the unregistering for you on window destroy. 2023-05-30 16:31:48 +08:00
Lea Anthony
178ea9c8c5 [windows] Split out wndProc. Generate windows events, support per-window themes 2023-05-30 16:31:48 +08:00
Lea Anthony
7c63cee9e8 [windows] Support AlwaysOnTop, EnableResize at runtime. Added Solid/Transparent/Translucent options. 2023-05-30 16:31:47 +08:00
stffabi
4a60dfc373 [v3, windows] Add MainThread dispatching and fixes the blocking window 2023-05-30 16:31:47 +08:00
Lea Anthony
829a829cb4 [windows] WIP 2023-05-30 16:31:47 +08:00
Travis McLane
34896ccb4e [darwin] add getPrimaryScreen/getScreens to impl (#2618) 2023-05-30 16:31:46 +08:00
Lea Anthony
5df5eb6a04 Fix module path for non-modified repo 2023-05-30 16:31:46 +08:00
Lea Anthony
31ba36baf3 [windows] Initial commit 2023-05-30 16:31:46 +08:00
Lea Anthony
bf10f71760 [windows] Fix paths for wails init 2023-05-30 16:31:45 +08:00
Lea Anthony
8aa61fff6d Intial STATUS.md commit 2023-05-30 16:31:45 +08:00
203 changed files with 16244 additions and 1009 deletions

1
v3/.gitignore vendored
View file

@ -6,3 +6,4 @@ cmd/wails/wails
/examples/menu/menu
/examples/clipboard/clipboard
/examples/plain/plain
/website/venv/

315
v3/STATUS.md Normal file
View file

@ -0,0 +1,315 @@
# Status
Status of features in v3. Incomplete - please add as you see fit.
## Application
Application interface methods
| Method | Windows | Linux | Mac | Notes |
|---------------------------------------------------------------|---------|-------|-----|-------|
| run() error | | | Y | |
| destroy() | | | Y | |
| setApplicationMenu(menu *Menu) | | | Y | |
| name() string | | | Y | |
| getCurrentWindowID() uint | | | Y | |
| showAboutDialog(name string, description string, icon []byte) | | | Y | |
| setIcon(icon []byte) | | | Y | |
| on(id uint) | | | Y | |
| dispatchOnMainThread(fn func()) | Y | | Y | |
| hide() | Y | | Y | |
| show() | Y | | Y | |
| getPrimaryScreen() (*Screen, error) | | | Y | |
| getScreens() ([]*Screen, error) | | | Y | |
## Webview Window
Webview Window Interface Methods
| Method | Windows | Linux | Mac | Notes |
|----------------------------------------------------|---------|-------|-----|------------------------------------------|
| center() | Y | | Y | |
| close() | | | Y | |
| destroy() | | | Y | |
| execJS(js string) | | | Y | |
| focus() | Y | | Y | |
| forceReload() | | | Y | |
| fullscreen() | Y | | Y | |
| getScreen() (*Screen, error) | | | Y | |
| getZoom() float64 | | | Y | |
| height() int | Y | | Y | |
| hide() | Y | | Y | |
| isFullscreen() bool | Y | | Y | |
| isMaximised() bool | Y | | Y | |
| isMinimised() bool | Y | | Y | |
| maximise() | Y | | Y | |
| minimise() | Y | | Y | |
| nativeWindowHandle() (uintptr, error) | Y | | Y | |
| on(eventID uint) | | | Y | |
| openContextMenu(menu *Menu, data *ContextMenuData) | | | Y | |
| position() (int, int) | Y | | Y | |
| reload() | | | Y | |
| run() | Y | | Y | |
| setAlwaysOnTop(alwaysOnTop bool) | Y | | Y | |
| setBackgroundColour(color RGBA) | Y | | Y | |
| setFrameless(bool) | | | Y | |
| setFullscreenButtonEnabled(enabled bool) | - | | Y | There is no fullscreen button in Windows |
| setHTML(html string) | | | Y | |
| setMaxSize(width, height int) | Y | | Y | |
| setMinSize(width, height int) | Y | | Y | |
| setPosition(x int, y int) | Y | | Y | |
| setResizable(resizable bool) | Y | | Y | |
| setSize(width, height int) | Y | | Y | |
| setTitle(title string) | Y | | Y | |
| setURL(url string) | | | Y | |
| setZoom(zoom float64) | | | Y | |
| show() | Y | | Y | |
| size() (int, int) | Y | | Y | |
| toggleDevTools() | | | Y | |
| unfullscreen() | Y | | Y | |
| unmaximise() | Y | | Y | |
| unminimise() | Y | | Y | |
| width() int | Y | | Y | |
| zoom() | | | Y | |
| zoomIn() | | | Y | |
| zoomOut() | | | Y | |
| zoomReset() | | | Y | |
## Runtime
### Application
| Feature | Windows | Linux | Mac | Notes |
|---------|---------|-------|-----|-------|
| Quit | | | Y | |
| Hide | Y | | Y | |
| Show | Y | | Y | |
### Dialogs
| Feature | Windows | Linux | Mac | Notes |
|----------|---------|-------|-----|-------|
| Info | Y | | Y | |
| Warning | Y | | Y | |
| Error | Y | | Y | |
| Question | Y | | Y | |
| OpenFile | Y | | Y | |
| SaveFile | Y | | Y | |
### Clipboard
| Feature | Windows | Linux | Mac | Notes |
|---------|---------|-------|-----|-------|
| SetText | | | Y | |
| Text | | | Y | |
### ContextMenu
| Feature | Windows | Linux | Mac | Notes |
|-----------------|---------|-------|-----|-------|
| OpenContextMenu | | | Y | |
### Screens
| Feature | Windows | Linux | Mac | Notes |
|------------|---------|-------|-----|-------|
| GetAll | Y | | Y | |
| GetPrimary | | | Y | |
| GetCurrent | | | Y | |
### Window
| Feature | Windows | Linux | Mac | Notes |
|---------------------|---------|-------|-----|--------------------------------------------------------------------------------------|
| SetTitle | Y | | Y | |
| SetSize | Y | | Y | |
| Size | Y | | Y | |
| SetPosition | Y | | Y | |
| Position | Y | | Y | |
| Focus | Y | | Y | |
| FullScreen | Y | | Y | |
| UnFullscreen | Y | | Y | |
| Minimise | Y | | Y | |
| UnMinimise | Y | | Y | |
| Maximise | Y | | Y | |
| UnMaximise | Y | | Y | |
| Show | Y | | Y | |
| Hide | Y | | Y | |
| Center | Y | | Y | |
| SetBackgroundColour | Y | | Y | https://github.com/MicrosoftEdge/WebView2Feedback/issues/1621#issuecomment-938234294 |
| SetAlwaysOnTop | Y | | Y | |
| SetResizable | Y | | Y | |
| SetMinSize | Y | | Y | |
| SetMaxSize | Y | | Y | |
| Width | Y | | Y | |
| Height | Y | | Y | |
| ZoomIn | | | Y | Increase view scale |
| ZoomOut | | | Y | Decrease view scale |
| ZoomReset | | | Y | Reset view scale |
| GetZoom | | | Y | Get current view scale |
| SetZoom | | | Y | Set view scale |
| Screen | | | Y | Get screen for window |
### Window Options
A 'Y' in the table below indicates that the option has been tested and is applied when the window is created.
An 'X' indicates that the option is not supported by the platform.
| Feature | Windows | Linux | Mac | Notes |
|---------------------------------|---------|-------|-----|--------------------------------------------|
| Name | Y | | | |
| Title | Y | | | |
| Width | Y | | | |
| Height | Y | | | |
| AlwaysOnTop | Y | | | |
| URL | | | | |
| DisableResize | Y | | | |
| Frameless | Y | | | |
| MinWidth | Y | | | |
| MinHeight | Y | | | |
| MaxWidth | Y | | | |
| MaxHeight | Y | | | |
| StartState | Y | | | |
| Mac | - | - | | |
| BackgroundType | | | | Acrylic seems to work but the others don't |
| BackgroundColour | Y | | | |
| HTML | | | | |
| JS | | | | |
| CSS | | | | |
| X | Y | | | |
| Y | Y | | | |
| HideOnClose | Y | | | |
| FullscreenButtonEnabled | Y | | | |
| Hidden | Y | | | |
| EnableFraudulentWebsiteWarnings | | | | |
| Zoom | | | | |
| ZoomControlEnabled | | | | |
| OpenInspectorOnStartup | | | | |
| EnableDragAndDrop | | | | |
| Windows | Y | - | - | |
| Focused | Y | | | |
### Log
To log or not to log? System logger vs custom logger.
## Menu
| Event | Windows | Linux | Mac | Notes |
|--------------------------|---------|-------|-----|-------|
| Default Application Menu | | | Y | |
## Tray Menus
| Feature | Windows | Linux | Mac | Notes |
|--------------------|---------|-------|-----|----------------------------------------------------------------------|
| Icon | Y | | Y | Windows has default icons for light/dark mode & supports PNG or ICO. |
| Label | - | | Y | |
| Label (ANSI Codes) | - | | | |
| Menu | Y | | Y | |
## Cross Platform Events
Mapping native events to cross-platform events.
| Event | Windows | Linux | Mac | Notes |
|--------------------------|---------|-------|-----------------|-------|
| WindowWillClose | | | WindowWillClose | |
| WindowDidClose | | | | |
| WindowDidResize | | | | |
| WindowDidHide | | | | |
| ApplicationWillTerminate | | | | |
... Add more
## Bindings Generation
TBD
## Models Generation
TBD
## Task file
TBD
## Theme
| Mode | Windows | Linux | Mac | Notes |
|--------|---------|-------|-----|-------|
| Dark | Y | | | |
| Light | Y | | | |
| System | Y | | | |
## NSIS Installer
TBD
## Templates
TBD
## Plugins
Built-in plugin support:
| Plugin | Windows | Linux | Mac | Notes |
|-----------------|---------|-------|-----|-------|
| Browser | | | Y | |
| KV Store | | | Y | |
| Log | | | Y | |
| Single Instance | | | Y | |
| SQLite | | | Y | |
| Start at login | | | Y | |
| Server | | | | |
## Packaging
| | Windows | Linux | Mac | Notes |
|-----------------|---------|-------|-----|-------|
| Icon Generation | | | Y | |
| Icon Embedding | | | Y | |
| Info.plist | - | | Y | |
| NSIS Installer | | | - | |
| Mac bundle | | | Y | |
| Windows exe | | | - | |
## Frameless Windows
| Feature | Windows | Linux | Mac | Notes |
|---------|---------|-------|-----|-------|
| Resize | | | | |
| Drag | | | | |
## Mac Specific
- [x] Translucency
### Mac Options
| Feature | Default | Notes |
|-------------------------|-------------------|------------------------------------------------------|
| Backdrop | MacBackdropNormal | Standard solid window |
| DisableShadow | false | |
| TitleBar | | Standard window decorations by default |
| Appearance | DefaultAppearance | |
| InvisibleTitleBarHeight | 0 | Creates an invisible title bar for frameless windows |
## Windows Specific
- [x] Translucency
- [x] Custom Themes
### Windows Options
| Feature | Default | Notes |
|-----------------------------------|---------|---------------------------------------------|
| BackdropType | | |
| DisableIcon | | |
| Theme | | |
| CustomTheme | | |
| DisableFramelessWindowDecorations | | |
| WindowMask | nil | Makes the window the contents of the bitmap |
## Linux Specific

View file

@ -1,49 +0,0 @@
# TODO
Informal and incomplete list of things needed in v3.
## General
- [x] Generate Bindings
- [x] Generate TS Models
- [ ] Dev Mode
- [ ] Generate Info.Plist from `info.json`
- [ ] Windows Port
- [ ] Linux Port
## Runtime
- [x] Pass window ID with window calls in JS
- [x] Implement alias for `window` in JS
- [x] Implement runtime dispatcher
- [x] Log
- [x] Same Window
- [ ] Other Window
- [x] Dialogs
- [x] Info
- [x] Warning
- [x] Error
- [x] Question
- [x] OpenFile
- [x] SaveFile
- [x] Events
- [x] Screens
- [x] Clipboard
- [x] Application
- [ ] Create `.d.ts` file
## Templates
- [ ] Create plain template
- [ ] Improve default template
## Runtime
- [ ] To log or not to log?
- [ ] Unify cross-platform events, eg. `onClose`
## Plugins
- [ ] Move logins to `v3/plugins`
- [ ] Expose application logger to plugins

View file

@ -3,124 +3,16 @@
version: "3"
includes:
runtime:
taskfile: ./internal/runtime
dir: ./internal/runtime
website:
taskfile: ./website
dir: ./website
optional: true
tasks:
build-runtime-debug:
dir: internal/runtime
internal: true
cmds:
- npx esbuild desktop/main.js --bundle --sourcemap=inline --outfile=runtime_debug_desktop_{{.PLATFORM}}.js --define:DEBUG=true --define:WINDOWS={{.WINDOWS}} --define:DARWIN={{.DARWIN}} --define:LINUX={{.LINUX}} --define:PLATFORM={{.PLATFORM}}
build-runtime-debug-windows:
cmds:
- task: build-runtime-debug
vars:
WINDOWS: true
DARWIN: false
LINUX: false
PLATFORM: windows
build-runtime-debug-linux:
cmds:
- task: build-runtime-debug
vars:
WINDOWS: false
DARWIN: false
LINUX: true
PLATFORM: linux
build-runtime-debug-darwin:
cmds:
- task: build-runtime-debug
vars:
WINDOWS: false
DARWIN: true
LINUX: false
PLATFORM: darwin
build-runtime-production:
dir: internal/runtime
internal: true
cmds:
- npx esbuild desktop/main.js --bundle --minify --outfile=runtime_production_desktop_{{.PLATFORM}}.js --define:DEBUG=true --define:WINDOWS={{.WINDOWS}} --define:DARWIN={{.DARWIN}} --define:LINUX={{.LINUX}} --define:PLATFORM={{.PLATFORM}}
build-runtime-production-windows:
cmds:
- task: build-runtime-production
vars:
WINDOWS: true
DARWIN: false
LINUX: false
PLATFORM: windows
build-runtime-production-linux:
cmds:
- task: build-runtime-production
vars:
WINDOWS: false
DARWIN: false
LINUX: true
PLATFORM: linux
build-runtime-production-darwin:
cmds:
- task: build-runtime-production
vars:
WINDOWS: false
DARWIN: true
LINUX: false
PLATFORM: darwin
install-runtime-dev-deps:
dir: internal/runtime/dev
internal: true
sources:
- package.json
cmds:
- npm install
install-runtime-deps:
dir: internal/runtime
internal: true
sources:
- package.json
cmds:
- npm install
test-runtime:
dir: internal/runtime
cmds:
- npx vitest run
update-runtime:
dir: internal/runtime
cmds:
- npx npm-check-updates -u
build-runtime-all:
dir: internal/runtime
deps:
- build-runtime-production-darwin
- build-runtime-production-windows
- build-runtime-production-linux
- build-runtime-debug-darwin
- build-runtime-debug-windows
- build-runtime-debug-linux
cmds:
- cmd: echo "build complete"
build-runtime:
dir: internal/runtime
deps:
- install-runtime-deps
cmds:
- task: build-runtime-all
recreate-template-dir:
dir: internal/templates
internal: true
@ -129,7 +21,7 @@ tasks:
- rm -rf {{.TEMPLATE_DIR}}
- mkdir -p {{.TEMPLATE_DIR}}
generate-template:
generate:template:
dir: internal/templates/{{.TEMPLATE}}
deps:
- task: recreate-template-dir
@ -156,49 +48,54 @@ tasks:
- go install
- echo "Reinstalled wails CLI"
generate-templates:
generate:events:
dir: tasks/events
cmds:
- go run generate.go
generate:templates:
dir: internal/templates/
deps:
- task: generate-template
- task: generate:template
vars:
TEMPLATE: svelte
- task: generate-template
- task: generate:template
vars:
TEMPLATE: svelte-ts
- task: generate-template
- task: generate:template
vars:
TEMPLATE: vue
- task: generate-template
- task: generate:template
vars:
TEMPLATE: vue-ts
- task: generate-template
- task: generate:template
vars:
TEMPLATE: react
- task: generate-template
- task: generate:template
vars:
TEMPLATE: react-ts
- task: generate-template
- task: generate:template
vars:
TEMPLATE: preact
- task: generate-template
- task: generate:template
vars:
TEMPLATE: preact-ts
- task: generate-template
- task: generate:template
vars:
TEMPLATE: lit
- task: generate-template
- task: generate:template
vars:
TEMPLATE: lit-ts
- task: generate-template
- task: generate:template
vars:
TEMPLATE: vanilla
- task: generate-template
- task: generate:template
vars:
TEMPLATE: vanilla-ts
- task: generate-template
- task: generate:template
vars:
TEMPLATE: react-swc
- task: generate-template
- task: generate:template
vars:
TEMPLATE: react-swc-ts
cmds:

View file

@ -59,6 +59,24 @@ TBD
Dialogs are now available in JavaScript!
### Windows
Dialog buttons in Windows are not configurable and are constant depending on the type of dialog. To trigger a callback when a button is pressed, create a button with the same name as the button you wish to have the callback attached to.
Example: Create a button with the label `Ok` and use `OnClick()` to set the callback method:
```go
dialog := app.QuestionDialog().
SetTitle("Update").
SetMessage("The cancel button is selected when pressing escape")
ok := dialog.AddButton("Ok")
ok.OnClick(func() {
// Do something
})
no := dialog.AddButton("Cancel")
dialog.SetDefaultButton(ok)
dialog.SetCancelButton(no)
dialog.Show()
```
## Drag and Drop
Native drag and drop can be enabled per-window. Simply set the `EnableDragAndDrop` window config option to `true` and the window will allow files to be dragged onto it. When this happens, the `events.FilesDropped` event will be emitted. The filenames can then be retrieved from the WindowEventContext using the `DroppedFiles()` method. This returns a slice of strings containing the filenames.
@ -180,3 +198,39 @@ const MyEnum = {
- Why use `float64`? Can't we use `int`?
- Because JavaScript doesn't have a concept of `int`. Everything is a `number`, which translates to `float64` in Go. There are also restrictions on casting types in Go's reflection package, which means using `int` doesn't work.
### BackgroundColour
In v2, this was a pointer to an `RGBA` struct. In v3, this is an `RGBA` struct value.
### WindowIsTranslucent
This flag has been removed. Now there is a `BackgroundType` flag that can be used to set the type of background the window should have.
This flag can be set to any of the following values:
- `BackgroundTypeSolid` - The window will have a solid background
- `BackgroundTypeTransparent` - The window will have a transparent background
- `BackgroundTypeTranslucent` - The window will have a translucent background
On Windows, if the `BackgroundType` is set to `BackgroundTypeTranslucent`, the type of translucency can be set using the
`BackdropType` flag in the `WindowsWindow` options. This can be set to any of the following values:
- `Auto` - The window will use an effect determined by the system
- `None` - The window will have no background
- `Mica` - The window will use the Mica effect
- `Acrylic` - The window will use the acrylic effect
- `Tabbed` - The window will use the tabbed effect
## Windows Application Options
### WndProcInterceptor
If this is set, the WndProc will be intercepted and the function will be called. This allows you to handle Windows
messages directly. The function should have the following signature:
```go
func(hwnd uintptr, msg uint32, wParam, lParam uintptr) (returnValue uintptr, shouldReturn)
```
The `shouldReturn` value should be set to `true` if the returnValue should be returned by the main wndProc method.
If it is set to `false`, the return value will be ignored and the message will continue to be processed by the main
wndProc method.

View file

@ -5,15 +5,20 @@ go 1.20
require github.com/wailsapp/wails/v3 v3.0.0-alpha.0
require (
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leaanthony/slicer v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/samber/lo v1.37.0 // indirect
github.com/wailsapp/go-webview2 v1.0.1 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/wailsapp/wails/v2 v2.3.2-0.20230117193915-45c3a501d9e6 // indirect
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.8.0 // indirect
)
replace github.com/wailsapp/wails/v3 => ../..

View file

@ -1,7 +1,13 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
@ -18,6 +24,8 @@ github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpo
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/wailsapp/go-webview2 v1.0.1 h1:dEJIeEApW/MhO2tTMISZBFZPuW7kwrFA1NtgFB1z1II=
github.com/wailsapp/go-webview2 v1.0.1/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4=
@ -25,8 +33,12 @@ golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View file

@ -19,14 +19,15 @@ func main() {
Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
})
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Assets: application.AssetOptions{
FS: assets,
},
})
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
URL: "/",
})
err := app.Run()
if err != nil {

View file

@ -67,7 +67,7 @@ func main() {
if runtime.GOOS == "darwin" {
myMenu.Add("New WebviewWindow (MacTitleBarHiddenInset)").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Mac: application.MacWindow{
TitleBar: application.MacTitleBarHiddenInset,
InvisibleTitleBarHeight: 25,
@ -81,7 +81,7 @@ func main() {
})
myMenu.Add("New WebviewWindow (MacTitleBarHiddenInsetUnified)").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Mac: application.MacWindow{
TitleBar: application.MacTitleBarHiddenInsetUnified,
InvisibleTitleBarHeight: 50,
@ -95,7 +95,7 @@ func main() {
})
myMenu.Add("New WebviewWindow (MacTitleBarHidden)").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Mac: application.MacWindow{
TitleBar: application.MacTitleBarHidden,
InvisibleTitleBarHeight: 25,

View file

@ -25,7 +25,7 @@ func main() {
},
})
mainWindow := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
mainWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Context Menu Demo",
Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent,
@ -34,7 +34,7 @@ func main() {
},
})
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Context Menu Demo",
Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent,

View file

@ -2,6 +2,7 @@ package main
import (
_ "embed"
"github.com/wailsapp/wails/v3/pkg/icons"
"log"
"os"
"runtime"
@ -19,6 +20,7 @@ func main() {
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
})
// Create a custom menu
menu := app.NewMenu()
menu.AddRole(application.AppMenu)
@ -45,7 +47,7 @@ func main() {
dialog := app.InfoDialog()
dialog.SetTitle("Custom Icon Example")
dialog.SetMessage("Using a custom icon")
dialog.SetIcon(application.DefaultApplicationIcon)
dialog.SetIcon(icons.ApplicationDarkMode256)
dialog.Show()
})
@ -85,7 +87,7 @@ func main() {
dialog := app.QuestionDialog()
dialog.SetTitle("Custom Icon Example")
dialog.SetMessage("Using a custom icon")
dialog.SetIcon(application.WailsLogoWhiteTransparent)
dialog.SetIcon(icons.WailsLogoWhiteTransparent)
dialog.SetDefaultButton(dialog.AddButton("I like it!"))
dialog.AddButton("Not so keen...")
dialog.Show()
@ -112,7 +114,7 @@ func main() {
dialog := app.WarningDialog()
dialog.SetTitle("Custom Icon Example")
dialog.SetMessage("Using a custom icon")
dialog.SetIcon(application.DefaultApplicationIcon)
dialog.SetIcon(icons.ApplicationLightMode256)
dialog.Show()
})
@ -137,7 +139,7 @@ func main() {
dialog := app.ErrorDialog()
dialog.SetTitle("Custom Icon Example")
dialog.SetMessage("Using a custom icon")
dialog.SetIcon(application.WailsLogoWhite)
dialog.SetIcon(icons.WailsLogoWhite)
dialog.Show()
})

View file

@ -25,7 +25,7 @@ func main() {
},
})
window := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
window := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Drag-n-drop Demo",
Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent,

View file

@ -26,10 +26,12 @@ func main() {
},
})
// Custom event handling
app.Events.On("myevent", func(e *application.WailsEvent) {
log.Printf("[Go] WailsEvent received: %+v\n", e)
})
// OS specific application events
app.On(events.Mac.ApplicationDidFinishLaunching, func() {
for {
log.Println("Sending event")
@ -41,7 +43,12 @@ func main() {
}
})
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
// Platform agnostic events
app.On(events.Common.ApplicationStarted, func() {
println("events.Common.ApplicationStarted fired!")
})
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Events Demo",
Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent,
@ -49,7 +56,7 @@ func main() {
InvisibleTitleBarHeight: 50,
},
})
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Events Demo",
Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent,

View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
.container {
display: flex;
flex-wrap: wrap;
height: 100%;
}
.quarter {
flex: 50%; /* This will cause elements to take up 50% of the container's width, causing them to wrap into 4 equal sections. */
box-sizing: border-box; /* This makes the padding part of the element's total width and height, ensuring they don't exceed 50%. */
padding: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="quarter" style="background-color: lightblue; --wails-draggable: drag">Draggable</div>
<div class="quarter" style="background-color: lightgreen;">Not Draggable</div>
<div class="quarter" style="background-color: lightpink;">Not Draggable</div>
<div class="quarter" style="background-color: lightyellow; --wails-draggable: drag">Draggable</div>
</div>
</body>
</html>

View file

@ -0,0 +1,36 @@
package main
import (
"embed"
_ "embed"
"log"
"github.com/wailsapp/wails/v3/pkg/application"
)
//go:embed assets
var assets embed.FS
func main() {
app := application.New(application.Options{
Name: "Frameless Demo",
Description: "A demo of frameless windows",
Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
Assets: application.AssetOptions{
FS: assets,
},
})
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Frameless: true,
})
err := app.Run()
if err != nil {
log.Fatal(err.Error())
}
}

View file

@ -2,6 +2,7 @@ package main
import (
_ "embed"
"github.com/wailsapp/wails/v3/pkg/icons"
"log"
"runtime"
"sync"
@ -62,9 +63,9 @@ func main() {
mySystray := app.NewSystemTray()
mySystray.SetLabel("Wails")
if runtime.GOOS == "darwin" {
mySystray.SetTemplateIcon(application.DefaultMacTemplateIcon)
mySystray.SetTemplateIcon(icons.SystrayMacTemplate)
} else {
mySystray.SetIcon(application.DefaultApplicationIcon)
mySystray.SetIcon(icons.ApplicationDarkMode256)
}
myMenu := app.NewMenu()
myMenu.Add("Item 1")
@ -102,20 +103,20 @@ func main() {
mySystray := app.NewSystemTray()
mySystray.SetLabel("Wails is awesome")
if runtime.GOOS == "darwin" {
mySystray.SetTemplateIcon(application.DefaultMacTemplateIcon)
mySystray.SetTemplateIcon(icons.SystrayMacTemplate)
} else {
mySystray.SetIcon(application.DefaultApplicationIcon)
mySystray.SetIcon(icons.ApplicationDarkMode256)
}
mySystray.SetMenu(myMenu)
mySystray.SetIconPosition(application.NSImageLeading)
myWindow := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
myWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Kitchen Sink",
Width: 600,
Height: 400,
AlwaysOnTop: true,
DisableResize: false,
BackgroundColour: &application.RGBA{
BackgroundColour: application.RGBA{
Red: 255,
Green: 255,
Blue: 255,
@ -184,7 +185,7 @@ func main() {
*/
var myWindow2 *application.WebviewWindow
var myWindow2Lock sync.RWMutex
myWindow2 = app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
myWindow2 = app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "#2",
Width: 1024,
Height: 768,

View file

@ -29,7 +29,12 @@ func main() {
// Click callbacks
myMenu.Add("Click Me!").OnClick(func(ctx *application.Context) {
ctx.ClickedMenuItem().SetLabel("Thanks mate!")
switch ctx.ClickedMenuItem().Label() {
case "Click Me!":
ctx.ClickedMenuItem().SetLabel("Thanks mate!")
case "Thanks mate!":
ctx.ClickedMenuItem().SetLabel("Click Me!")
}
})
// You can control the current window from the menu
@ -82,7 +87,15 @@ func main() {
beatles.SetLabel("Hello")
}
})
myMenu.Add("Hide the beatles").OnClick(func(ctx *application.Context) {
if beatles.Hidden() {
ctx.ClickedMenuItem().SetLabel("Unhide the beatles!")
beatles.SetHidden(false)
} else {
beatles.SetHidden(true)
ctx.ClickedMenuItem().SetLabel("Hide the beatles!")
}
})
app.SetMenu(menu)
app.NewWebviewWindow()

View file

@ -4,6 +4,7 @@ import (
_ "embed"
"log"
"net/http"
"time"
"github.com/wailsapp/wails/v3/pkg/application"
)
@ -23,7 +24,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; user-select: none; -ms-user-select: none; -webkit-user-select: none; } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{
@ -38,6 +39,21 @@ func main() {
println("clicked")
})
go func() {
time.Sleep(5 * time.Second)
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle new Window from GoRoutine",
Width: 500,
Height: 500,
Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified,
InvisibleTitleBarHeight: 50,
},
})
}()
err := app.Run()
if err != nil {

View file

@ -24,7 +24,7 @@ func main() {
},
})
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Screen Demo",
Width: 800,
Height: 600,

View file

@ -2,12 +2,21 @@ package main
import (
_ "embed"
"fmt"
"github.com/wailsapp/wails/v3/pkg/icons"
"log"
"runtime"
"github.com/wailsapp/wails/v3/pkg/application"
)
var counter int
func clickCount() int {
counter++
return counter
}
func main() {
app := application.New(application.Options{
Name: "Systray Demo",
@ -17,26 +26,50 @@ func main() {
},
})
window := app.NewWebviewWindow().Hide()
systemTray := app.NewSystemTray()
if runtime.GOOS == "darwin" {
systemTray.SetIcon(application.DefaultMacTemplateIcon)
systemTray.SetTemplateIcon(icons.SystrayMacTemplate)
}
myMenu := app.NewMenu()
myMenu.Add("Hello World!").OnClick(func(ctx *application.Context) {
app.InfoDialog().SetTitle("Hello World!").SetMessage("Hello World!").Show()
println("Hello World!")
q := app.QuestionDialog().SetTitle("Ready?").SetMessage("Are you feeling ready?")
q.AddButton("Yes").OnClick(func() {
println("Awesome!")
})
q.AddButton("No").SetAsDefault().OnClick(func() {
println("Boo!")
})
q.Show()
})
subMenu := myMenu.AddSubmenu("Submenu")
subMenu.Add("Click me!").OnClick(func(ctx *application.Context) {
ctx.ClickedMenuItem().SetLabel("Clicked!")
})
myMenu.AddSeparator()
myMenu.AddCheckbox("Checked", true).OnClick(func(ctx *application.Context) {
println("Checked: ", ctx.ClickedMenuItem().Checked())
app.InfoDialog().SetTitle("Hello World!").SetMessage("Hello World!").Show()
})
myMenu.Add("Enabled").OnClick(func(ctx *application.Context) {
println("Click me!")
ctx.ClickedMenuItem().SetLabel("Disabled!").SetEnabled(false)
})
myMenu.AddSeparator()
myMenu.Add("Quit").OnClick(func(ctx *application.Context) {
app.Quit()
})
systemTray.SetMenu(myMenu)
systemTray.OnClick(func() {
window.SetTitle(fmt.Sprintf("Clicked %d times", clickCount()))
window.Show().Focus()
})
err := app.Run()
if err != nil {

View file

@ -2,14 +2,8 @@ package main
import (
_ "embed"
"fmt"
"log"
"math/rand"
"runtime"
"strconv"
"time"
"github.com/wailsapp/wails/v3/pkg/events"
"log"
"github.com/wailsapp/wails/v3/pkg/application"
)
@ -25,257 +19,261 @@ func main() {
app.On(events.Mac.ApplicationDidFinishLaunching, func() {
log.Println("ApplicationDidFinishLaunching")
})
currentWindow := func(fn func(window *application.WebviewWindow)) {
if app.CurrentWindow() != nil {
fn(app.CurrentWindow())
} else {
println("Current WebviewWindow is nil")
}
}
// Create a custom menu
menu := app.NewMenu()
menu.AddRole(application.AppMenu)
windowCounter := 1
// Let's make a "Demo" menu
myMenu := menu.AddSubmenu("New")
myMenu.Add("New WebviewWindow").
SetAccelerator("CmdOrCtrl+N").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindow().
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
SetURL("https://wails.io").
Show()
windowCounter++
})
myMenu.Add("New WebviewWindow (Hide on Close").
SetAccelerator("CmdOrCtrl+H").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{HideOnClose: true}).
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
SetURL("https://wails.io").
Show()
windowCounter++
})
myMenu.Add("New Frameless WebviewWindow").
SetAccelerator("CmdOrCtrl+F").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
X: rand.Intn(1000),
Y: rand.Intn(800),
Frameless: true,
Mac: application.MacWindow{
InvisibleTitleBarHeight: 50,
},
}).Show()
windowCounter++
})
if runtime.GOOS == "darwin" {
myMenu.Add("New WebviewWindow (MacTitleBarHiddenInset)").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Mac: application.MacWindow{
TitleBar: application.MacTitleBarHiddenInset,
InvisibleTitleBarHeight: 25,
},
}).
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
SetHTML("<br/><br/><p>A MacTitleBarHiddenInset WebviewWindow example</p>").
Show()
windowCounter++
})
myMenu.Add("New WebviewWindow (MacTitleBarHiddenInsetUnified)").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Mac: application.MacWindow{
TitleBar: application.MacTitleBarHiddenInsetUnified,
InvisibleTitleBarHeight: 50,
},
}).
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
SetHTML("<br/><br/><p>A MacTitleBarHiddenInsetUnified WebviewWindow example</p>").
Show()
windowCounter++
})
myMenu.Add("New WebviewWindow (MacTitleBarHidden)").
OnClick(func(ctx *application.Context) {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Mac: application.MacWindow{
TitleBar: application.MacTitleBarHidden,
InvisibleTitleBarHeight: 25,
},
}).
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
SetHTML("<br/><br/><p>A MacTitleBarHidden WebviewWindow example</p>").
Show()
windowCounter++
})
}
sizeMenu := menu.AddSubmenu("Size")
sizeMenu.Add("Set Size (800,600)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetSize(800, 600)
})
//
//currentWindow := func(fn func(window *application.WebviewWindow)) {
// if app.CurrentWindow() != nil {
// fn(app.CurrentWindow())
// } else {
// println("Current WebviewWindow is nil")
// }
//}
//
//// Create a custom menu
//menu := app.NewMenu()
//menu.AddRole(application.AppMenu)
//
//windowCounter := 1
//
//// Let's make a "Demo" menu
//myMenu := menu.AddSubmenu("New")
//
//myMenu.Add("New WebviewWindow").
// SetAccelerator("CmdOrCtrl+N").
// OnClick(func(ctx *application.Context) {
// app.NewWebviewWindow().
// SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
// SetPosition(rand.Intn(1000), rand.Intn(800)).
// SetURL("https://wails.io").
// Show()
// windowCounter++
// })
//myMenu.Add("New WebviewWindow (Hide on Close").
// SetAccelerator("CmdOrCtrl+H").
// OnClick(func(ctx *application.Context) {
// app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{HideOnClose: true}).
// SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
// SetPosition(rand.Intn(1000), rand.Intn(800)).
// SetURL("https://wails.io").
// Show()
// windowCounter++
// })
//myMenu.Add("New Frameless WebviewWindow").
// SetAccelerator("CmdOrCtrl+F").
// OnClick(func(ctx *application.Context) {
// app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
// X: rand.Intn(1000),
// Y: rand.Intn(800),
// Frameless: true,
// Mac: application.MacWindow{
// InvisibleTitleBarHeight: 50,
// },
// }).Show()
// windowCounter++
// })
//if runtime.GOOS == "darwin" {
// myMenu.Add("New WebviewWindow (MacTitleBarHiddenInset)").
// OnClick(func(ctx *application.Context) {
// app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
// Mac: application.MacWindow{
// TitleBar: application.MacTitleBarHiddenInset,
// InvisibleTitleBarHeight: 25,
// },
// }).
// SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
// SetPosition(rand.Intn(1000), rand.Intn(800)).
// SetHTML("<br/><br/><p>A MacTitleBarHiddenInset WebviewWindow example</p>").
// Show()
// windowCounter++
// })
// myMenu.Add("New WebviewWindow (MacTitleBarHiddenInsetUnified)").
// OnClick(func(ctx *application.Context) {
// app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
// Mac: application.MacWindow{
// TitleBar: application.MacTitleBarHiddenInsetUnified,
// InvisibleTitleBarHeight: 50,
// },
// }).
// SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
// SetPosition(rand.Intn(1000), rand.Intn(800)).
// SetHTML("<br/><br/><p>A MacTitleBarHiddenInsetUnified WebviewWindow example</p>").
// Show()
// windowCounter++
// })
// myMenu.Add("New WebviewWindow (MacTitleBarHidden)").
// OnClick(func(ctx *application.Context) {
// app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
// Mac: application.MacWindow{
// TitleBar: application.MacTitleBarHidden,
// InvisibleTitleBarHeight: 25,
// },
// }).
// SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
// SetPosition(rand.Intn(1000), rand.Intn(800)).
// SetHTML("<br/><br/><p>A MacTitleBarHidden WebviewWindow example</p>").
// Show()
// windowCounter++
// })
//}
//
//sizeMenu := menu.AddSubmenu("Size")
//sizeMenu.Add("Set Size (800,600)").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetSize(800, 600)
// })
//})
//
//sizeMenu.Add("Set Size (Random)").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetSize(rand.Intn(800)+200, rand.Intn(600)+200)
// })
//})
//sizeMenu.Add("Set Min Size (200,200)").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetMinSize(200, 200)
// })
//})
//sizeMenu.Add("Set Max Size (600,600)").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetFullscreenButtonEnabled(false)
// w.SetMaxSize(600, 600)
// })
//})
//sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// width, height := w.Size()
// app.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show()
// })
//})
//
//sizeMenu.Add("Reset Min Size").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetMinSize(0, 0)
// })
//})
//
//sizeMenu.Add("Reset Max Size").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetMaxSize(0, 0)
// w.SetFullscreenButtonEnabled(true)
// })
//})
//positionMenu := menu.AddSubmenu("Position")
//positionMenu.Add("Set Position (0,0)").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetPosition(0, 0)
// })
//})
//positionMenu.Add("Set Position (Random)").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetPosition(rand.Intn(1000), rand.Intn(800))
// })
//})
//
//positionMenu.Add("Get Position").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// x, y := w.Position()
// app.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
// })
//})
//
//positionMenu.Add("Center").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.Center()
// })
//})
//stateMenu := menu.AddSubmenu("State")
//stateMenu.Add("Minimise (for 2 secs)").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.Minimise()
// time.Sleep(2 * time.Second)
// w.Restore()
// })
//})
//stateMenu.Add("Maximise").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.Maximise()
// })
//})
//stateMenu.Add("Fullscreen").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.Fullscreen()
// })
//})
//stateMenu.Add("UnFullscreen").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.UnFullscreen()
// })
//})
//stateMenu.Add("Restore").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.Restore()
// })
//})
//stateMenu.Add("Hide (for 2 seconds)").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.Hide()
// time.Sleep(2 * time.Second)
// w.Show()
// })
//})
//stateMenu.Add("Always on Top").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetAlwaysOnTop(true)
// })
//})
//stateMenu.Add("Not always on Top").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetAlwaysOnTop(false)
// })
//})
//stateMenu.Add("Google.com").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetURL("https://google.com")
// })
//})
//stateMenu.Add("wails.io").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// w.SetURL("https://wails.io")
// })
//})
//stateMenu.Add("Get Primary Screen").OnClick(func(ctx *application.Context) {
// screen, err := app.GetPrimaryScreen()
// if err != nil {
// app.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
// return
// }
// msg := fmt.Sprintf("Screen: %+v", screen)
// app.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show()
//})
//stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) {
// screens, err := app.GetScreens()
// if err != nil {
// app.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
// return
// }
// for _, screen := range screens {
// msg := fmt.Sprintf("Screen: %+v", screen)
// app.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
// }
//})
//stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) {
// currentWindow(func(w *application.WebviewWindow) {
// screen, err := w.GetScreen()
// if err != nil {
// app.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
// return
// }
// msg := fmt.Sprintf("Screen: %+v", screen)
// app.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
// })
//})
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Mac: application.MacWindow{
DisableShadow: true,
},
})
sizeMenu.Add("Set Size (Random)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetSize(rand.Intn(800)+200, rand.Intn(600)+200)
})
})
sizeMenu.Add("Set Min Size (200,200)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetMinSize(200, 200)
})
})
sizeMenu.Add("Set Max Size (600,600)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetFullscreenButtonEnabled(false)
w.SetMaxSize(600, 600)
})
})
sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
width, height := w.Size()
app.InfoDialog().SetTitle("Current WebviewWindow Size").SetMessage("Width: " + strconv.Itoa(width) + " Height: " + strconv.Itoa(height)).Show()
})
})
sizeMenu.Add("Reset Min Size").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetMinSize(0, 0)
})
})
sizeMenu.Add("Reset Max Size").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetMaxSize(0, 0)
w.SetFullscreenButtonEnabled(true)
})
})
positionMenu := menu.AddSubmenu("Position")
positionMenu.Add("Set Position (0,0)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetPosition(0, 0)
})
})
positionMenu.Add("Set Position (Random)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetPosition(rand.Intn(1000), rand.Intn(800))
})
})
positionMenu.Add("Get Position").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
x, y := w.Position()
app.InfoDialog().SetTitle("Current WebviewWindow Position").SetMessage("X: " + strconv.Itoa(x) + " Y: " + strconv.Itoa(y)).Show()
})
})
positionMenu.Add("Center").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Center()
})
})
stateMenu := menu.AddSubmenu("State")
stateMenu.Add("Minimise (for 2 secs)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Minimise()
time.Sleep(2 * time.Second)
w.Restore()
})
})
stateMenu.Add("Maximise").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Maximise()
})
})
stateMenu.Add("Fullscreen").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Fullscreen()
})
})
stateMenu.Add("UnFullscreen").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.UnFullscreen()
})
})
stateMenu.Add("Restore").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Restore()
})
})
stateMenu.Add("Hide (for 2 seconds)").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.Hide()
time.Sleep(2 * time.Second)
w.Show()
})
})
stateMenu.Add("Always on Top").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetAlwaysOnTop(true)
})
})
stateMenu.Add("Not always on Top").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetAlwaysOnTop(false)
})
})
stateMenu.Add("Google.com").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetURL("https://google.com")
})
})
stateMenu.Add("wails.io").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
w.SetURL("https://wails.io")
})
})
stateMenu.Add("Get Primary Screen").OnClick(func(ctx *application.Context) {
screen, err := app.GetPrimaryScreen()
if err != nil {
app.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
msg := fmt.Sprintf("Screen: %+v", screen)
app.InfoDialog().SetTitle("Primary Screen").SetMessage(msg).Show()
})
stateMenu.Add("Get Screens").OnClick(func(ctx *application.Context) {
screens, err := app.GetScreens()
if err != nil {
app.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
for _, screen := range screens {
msg := fmt.Sprintf("Screen: %+v", screen)
app.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
}
})
stateMenu.Add("Get Screen for WebviewWindow").OnClick(func(ctx *application.Context) {
currentWindow(func(w *application.WebviewWindow) {
screen, err := w.GetScreen()
if err != nil {
app.ErrorDialog().SetTitle("Error").SetMessage(err.Error()).Show()
return
}
msg := fmt.Sprintf("Screen: %+v", screen)
app.InfoDialog().SetTitle(fmt.Sprintf("Screen %s", screen.ID)).SetMessage(msg).Show()
})
})
app.NewWebviewWindow()
app.SetMenu(menu)
//app.SetMenu(menu)
err := app.Run()
if err != nil {

View file

@ -34,7 +34,7 @@ func main() {
newWindow := func() {
windowName := "WebviewWindow " + strconv.Itoa(windowCounter)
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Name: windowName,
}).
SetTitle(windowName).

View file

@ -24,7 +24,7 @@ func main() {
},
})
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Wails ML Demo",
Width: 800,
Height: 600,

View file

@ -3,8 +3,11 @@ module github.com/wailsapp/wails/v3
go 1.19
require (
github.com/bep/debounce v1.2.1
github.com/go-ole/go-ole v1.2.6
github.com/go-task/task/v3 v3.20.0
github.com/google/go-cmp v0.5.9
github.com/google/uuid v1.3.0
github.com/jackmordaunt/icns/v2 v2.2.1
github.com/json-iterator/go v1.1.12
github.com/leaanthony/clir v1.6.0
@ -16,7 +19,9 @@ require (
github.com/pterm/pterm v0.12.51
github.com/samber/lo v1.37.0
github.com/tc-hib/winres v0.1.6
github.com/wailsapp/go-webview2 v1.0.1
github.com/wailsapp/wails/v2 v2.3.2-0.20230117193915-45c3a501d9e6
golang.org/x/sys v0.8.0
modernc.org/sqlite v1.21.0
)
@ -27,8 +32,8 @@ require (
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gookit/color v1.5.2 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/joho/godotenv v1.4.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/leaanthony/slicer v1.5.0 // indirect
@ -53,7 +58,6 @@ require (
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/term v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.1.12 // indirect

View file

@ -12,6 +12,8 @@ github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/
github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
github.com/MarvinJWendt/testza v0.5.1 h1:a9Fqx6vQrHQ4CyiaLhktfTTelwGotmFWy8MNhyaohw8=
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
@ -23,6 +25,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/task/v3 v3.20.0 h1:pTavuhP+AiEpKLzh5I6Lja9Ux7ypYO5QMsEPTbhYEDc=
@ -39,6 +43,8 @@ github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
github.com/jackmordaunt/icns/v2 v2.2.1 h1:MGklwYP2yohKn2Bw7XxlF69LZe98S1vUfl5OvAulPwg=
github.com/jackmordaunt/icns/v2 v2.2.1/go.mod h1:6aYIB9eSzyfHHMKqDf17Xrs1zetQPReAkiUSHzdw4cI=
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/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@ -138,6 +144,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/tc-hib/winres v0.1.6 h1:qgsYHze+BxQPEYilxIz/KCQGaClvI2+yLBAZs+3+0B8=
github.com/tc-hib/winres v0.1.6/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A=
github.com/wailsapp/go-webview2 v1.0.1 h1:dEJIeEApW/MhO2tTMISZBFZPuW7kwrFA1NtgFB1z1II=
github.com/wailsapp/go-webview2 v1.0.1/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
@ -167,8 +175,10 @@ golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -182,8 +192,8 @@ golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

View file

@ -4,6 +4,8 @@ import (
"bytes"
"fmt"
"image"
"image/color"
"image/png"
"os"
"strconv"
"strings"
@ -124,3 +126,39 @@ func generateWindowsIcon(iconData []byte, sizes []int, options *IconsOptions) er
}
return nil
}
func GenerateTemplateIcon(data []byte, outputFilename string) error {
// Decode the input file as a PNG
buffer := bytes.NewBuffer(data)
img, err := png.Decode(buffer)
if err != nil {
return fmt.Errorf("failed to decode input file as PNG: %w", err)
}
// Create a new image with the same dimensions and RGBA color model
bounds := img.Bounds()
iconImg := image.NewRGBA(bounds)
// Iterate over each pixel of the input image
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
// Get the alpha of the pixel
_, _, _, a := img.At(x, y).RGBA()
iconImg.SetRGBA(x, y, color.RGBA{R: 0, G: 0, B: 0, A: uint8(a)})
}
}
// Create the output file
outFile, err := os.Create(outputFilename)
if err != nil {
return fmt.Errorf("failed to create output file: %w", err)
}
defer outFile.Close()
// Encode the template icon image as a PNG and write it to the output file
if err := png.Encode(outFile, iconImg); err != nil {
return fmt.Errorf("failed to encode output image as PNG: %w", err)
}
return nil
}

View file

@ -1,12 +1,12 @@
package debug
import (
"github.com/samber/lo"
"path/filepath"
"runtime"
)
"runtime/debug"
import "runtime/debug"
"github.com/samber/lo"
)
// Why go doesn't provide this as a map already is beyond me.
var buildSettings = map[string]string{}
@ -20,7 +20,7 @@ func init() {
buildSettings = lo.Associate(buildInfo.Settings, func(setting debug.BuildSetting) (string, string) {
return setting.Key, setting.Value
})
if isLocalBuild() {
if isLocalBuild() || buildInfo.Path == "" {
modulePath := RelativePath("..", "..", "..")
LocalModulePath, _ = filepath.Abs(modulePath)
}

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Harry Phillips
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,31 @@
# Common File Dialog bindings for Golang
[Project Home](https://github.com/harry1453/go-common-file-dialog)
This library contains bindings for Windows Vista and
newer's [Common File Dialogs](https://docs.microsoft.com/en-us/windows/win32/shell/common-file-dialog), which is the
standard system dialog for selecting files or folders to open or save.
The Common File Dialogs have to be accessed via
the [COM Interface](https://en.wikipedia.org/wiki/Component_Object_Model), normally via C++ or via bindings (like in C#)
.
This library contains bindings for Golang. **It does not require CGO**, and contains empty stubs for non-windows
platforms (so is safe to compile and run on platforms other than windows, but will just return errors at runtime).
This can be very useful if you want to quickly get a file selector in your Golang application. The `cfdutil` package
contains utility functions with a single call to open and configure a dialog, and then get the result from it. Examples
for this are in [`_examples/usingutil`](_examples/usingutil). Or, if you want finer control over the dialog's operation,
you can use the base package. Examples for this are in [`_examples/notusingutil`](_examples/notusingutil).
This library is available under the MIT license.
Currently supported features:
* Open File Dialog (to open a single file)
* Open Multiple Files Dialog (to open multiple files)
* Open Folder Dialog
* Save File Dialog
* Dialog "roles" to allow Windows to remember different "last locations" for different types of dialog
* Set dialog Title, Default Folder and Initial Folder
* Set dialog File Filters

View file

@ -0,0 +1,72 @@
// Cross-platform.
// Common File Dialogs
package cfd
type Dialog interface {
// Show the dialog to the user.
// Blocks until the user has closed the dialog.
Show() error
// Sets the dialog's parent window. Use 0 to set the dialog to have no parent window.
SetParentWindowHandle(hwnd uintptr)
// Show the dialog to the user.
// Blocks until the user has closed the dialog and returns their selection.
// Returns an error if the user cancelled the dialog.
// Do not use for the Open Multiple Files dialog. Use ShowAndGetResults instead.
ShowAndGetResult() (string, error)
// Sets the title of the dialog window.
SetTitle(title string) error
// Sets the "role" of the dialog. This is used to derive the dialog's GUID, which the
// OS will use to differentiate it from dialogs that are intended for other purposes.
// This means that, for example, a dialog with role "Import" will have a different
// previous location that it will open to than a dialog with role "Open". Can be any string.
SetRole(role string) error
// Sets the folder used as a default if there is not a recently used folder value available
SetDefaultFolder(defaultFolder string) error
// Sets the folder that the dialog always opens to.
// If this is set, it will override the "default folder" behaviour and the dialog will always open to this folder.
SetFolder(folder string) error
// Gets the selected file or folder path, as an absolute path eg. "C:\Folder\file.txt"
// Do not use for the Open Multiple Files dialog. Use GetResults instead.
GetResult() (string, error)
// Sets the file name, I.E. the contents of the file name text box.
// For Select Folder Dialog, sets folder name.
SetFileName(fileName string) error
// Release the resources allocated to this Dialog.
// Should be called when the dialog is finished with.
Release() error
}
type FileDialog interface {
Dialog
// Set the list of file filters that the user can select.
SetFileFilters(fileFilter []FileFilter) error
// Set the selected item from the list of file filters (set using SetFileFilters) by its index. Defaults to 0 (the first item in the list) if not called.
SetSelectedFileFilterIndex(index uint) error
// Sets the default extension applied when a user does not provide one as part of the file name.
// If the user selects a different file filter, the default extension will be automatically updated to match the new file filter.
// For Open / Open Multiple File Dialog, this only has an effect when the user specifies a file name with no extension and a file with the default extension exists.
// For Save File Dialog, this extension will be used whenever a user does not specify an extension.
SetDefaultExtension(defaultExtension string) error
}
type OpenFileDialog interface {
FileDialog
}
type OpenMultipleFilesDialog interface {
FileDialog
// Show the dialog to the user.
// Blocks until the user has closed the dialog and returns the selected files.
ShowAndGetResults() ([]string, error)
// Gets the selected file paths, as absolute paths eg. "C:\Folder\file.txt"
GetResults() ([]string, error)
}
type SelectFolderDialog interface {
Dialog
}
type SaveFileDialog interface { // TODO Properties
FileDialog
}

View file

@ -0,0 +1,28 @@
//go:build !windows
// +build !windows
package cfd
import "fmt"
var unsupportedError = fmt.Errorf("common file dialogs are only available on windows")
// TODO doc
func NewOpenFileDialog(config DialogConfig) (OpenFileDialog, error) {
return nil, unsupportedError
}
// TODO doc
func NewOpenMultipleFilesDialog(config DialogConfig) (OpenMultipleFilesDialog, error) {
return nil, unsupportedError
}
// TODO doc
func NewSelectFolderDialog(config DialogConfig) (SelectFolderDialog, error) {
return nil, unsupportedError
}
// TODO doc
func NewSaveFileDialog(config DialogConfig) (SaveFileDialog, error) {
return nil, unsupportedError
}

View file

@ -0,0 +1,79 @@
//go:build windows
// +build windows
package cfd
import "github.com/go-ole/go-ole"
func initialize() {
// Swallow error
_ = ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_DISABLE_OLE1DDE)
}
// TODO doc
func NewOpenFileDialog(config DialogConfig) (OpenFileDialog, error) {
initialize()
openDialog, err := newIFileOpenDialog()
if err != nil {
return nil, err
}
err = config.apply(openDialog)
if err != nil {
return nil, err
}
return openDialog, nil
}
// TODO doc
func NewOpenMultipleFilesDialog(config DialogConfig) (OpenMultipleFilesDialog, error) {
initialize()
openDialog, err := newIFileOpenDialog()
if err != nil {
return nil, err
}
err = config.apply(openDialog)
if err != nil {
return nil, err
}
err = openDialog.setIsMultiselect(true)
if err != nil {
return nil, err
}
return openDialog, nil
}
// TODO doc
func NewSelectFolderDialog(config DialogConfig) (SelectFolderDialog, error) {
initialize()
openDialog, err := newIFileOpenDialog()
if err != nil {
return nil, err
}
err = config.apply(openDialog)
if err != nil {
return nil, err
}
err = openDialog.setPickFolders(true)
if err != nil {
return nil, err
}
return openDialog, nil
}
// TODO doc
func NewSaveFileDialog(config DialogConfig) (SaveFileDialog, error) {
initialize()
saveDialog, err := newIFileSaveDialog()
if err != nil {
return nil, err
}
err = config.apply(saveDialog)
if err != nil {
return nil, err
}
return saveDialog, nil
}

View file

@ -0,0 +1,120 @@
// Cross-platform.
package cfd
type FileFilter struct {
// The display name of the filter (That is shown to the user)
DisplayName string
// The filter pattern. Eg. "*.txt;*.png" to select all txt and png files, "*.*" to select any files, etc.
Pattern string
}
type DialogConfig struct {
// The title of the dialog
Title string
// The role of the dialog. This is used to derive the dialog's GUID, which the
// OS will use to differentiate it from dialogs that are intended for other purposes.
// This means that, for example, a dialog with role "Import" will have a different
// previous location that it will open to than a dialog with role "Open". Can be any string.
Role string
// The default folder - the folder that is used the first time the user opens it
// (after the first time their last used location is used).
DefaultFolder string
// The initial folder - the folder that the dialog always opens to if not empty.
// If this is not empty, it will override the "default folder" behaviour and
// the dialog will always open to this folder.
Folder string
// The file filters that restrict which types of files the dialog is able to choose.
// Ignored by Select Folder Dialog.
FileFilters []FileFilter
// Sets the initially selected file filter. This is an index of FileFilters.
// Ignored by Select Folder Dialog.
SelectedFileFilterIndex uint
// The initial name of the file (I.E. the text in the file name text box) when the user opens the dialog.
// For the Select Folder Dialog, this sets the initial folder name.
FileName string
// The default extension applied when a user does not provide one as part of the file name.
// If the user selects a different file filter, the default extension will be automatically updated to match the new file filter.
// For Open / Open Multiple File Dialog, this only has an effect when the user specifies a file name with no extension and a file with the default extension exists.
// For Save File Dialog, this extension will be used whenever a user does not specify an extension.
// Ignored by Select Folder Dialog.
DefaultExtension string
// ParentWindowHandle is the handle (HWND) to the parent window of the dialog.
// If left as 0 / nil, the dialog will have no parent window.
ParentWindowHandle uintptr
}
var defaultFilters = []FileFilter{
{
DisplayName: "All Files (*.*)",
Pattern: "*.*",
},
}
func (config *DialogConfig) apply(dialog Dialog) (err error) {
if config.Title != "" {
err = dialog.SetTitle(config.Title)
if err != nil {
return
}
}
if config.Role != "" {
err = dialog.SetRole(config.Role)
if err != nil {
return
}
}
if config.Folder != "" {
err = dialog.SetFolder(config.Folder)
if err != nil {
return
}
}
if config.DefaultFolder != "" {
err = dialog.SetDefaultFolder(config.DefaultFolder)
if err != nil {
return
}
}
if config.FileName != "" {
err = dialog.SetFileName(config.FileName)
if err != nil {
return
}
}
dialog.SetParentWindowHandle(config.ParentWindowHandle)
if dialog, ok := dialog.(FileDialog); ok {
var fileFilters []FileFilter
if config.FileFilters != nil && len(config.FileFilters) > 0 {
fileFilters = config.FileFilters
} else {
fileFilters = defaultFilters
}
err = dialog.SetFileFilters(fileFilters)
if err != nil {
return
}
if config.SelectedFileFilterIndex != 0 {
err = dialog.SetSelectedFileFilterIndex(config.SelectedFileFilterIndex)
if err != nil {
return
}
}
if config.DefaultExtension != "" {
err = dialog.SetDefaultExtension(config.DefaultExtension)
if err != nil {
return
}
}
}
return
}

View file

@ -0,0 +1,7 @@
package cfd
import "errors"
var (
ErrorCancelled = errors.New("cancelled by user")
)

View file

@ -0,0 +1,201 @@
//go:build windows
// +build windows
package cfd
import (
"github.com/go-ole/go-ole"
"github.com/google/uuid"
"syscall"
"unsafe"
)
var (
fileOpenDialogCLSID = ole.NewGUID("{DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7}")
fileOpenDialogIID = ole.NewGUID("{d57c7288-d4ad-4768-be02-9d969532d960}")
)
type iFileOpenDialog struct {
vtbl *iFileOpenDialogVtbl
parentWindowHandle uintptr
}
type iFileOpenDialogVtbl struct {
iFileDialogVtbl
GetResults uintptr // func (ppenum **IShellItemArray) HRESULT
GetSelectedItems uintptr
}
func newIFileOpenDialog() (*iFileOpenDialog, error) {
if unknown, err := ole.CreateInstance(fileOpenDialogCLSID, fileOpenDialogIID); err == nil {
return (*iFileOpenDialog)(unsafe.Pointer(unknown)), nil
} else {
return nil, err
}
}
func (fileOpenDialog *iFileOpenDialog) Show() error {
return fileOpenDialog.vtbl.show(unsafe.Pointer(fileOpenDialog), fileOpenDialog.parentWindowHandle)
}
func (fileOpenDialog *iFileOpenDialog) SetParentWindowHandle(hwnd uintptr) {
fileOpenDialog.parentWindowHandle = hwnd
}
func (fileOpenDialog *iFileOpenDialog) ShowAndGetResult() (string, error) {
isMultiselect, err := fileOpenDialog.isMultiselect()
if err != nil {
return "", err
}
if isMultiselect {
// We should panic as this error is caused by the developer using the library
panic("use ShowAndGetResults for open multiple files dialog")
}
if err := fileOpenDialog.Show(); err != nil {
return "", err
}
return fileOpenDialog.GetResult()
}
func (fileOpenDialog *iFileOpenDialog) ShowAndGetResults() ([]string, error) {
isMultiselect, err := fileOpenDialog.isMultiselect()
if err != nil {
return nil, err
}
if !isMultiselect {
// We should panic as this error is caused by the developer using the library
panic("use ShowAndGetResult for open single file dialog")
}
if err := fileOpenDialog.Show(); err != nil {
return nil, err
}
return fileOpenDialog.GetResults()
}
func (fileOpenDialog *iFileOpenDialog) SetTitle(title string) error {
return fileOpenDialog.vtbl.setTitle(unsafe.Pointer(fileOpenDialog), title)
}
func (fileOpenDialog *iFileOpenDialog) GetResult() (string, error) {
isMultiselect, err := fileOpenDialog.isMultiselect()
if err != nil {
return "", err
}
if isMultiselect {
// We should panic as this error is caused by the developer using the library
panic("use GetResults for open multiple files dialog")
}
return fileOpenDialog.vtbl.getResultString(unsafe.Pointer(fileOpenDialog))
}
func (fileOpenDialog *iFileOpenDialog) Release() error {
return fileOpenDialog.vtbl.release(unsafe.Pointer(fileOpenDialog))
}
func (fileOpenDialog *iFileOpenDialog) SetDefaultFolder(defaultFolderPath string) error {
return fileOpenDialog.vtbl.setDefaultFolder(unsafe.Pointer(fileOpenDialog), defaultFolderPath)
}
func (fileOpenDialog *iFileOpenDialog) SetFolder(defaultFolderPath string) error {
return fileOpenDialog.vtbl.setFolder(unsafe.Pointer(fileOpenDialog), defaultFolderPath)
}
func (fileOpenDialog *iFileOpenDialog) SetFileFilters(filter []FileFilter) error {
return fileOpenDialog.vtbl.setFileTypes(unsafe.Pointer(fileOpenDialog), filter)
}
func (fileOpenDialog *iFileOpenDialog) SetRole(role string) error {
return fileOpenDialog.vtbl.setClientGuid(unsafe.Pointer(fileOpenDialog), StringToUUID(role))
}
// This should only be callable when the user asks for a multi select because
// otherwise they will be given the Dialog interface which does not expose this function.
func (fileOpenDialog *iFileOpenDialog) GetResults() ([]string, error) {
isMultiselect, err := fileOpenDialog.isMultiselect()
if err != nil {
return nil, err
}
if !isMultiselect {
// We should panic as this error is caused by the developer using the library
panic("use GetResult for open single file dialog")
}
return fileOpenDialog.vtbl.getResultsStrings(unsafe.Pointer(fileOpenDialog))
}
func (fileOpenDialog *iFileOpenDialog) SetDefaultExtension(defaultExtension string) error {
return fileOpenDialog.vtbl.setDefaultExtension(unsafe.Pointer(fileOpenDialog), defaultExtension)
}
func (fileOpenDialog *iFileOpenDialog) SetFileName(initialFileName string) error {
return fileOpenDialog.vtbl.setFileName(unsafe.Pointer(fileOpenDialog), initialFileName)
}
func (fileOpenDialog *iFileOpenDialog) SetSelectedFileFilterIndex(index uint) error {
return fileOpenDialog.vtbl.setSelectedFileFilterIndex(unsafe.Pointer(fileOpenDialog), index)
}
func (fileOpenDialog *iFileOpenDialog) setPickFolders(pickFolders bool) error {
const FosPickfolders = 0x20
if pickFolders {
return fileOpenDialog.vtbl.addOption(unsafe.Pointer(fileOpenDialog), FosPickfolders)
} else {
return fileOpenDialog.vtbl.removeOption(unsafe.Pointer(fileOpenDialog), FosPickfolders)
}
}
const FosAllowMultiselect = 0x200
func (fileOpenDialog *iFileOpenDialog) isMultiselect() (bool, error) {
options, err := fileOpenDialog.vtbl.getOptions(unsafe.Pointer(fileOpenDialog))
if err != nil {
return false, err
}
return options&FosAllowMultiselect != 0, nil
}
func (fileOpenDialog *iFileOpenDialog) setIsMultiselect(isMultiselect bool) error {
if isMultiselect {
return fileOpenDialog.vtbl.addOption(unsafe.Pointer(fileOpenDialog), FosAllowMultiselect)
} else {
return fileOpenDialog.vtbl.removeOption(unsafe.Pointer(fileOpenDialog), FosAllowMultiselect)
}
}
func (vtbl *iFileOpenDialogVtbl) getResults(objPtr unsafe.Pointer) (*iShellItemArray, error) {
var shellItemArray *iShellItemArray
ret, _, _ := syscall.Syscall(vtbl.GetResults,
1,
uintptr(objPtr),
uintptr(unsafe.Pointer(&shellItemArray)),
0)
return shellItemArray, hresultToError(ret)
}
func (vtbl *iFileOpenDialogVtbl) getResultsStrings(objPtr unsafe.Pointer) ([]string, error) {
shellItemArray, err := vtbl.getResults(objPtr)
if err != nil {
return nil, err
}
if shellItemArray == nil {
return nil, ErrorCancelled
}
defer shellItemArray.vtbl.release(unsafe.Pointer(shellItemArray))
count, err := shellItemArray.vtbl.getCount(unsafe.Pointer(shellItemArray))
if err != nil {
return nil, err
}
var results []string
for i := uintptr(0); i < count; i++ {
newItem, err := shellItemArray.vtbl.getItemAt(unsafe.Pointer(shellItemArray), i)
if err != nil {
return nil, err
}
results = append(results, newItem)
}
return results, nil
}
func StringToUUID(str string) *ole.GUID {
return ole.NewGUID(uuid.NewSHA1(uuid.Nil, []byte(str)).String())
}

View file

@ -0,0 +1,92 @@
//go:build windows
// +build windows
package cfd
import (
"github.com/go-ole/go-ole"
"unsafe"
)
var (
saveFileDialogCLSID = ole.NewGUID("{C0B4E2F3-BA21-4773-8DBA-335EC946EB8B}")
saveFileDialogIID = ole.NewGUID("{84bccd23-5fde-4cdb-aea4-af64b83d78ab}")
)
type iFileSaveDialog struct {
vtbl *iFileSaveDialogVtbl
parentWindowHandle uintptr
}
type iFileSaveDialogVtbl struct {
iFileDialogVtbl
SetSaveAsItem uintptr
SetProperties uintptr
SetCollectedProperties uintptr
GetProperties uintptr
ApplyProperties uintptr
}
func newIFileSaveDialog() (*iFileSaveDialog, error) {
if unknown, err := ole.CreateInstance(saveFileDialogCLSID, saveFileDialogIID); err == nil {
return (*iFileSaveDialog)(unsafe.Pointer(unknown)), nil
} else {
return nil, err
}
}
func (fileSaveDialog *iFileSaveDialog) Show() error {
return fileSaveDialog.vtbl.show(unsafe.Pointer(fileSaveDialog), fileSaveDialog.parentWindowHandle)
}
func (fileSaveDialog *iFileSaveDialog) SetParentWindowHandle(hwnd uintptr) {
fileSaveDialog.parentWindowHandle = hwnd
}
func (fileSaveDialog *iFileSaveDialog) ShowAndGetResult() (string, error) {
if err := fileSaveDialog.Show(); err != nil {
return "", err
}
return fileSaveDialog.GetResult()
}
func (fileSaveDialog *iFileSaveDialog) SetTitle(title string) error {
return fileSaveDialog.vtbl.setTitle(unsafe.Pointer(fileSaveDialog), title)
}
func (fileSaveDialog *iFileSaveDialog) GetResult() (string, error) {
return fileSaveDialog.vtbl.getResultString(unsafe.Pointer(fileSaveDialog))
}
func (fileSaveDialog *iFileSaveDialog) Release() error {
return fileSaveDialog.vtbl.release(unsafe.Pointer(fileSaveDialog))
}
func (fileSaveDialog *iFileSaveDialog) SetDefaultFolder(defaultFolderPath string) error {
return fileSaveDialog.vtbl.setDefaultFolder(unsafe.Pointer(fileSaveDialog), defaultFolderPath)
}
func (fileSaveDialog *iFileSaveDialog) SetFolder(defaultFolderPath string) error {
return fileSaveDialog.vtbl.setFolder(unsafe.Pointer(fileSaveDialog), defaultFolderPath)
}
func (fileSaveDialog *iFileSaveDialog) SetFileFilters(filter []FileFilter) error {
return fileSaveDialog.vtbl.setFileTypes(unsafe.Pointer(fileSaveDialog), filter)
}
func (fileSaveDialog *iFileSaveDialog) SetRole(role string) error {
return fileSaveDialog.vtbl.setClientGuid(unsafe.Pointer(fileSaveDialog), StringToUUID(role))
}
func (fileSaveDialog *iFileSaveDialog) SetDefaultExtension(defaultExtension string) error {
return fileSaveDialog.vtbl.setDefaultExtension(unsafe.Pointer(fileSaveDialog), defaultExtension)
}
func (fileSaveDialog *iFileSaveDialog) SetFileName(initialFileName string) error {
return fileSaveDialog.vtbl.setFileName(unsafe.Pointer(fileSaveDialog), initialFileName)
}
func (fileSaveDialog *iFileSaveDialog) SetSelectedFileFilterIndex(index uint) error {
return fileSaveDialog.vtbl.setSelectedFileFilterIndex(unsafe.Pointer(fileSaveDialog), index)
}

View file

@ -0,0 +1,53 @@
//go:build windows
// +build windows
package cfd
import (
"github.com/go-ole/go-ole"
"syscall"
"unsafe"
)
var (
procSHCreateItemFromParsingName = syscall.NewLazyDLL("Shell32.dll").NewProc("SHCreateItemFromParsingName")
iidShellItem = ole.NewGUID("43826d1e-e718-42ee-bc55-a1e261c37bfe")
)
type iShellItem struct {
vtbl *iShellItemVtbl
}
type iShellItemVtbl struct {
iUnknownVtbl
BindToHandler uintptr
GetParent uintptr
GetDisplayName uintptr // func (sigdnName SIGDN, ppszName *LPWSTR) HRESULT
GetAttributes uintptr
Compare uintptr
}
func newIShellItem(path string) (*iShellItem, error) {
var shellItem *iShellItem
pathPtr := ole.SysAllocString(path)
ret, _, _ := procSHCreateItemFromParsingName.Call(
uintptr(unsafe.Pointer(pathPtr)),
0,
uintptr(unsafe.Pointer(iidShellItem)),
uintptr(unsafe.Pointer(&shellItem)))
return shellItem, hresultToError(ret)
}
func (vtbl *iShellItemVtbl) getDisplayName(objPtr unsafe.Pointer) (string, error) {
var ptr *uint16
ret, _, _ := syscall.Syscall(vtbl.GetDisplayName,
2,
uintptr(objPtr),
0x80058000, // SIGDN_FILESYSPATH
uintptr(unsafe.Pointer(&ptr)))
if err := hresultToError(ret); err != nil {
return "", err
}
defer ole.CoTaskMemFree(uintptr(unsafe.Pointer(ptr)))
return ole.LpOleStrToString(ptr), nil
}

View file

@ -0,0 +1,67 @@
//go:build windows
// +build windows
package cfd
import (
"github.com/go-ole/go-ole"
"syscall"
"unsafe"
)
const (
iidShellItemArrayGUID = "{b63ea76d-1f85-456f-a19c-48159efa858b}"
)
var (
iidShellItemArray *ole.GUID
)
func init() {
iidShellItemArray, _ = ole.IIDFromString(iidShellItemArrayGUID)
}
type iShellItemArray struct {
vtbl *iShellItemArrayVtbl
}
type iShellItemArrayVtbl struct {
iUnknownVtbl
BindToHandler uintptr
GetPropertyStore uintptr
GetPropertyDescriptionList uintptr
GetAttributes uintptr
GetCount uintptr // func (pdwNumItems *DWORD) HRESULT
GetItemAt uintptr // func (dwIndex DWORD, ppsi **IShellItem) HRESULT
EnumItems uintptr
}
func (vtbl *iShellItemArrayVtbl) getCount(objPtr unsafe.Pointer) (uintptr, error) {
var count uintptr
ret, _, _ := syscall.Syscall(vtbl.GetCount,
1,
uintptr(objPtr),
uintptr(unsafe.Pointer(&count)),
0)
if err := hresultToError(ret); err != nil {
return 0, err
}
return count, nil
}
func (vtbl *iShellItemArrayVtbl) getItemAt(objPtr unsafe.Pointer, index uintptr) (string, error) {
var shellItem *iShellItem
ret, _, _ := syscall.Syscall(vtbl.GetItemAt,
2,
uintptr(objPtr),
index,
uintptr(unsafe.Pointer(&shellItem)))
if err := hresultToError(ret); err != nil {
return "", err
}
if shellItem == nil {
return "", ErrorCancelled
}
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
return shellItem.vtbl.getDisplayName(unsafe.Pointer(shellItem))
}

View file

@ -0,0 +1,48 @@
//go:build windows
// +build windows
package cfd
type comDlgFilterSpec struct {
pszName *int16
pszSpec *int16
}
type iUnknownVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
}
type iModalWindowVtbl struct {
iUnknownVtbl
Show uintptr // func (hwndOwner HWND) HRESULT
}
type iFileDialogVtbl struct {
iModalWindowVtbl
SetFileTypes uintptr // func (cFileTypes UINT, rgFilterSpec *COMDLG_FILTERSPEC) HRESULT
SetFileTypeIndex uintptr // func(iFileType UINT) HRESULT
GetFileTypeIndex uintptr
Advise uintptr
Unadvise uintptr
SetOptions uintptr // func (fos FILEOPENDIALOGOPTIONS) HRESULT
GetOptions uintptr // func (pfos *FILEOPENDIALOGOPTIONS) HRESULT
SetDefaultFolder uintptr // func (psi *IShellItem) HRESULT
SetFolder uintptr // func (psi *IShellItem) HRESULT
GetFolder uintptr
GetCurrentSelection uintptr
SetFileName uintptr // func (pszName LPCWSTR) HRESULT
GetFileName uintptr
SetTitle uintptr // func(pszTitle LPCWSTR) HRESULT
SetOkButtonLabel uintptr
SetFileNameLabel uintptr
GetResult uintptr // func (ppsi **IShellItem) HRESULT
AddPlace uintptr
SetDefaultExtension uintptr // func (pszDefaultExtension LPCWSTR) HRESULT
// This can only be used from a callback.
Close uintptr
SetClientGuid uintptr // func (guid REFGUID) HRESULT
ClearClientData uintptr
SetFilter uintptr
}

View file

@ -0,0 +1,227 @@
//go:build windows
// +build windows
package cfd
import (
"fmt"
"github.com/go-ole/go-ole"
"strings"
"syscall"
"unsafe"
)
func hresultToError(hr uintptr) error {
if hr < 0 {
return ole.NewError(hr)
}
return nil
}
func (vtbl *iUnknownVtbl) release(objPtr unsafe.Pointer) error {
ret, _, _ := syscall.Syscall(vtbl.Release,
0,
uintptr(objPtr),
0,
0)
return hresultToError(ret)
}
func (vtbl *iModalWindowVtbl) show(objPtr unsafe.Pointer, hwnd uintptr) error {
ret, _, _ := syscall.Syscall(vtbl.Show,
1,
uintptr(objPtr),
hwnd,
0)
return hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) setFileTypes(objPtr unsafe.Pointer, filters []FileFilter) error {
cFileTypes := len(filters)
if cFileTypes < 0 {
return fmt.Errorf("must specify at least one filter")
}
comDlgFilterSpecs := make([]comDlgFilterSpec, cFileTypes)
for i := 0; i < cFileTypes; i++ {
filter := &filters[i]
comDlgFilterSpecs[i] = comDlgFilterSpec{
pszName: ole.SysAllocString(filter.DisplayName),
pszSpec: ole.SysAllocString(filter.Pattern),
}
}
ret, _, _ := syscall.Syscall(vtbl.SetFileTypes,
2,
uintptr(objPtr),
uintptr(cFileTypes),
uintptr(unsafe.Pointer(&comDlgFilterSpecs[0])))
return hresultToError(ret)
}
// Options are:
// FOS_OVERWRITEPROMPT = 0x2,
// FOS_STRICTFILETYPES = 0x4,
// FOS_NOCHANGEDIR = 0x8,
// FOS_PICKFOLDERS = 0x20,
// FOS_FORCEFILESYSTEM = 0x40,
// FOS_ALLNONSTORAGEITEMS = 0x80,
// FOS_NOVALIDATE = 0x100,
// FOS_ALLOWMULTISELECT = 0x200,
// FOS_PATHMUSTEXIST = 0x800,
// FOS_FILEMUSTEXIST = 0x1000,
// FOS_CREATEPROMPT = 0x2000,
// FOS_SHAREAWARE = 0x4000,
// FOS_NOREADONLYRETURN = 0x8000,
// FOS_NOTESTFILECREATE = 0x10000,
// FOS_HIDEMRUPLACES = 0x20000,
// FOS_HIDEPINNEDPLACES = 0x40000,
// FOS_NODEREFERENCELINKS = 0x100000,
// FOS_OKBUTTONNEEDSINTERACTION = 0x200000,
// FOS_DONTADDTORECENT = 0x2000000,
// FOS_FORCESHOWHIDDEN = 0x10000000,
// FOS_DEFAULTNOMINIMODE = 0x20000000,
// FOS_FORCEPREVIEWPANEON = 0x40000000,
// FOS_SUPPORTSTREAMABLEITEMS = 0x80000000
func (vtbl *iFileDialogVtbl) setOptions(objPtr unsafe.Pointer, options uint32) error {
ret, _, _ := syscall.Syscall(vtbl.SetOptions,
1,
uintptr(objPtr),
uintptr(options),
0)
return hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) getOptions(objPtr unsafe.Pointer) (uint32, error) {
var options uint32
ret, _, _ := syscall.Syscall(vtbl.GetOptions,
1,
uintptr(objPtr),
uintptr(unsafe.Pointer(&options)),
0)
return options, hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) addOption(objPtr unsafe.Pointer, option uint32) error {
if options, err := vtbl.getOptions(objPtr); err == nil {
return vtbl.setOptions(objPtr, options|option)
} else {
return err
}
}
func (vtbl *iFileDialogVtbl) removeOption(objPtr unsafe.Pointer, option uint32) error {
if options, err := vtbl.getOptions(objPtr); err == nil {
return vtbl.setOptions(objPtr, options&^option)
} else {
return err
}
}
func (vtbl *iFileDialogVtbl) setDefaultFolder(objPtr unsafe.Pointer, path string) error {
shellItem, err := newIShellItem(path)
if err != nil {
return err
}
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
ret, _, _ := syscall.Syscall(vtbl.SetDefaultFolder,
1,
uintptr(objPtr),
uintptr(unsafe.Pointer(shellItem)),
0)
return hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) setFolder(objPtr unsafe.Pointer, path string) error {
shellItem, err := newIShellItem(path)
if err != nil {
return err
}
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
ret, _, _ := syscall.Syscall(vtbl.SetFolder,
1,
uintptr(objPtr),
uintptr(unsafe.Pointer(shellItem)),
0)
return hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) setTitle(objPtr unsafe.Pointer, title string) error {
titlePtr := ole.SysAllocString(title)
ret, _, _ := syscall.Syscall(vtbl.SetTitle,
1,
uintptr(objPtr),
uintptr(unsafe.Pointer(titlePtr)),
0)
return hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) close(objPtr unsafe.Pointer) error {
ret, _, _ := syscall.Syscall(vtbl.Close,
1,
uintptr(objPtr),
0,
0)
return hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) getResult(objPtr unsafe.Pointer) (*iShellItem, error) {
var shellItem *iShellItem
ret, _, _ := syscall.Syscall(vtbl.GetResult,
1,
uintptr(objPtr),
uintptr(unsafe.Pointer(&shellItem)),
0)
return shellItem, hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) getResultString(objPtr unsafe.Pointer) (string, error) {
shellItem, err := vtbl.getResult(objPtr)
if err != nil {
return "", err
}
if shellItem == nil {
return "", ErrorCancelled
}
defer shellItem.vtbl.release(unsafe.Pointer(shellItem))
return shellItem.vtbl.getDisplayName(unsafe.Pointer(shellItem))
}
func (vtbl *iFileDialogVtbl) setClientGuid(objPtr unsafe.Pointer, guid *ole.GUID) error {
ret, _, _ := syscall.Syscall(vtbl.SetClientGuid,
1,
uintptr(objPtr),
uintptr(unsafe.Pointer(guid)),
0)
return hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) setDefaultExtension(objPtr unsafe.Pointer, defaultExtension string) error {
if defaultExtension[0] == '.' {
defaultExtension = strings.TrimPrefix(defaultExtension, ".")
}
defaultExtensionPtr := ole.SysAllocString(defaultExtension)
ret, _, _ := syscall.Syscall(vtbl.SetDefaultExtension,
1,
uintptr(objPtr),
uintptr(unsafe.Pointer(defaultExtensionPtr)),
0)
return hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) setFileName(objPtr unsafe.Pointer, fileName string) error {
fileNamePtr := ole.SysAllocString(fileName)
ret, _, _ := syscall.Syscall(vtbl.SetFileName,
1,
uintptr(objPtr),
uintptr(unsafe.Pointer(fileNamePtr)),
0)
return hresultToError(ret)
}
func (vtbl *iFileDialogVtbl) setSelectedFileFilterIndex(objPtr unsafe.Pointer, index uint) error {
ret, _, _ := syscall.Syscall(vtbl.SetFileTypeIndex,
1,
uintptr(objPtr),
uintptr(index+1), // SetFileTypeIndex counts from 1
0)
return hresultToError(ret)
}

View file

@ -0,0 +1,45 @@
package cfdutil
import (
"github.com/wailsapp/wails/v3/internal/go-common-file-dialog/cfd"
)
// TODO doc
func ShowOpenFileDialog(config cfd.DialogConfig) (string, error) {
dialog, err := cfd.NewOpenFileDialog(config)
if err != nil {
return "", err
}
defer dialog.Release()
return dialog.ShowAndGetResult()
}
// TODO doc
func ShowOpenMultipleFilesDialog(config cfd.DialogConfig) ([]string, error) {
dialog, err := cfd.NewOpenMultipleFilesDialog(config)
if err != nil {
return nil, err
}
defer dialog.Release()
return dialog.ShowAndGetResults()
}
// TODO doc
func ShowPickFolderDialog(config cfd.DialogConfig) (string, error) {
dialog, err := cfd.NewSelectFolderDialog(config)
if err != nil {
return "", err
}
defer dialog.Release()
return dialog.ShowAndGetResult()
}
// TODO doc
func ShowSaveFileDialog(config cfd.DialogConfig) (string, error) {
dialog, err := cfd.NewSaveFileDialog(config)
if err != nil {
return "", err
}
defer dialog.Release()
return dialog.ShowAndGetResult()
}

View file

@ -0,0 +1,10 @@
package util
import (
"github.com/go-ole/go-ole"
"github.com/google/uuid"
)
func StringToUUID(str string) *ole.GUID {
return ole.NewGUID(uuid.NewSHA1(uuid.Nil, []byte(str)).String())
}

View file

@ -0,0 +1,14 @@
package util
import (
"github.com/go-ole/go-ole"
"testing"
)
func TestStringToUUID(t *testing.T) {
generated := *StringToUUID("TestTestTest")
expected := *ole.NewGUID("7933985F-2C87-5A5B-A26E-5D0326829AC2")
if generated != expected {
t.Errorf("not equal. expected %s, found %s", expected.String(), generated.String())
}
}

2
v3/internal/runtime/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules
.task

View file

@ -0,0 +1,108 @@
# https://taskfile.dev
version: "3"
tasks:
install-deps:
internal: true
sources:
- package.json
cmds:
- npm install
test:
cmds:
- npx vitest run
update:
cmds:
- npx npm-check-updates -u
build:debug:
internal: true
cmds:
- npx esbuild desktop/main.js --bundle --tree-shaking=true --sourcemap=inline --outfile=runtime_debug_desktop_{{.PLATFORM}}.js --define:DEBUG=true --define:WINDOWS={{.WINDOWS}} --define:DARWIN={{.DARWIN}} --define:LINUX={{.LINUX}} --define:PLATFORM={{.PLATFORM}} --define:INVOKE={{.INVOKE}}
build:debug:windows:
cmds:
- task: build:debug
vars:
WINDOWS: true
DARWIN: false
LINUX: false
PLATFORM: windows
INVOKE: "chrome.webview.postMessage"
build:debug:linux:
cmds:
- task: build:debug
vars:
WINDOWS: false
DARWIN: false
LINUX: true
PLATFORM: linux
INVOKE: "webkit.messageHandlers.external.postMessage"
build:debug:darwin:
cmds:
- task: build:debug
vars:
WINDOWS: false
DARWIN: true
LINUX: false
PLATFORM: darwin
INVOKE: "webkit.messageHandlers.external.postMessage"
build:production:
internal: true
cmds:
- npx esbuild desktop/main.js --bundle --tree-shaking=true --minify --outfile=runtime_production_desktop_{{.PLATFORM}}.js --define:DEBUG=true --define:WINDOWS={{.WINDOWS}} --define:DARWIN={{.DARWIN}} --define:LINUX={{.LINUX}} --define:PLATFORM={{.PLATFORM}} --define:INVOKE={{.INVOKE}}
build:production:windows:
cmds:
- task: build:production
vars:
WINDOWS: true
DARWIN: false
LINUX: false
PLATFORM: windows
INVOKE: "chrome.webview.postMessage"
build:production:linux:
cmds:
- task: build:production
vars:
WINDOWS: false
DARWIN: false
LINUX: true
PLATFORM: linux
INVOKE: "webkit.messageHandlers.external.postMessage"
build:production:darwin:
cmds:
- task: build:production
vars:
WINDOWS: false
DARWIN: true
LINUX: false
PLATFORM: darwin
INVOKE: "webkit.messageHandlers.external.postMessage"
build:all:
internal: true
deps:
- build:debug:windows
- build:debug:linux
- build:debug:darwin
- build:production:windows
- build:production:linux
- build:production:darwin
cmds:
- cmd: echo "Build Complete."
build:
deps:
- install-deps
cmds:
- task: build:all

View file

@ -1,3 +1,3 @@
# README
After updating any files in this directory, you must run `wails task build-runtime` to regenerate the compiled JS.
After updating any files in this directory, you must run `wails task build:runtime` to regenerate the compiled JS.

View file

@ -0,0 +1,69 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 9 */
import {invoke} from "./invoke";
let shouldDrag = false;
export function dragTest(e) {
let val = window.getComputedStyle(e.target).getPropertyValue("--wails-draggable");
if (val) {
val = val.trim();
}
if (val !== "drag") {
return false;
}
// Only process the primary button
if (e.buttons !== 1) {
return false;
}
return e.detail === 1;
}
export function setupDrag() {
window.addEventListener('mousedown', onMouseDown);
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', onMouseUp);
}
function onMouseDown(e) {
if (dragTest(e)) {
// Ignore drag on scrollbars
if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight) {
return;
}
shouldDrag = true;
} else {
shouldDrag = false;
}
}
function onMouseUp(e) {
document.body.style.cursor = window.wails.previousCursor || 'auto';
shouldDrag = false;
}
function onMouseMove(e) {
if (shouldDrag) {
shouldDrag = false;
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
if (mousePressed > 0) {
window.wails.previousCursor = document.body.style.cursor;
document.body.style.cursor = 'grab';
invoke("drag");
return;
}
}
}

View file

@ -0,0 +1,14 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 9 */
// defined in the Taskfile
export let invoke = (WINDOWS?chrome.webview.postMessage:window.webkit.messageHandlers.external.postMessage);

View file

@ -20,6 +20,7 @@ import {dispatchWailsEvent, Emit, Off, OffAll, On, Once, OnMultiple} from "./eve
import {dialogCallback, dialogErrorCallback, Error, Info, OpenFile, Question, SaveFile, Warning,} from "./dialogs";
import {enableContextMenus} from "./contextmenu";
import {reloadWML} from "./wml";
import {setupDrag} from "./drag";
window.wails = {
...newRuntime(null),
@ -78,6 +79,8 @@ if (DEBUG) {
enableContextMenus(true);
setupDrag();
document.addEventListener("DOMContentLoaded", function(event) {
reloadWML();
});

View file

@ -9,7 +9,7 @@
"version": "3.0.0",
"license": "ISC",
"devDependencies": {
"esbuild": "^0.17.5",
"esbuild": "^0.17.19",
"happy-dom": "^8.1.5",
"nanoid": "^4.0.0",
"npm-check-updates": "^16.6.3",
@ -18,9 +18,9 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.5.tgz",
"integrity": "sha512-crmPUzgCmF+qZXfl1YkiFoUta2XAfixR1tEnr/gXIixE+WL8Z0BGqfydP5oox0EUOgQMMRgtATtakyAcClQVqQ==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
"integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
"cpu": [
"arm"
],
@ -34,9 +34,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.5.tgz",
"integrity": "sha512-KHWkDqYAMmKZjY4RAN1PR96q6UOtfkWlTS8uEwWxdLtkRt/0F/csUhXIrVfaSIFxnscIBMPynGfhsMwQDRIBQw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
"integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
"cpu": [
"arm64"
],
@ -50,9 +50,9 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.5.tgz",
"integrity": "sha512-8fI/AnIdmWz/+1iza2WrCw8kwXK9wZp/yZY/iS8ioC+U37yJCeppi9EHY05ewJKN64ASoBIseufZROtcFnX5GA==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
"integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
"cpu": [
"x64"
],
@ -66,9 +66,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.5.tgz",
"integrity": "sha512-EAvaoyIySV6Iif3NQCglUNpnMfHSUgC5ugt2efl3+QDntucJe5spn0udNZjTgNi6tKVqSceOw9tQ32liNZc1Xw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
"integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
"cpu": [
"arm64"
],
@ -82,9 +82,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.5.tgz",
"integrity": "sha512-ha7QCJh1fuSwwCgoegfdaljowwWozwTDjBgjD3++WAy/qwee5uUi1gvOg2WENJC6EUyHBOkcd3YmLDYSZ2TPPA==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
"integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
"cpu": [
"x64"
],
@ -98,9 +98,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.5.tgz",
"integrity": "sha512-VbdXJkn2aI2pQ/wxNEjEcnEDwPpxt3CWWMFYmO7CcdFBoOsABRy2W8F3kjbF9F/pecEUDcI3b5i2w+By4VQFPg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
"integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
"cpu": [
"arm64"
],
@ -114,9 +114,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.5.tgz",
"integrity": "sha512-olgGYND1/XnnWxwhjtY3/ryjOG/M4WfcA6XH8dBTH1cxMeBemMODXSFhkw71Kf4TeZFFTN25YOomaNh0vq2iXg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
"integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
"cpu": [
"x64"
],
@ -130,9 +130,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.5.tgz",
"integrity": "sha512-YBdCyQwA3OQupi6W2/WO4FnI+NWFWe79cZEtlbqSESOHEg7a73htBIRiE6uHPQe7Yp5E4aALv+JxkRLGEUL7tw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
"integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
"cpu": [
"arm"
],
@ -146,9 +146,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.5.tgz",
"integrity": "sha512-8a0bqSwu3OlLCfu2FBbDNgQyBYdPJh1B9PvNX7jMaKGC9/KopgHs37t+pQqeMLzcyRqG6z55IGNQAMSlCpBuqg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
"integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
"cpu": [
"arm64"
],
@ -162,9 +162,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.5.tgz",
"integrity": "sha512-uCwm1r/+NdP7vndctgq3PoZrnmhmnecWAr114GWMRwg2QMFFX+kIWnp7IO220/JLgnXK/jP7VKAFBGmeOYBQYQ==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
"integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
"cpu": [
"ia32"
],
@ -178,9 +178,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.5.tgz",
"integrity": "sha512-3YxhSBl5Sb6TtBjJu+HP93poBruFzgXmf3PVfIe4xOXMj1XpxboYZyw3W8BhoX/uwxzZz4K1I99jTE/5cgDT1g==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
"integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
"cpu": [
"loong64"
],
@ -194,9 +194,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.5.tgz",
"integrity": "sha512-Hy5Z0YVWyYHdtQ5mfmfp8LdhVwGbwVuq8mHzLqrG16BaMgEmit2xKO+iDakHs+OetEx0EN/2mUzDdfdktI+Nmg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
"integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
"cpu": [
"mips64el"
],
@ -210,9 +210,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.5.tgz",
"integrity": "sha512-5dbQvBLbU/Y3Q4ABc9gi23hww1mQcM7KZ9KBqabB7qhJswYMf8WrDDOSw3gdf3p+ffmijMd28mfVMvFucuECyg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
"integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
"cpu": [
"ppc64"
],
@ -226,9 +226,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.5.tgz",
"integrity": "sha512-fp/KUB/ZPzEWGTEUgz9wIAKCqu7CjH1GqXUO2WJdik1UNBQ7Xzw7myIajpxztE4Csb9504ERiFMxZg5KZ6HlZQ==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
"integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
"cpu": [
"riscv64"
],
@ -242,9 +242,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.5.tgz",
"integrity": "sha512-kRV3yw19YDqHTp8SfHXfObUFXlaiiw4o2lvT1XjsPZ++22GqZwSsYWJLjMi1Sl7j9qDlDUduWDze/nQx0d6Lzw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
"integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
"cpu": [
"s390x"
],
@ -258,9 +258,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.5.tgz",
"integrity": "sha512-vnxuhh9e4pbtABNLbT2ANW4uwQ/zvcHRCm1JxaYkzSehugoFd5iXyC4ci1nhXU13mxEwCnrnTIiiSGwa/uAF1g==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
"integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
"cpu": [
"x64"
],
@ -274,9 +274,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.5.tgz",
"integrity": "sha512-cigBpdiSx/vPy7doUyImsQQBnBjV5f1M99ZUlaJckDAJjgXWl6y9W17FIfJTy8TxosEF6MXq+fpLsitMGts2nA==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
"integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
"cpu": [
"x64"
],
@ -290,9 +290,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.5.tgz",
"integrity": "sha512-VdqRqPVIjjZfkf40LrqOaVuhw9EQiAZ/GNCSM2UplDkaIzYVsSnycxcFfAnHdWI8Gyt6dO15KHikbpxwx+xHbw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
"integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
"cpu": [
"x64"
],
@ -306,9 +306,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.5.tgz",
"integrity": "sha512-ItxPaJ3MBLtI4nK+mALLEoUs6amxsx+J1ibnfcYMkqaCqHST1AkF4aENpBehty3czqw64r/XqL+W9WqU6kc2Qw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
"integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
"cpu": [
"x64"
],
@ -322,9 +322,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.5.tgz",
"integrity": "sha512-4u2Q6qsJTYNFdS9zHoAi80spzf78C16m2wla4eJPh4kSbRv+BpXIfl6TmBSWupD8e47B1NrTfrOlEuco7mYQtg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
"integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
"cpu": [
"arm64"
],
@ -338,9 +338,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.5.tgz",
"integrity": "sha512-KYlm+Xu9TXsfTWAcocLuISRtqxKp/Y9ZBVg6CEEj0O5J9mn7YvBKzAszo2j1ndyzUPk+op+Tie2PJeN+BnXGqQ==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
"integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
"cpu": [
"ia32"
],
@ -354,9 +354,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.5.tgz",
"integrity": "sha512-XgA9qWRqby7xdYXuF6KALsn37QGBMHsdhmnpjfZtYxKxbTOwfnDM6MYi2WuUku5poNaX2n9XGVr20zgT/2QwCw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
"integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
"cpu": [
"x64"
],
@ -1451,9 +1451,9 @@
"dev": true
},
"node_modules/esbuild": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.5.tgz",
"integrity": "sha512-Bu6WLCc9NMsNoMJUjGl3yBzTjVLXdysMltxQWiLAypP+/vQrf+3L1Xe8fCXzxaECus2cEJ9M7pk4yKatEwQMqQ==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
"integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
"dev": true,
"hasInstallScript": true,
"bin": {
@ -1463,28 +1463,28 @@
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.17.5",
"@esbuild/android-arm64": "0.17.5",
"@esbuild/android-x64": "0.17.5",
"@esbuild/darwin-arm64": "0.17.5",
"@esbuild/darwin-x64": "0.17.5",
"@esbuild/freebsd-arm64": "0.17.5",
"@esbuild/freebsd-x64": "0.17.5",
"@esbuild/linux-arm": "0.17.5",
"@esbuild/linux-arm64": "0.17.5",
"@esbuild/linux-ia32": "0.17.5",
"@esbuild/linux-loong64": "0.17.5",
"@esbuild/linux-mips64el": "0.17.5",
"@esbuild/linux-ppc64": "0.17.5",
"@esbuild/linux-riscv64": "0.17.5",
"@esbuild/linux-s390x": "0.17.5",
"@esbuild/linux-x64": "0.17.5",
"@esbuild/netbsd-x64": "0.17.5",
"@esbuild/openbsd-x64": "0.17.5",
"@esbuild/sunos-x64": "0.17.5",
"@esbuild/win32-arm64": "0.17.5",
"@esbuild/win32-ia32": "0.17.5",
"@esbuild/win32-x64": "0.17.5"
"@esbuild/android-arm": "0.17.19",
"@esbuild/android-arm64": "0.17.19",
"@esbuild/android-x64": "0.17.19",
"@esbuild/darwin-arm64": "0.17.19",
"@esbuild/darwin-x64": "0.17.19",
"@esbuild/freebsd-arm64": "0.17.19",
"@esbuild/freebsd-x64": "0.17.19",
"@esbuild/linux-arm": "0.17.19",
"@esbuild/linux-arm64": "0.17.19",
"@esbuild/linux-ia32": "0.17.19",
"@esbuild/linux-loong64": "0.17.19",
"@esbuild/linux-mips64el": "0.17.19",
"@esbuild/linux-ppc64": "0.17.19",
"@esbuild/linux-riscv64": "0.17.19",
"@esbuild/linux-s390x": "0.17.19",
"@esbuild/linux-x64": "0.17.19",
"@esbuild/netbsd-x64": "0.17.19",
"@esbuild/openbsd-x64": "0.17.19",
"@esbuild/sunos-x64": "0.17.19",
"@esbuild/win32-arm64": "0.17.19",
"@esbuild/win32-ia32": "0.17.19",
"@esbuild/win32-x64": "0.17.19"
}
},
"node_modules/esbuild-android-64": {
@ -5127,156 +5127,156 @@
},
"dependencies": {
"@esbuild/android-arm": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.5.tgz",
"integrity": "sha512-crmPUzgCmF+qZXfl1YkiFoUta2XAfixR1tEnr/gXIixE+WL8Z0BGqfydP5oox0EUOgQMMRgtATtakyAcClQVqQ==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
"integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
"dev": true,
"optional": true
},
"@esbuild/android-arm64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.5.tgz",
"integrity": "sha512-KHWkDqYAMmKZjY4RAN1PR96q6UOtfkWlTS8uEwWxdLtkRt/0F/csUhXIrVfaSIFxnscIBMPynGfhsMwQDRIBQw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
"integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
"dev": true,
"optional": true
},
"@esbuild/android-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.5.tgz",
"integrity": "sha512-8fI/AnIdmWz/+1iza2WrCw8kwXK9wZp/yZY/iS8ioC+U37yJCeppi9EHY05ewJKN64ASoBIseufZROtcFnX5GA==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
"integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
"dev": true,
"optional": true
},
"@esbuild/darwin-arm64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.5.tgz",
"integrity": "sha512-EAvaoyIySV6Iif3NQCglUNpnMfHSUgC5ugt2efl3+QDntucJe5spn0udNZjTgNi6tKVqSceOw9tQ32liNZc1Xw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
"integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
"dev": true,
"optional": true
},
"@esbuild/darwin-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.5.tgz",
"integrity": "sha512-ha7QCJh1fuSwwCgoegfdaljowwWozwTDjBgjD3++WAy/qwee5uUi1gvOg2WENJC6EUyHBOkcd3YmLDYSZ2TPPA==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
"integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
"dev": true,
"optional": true
},
"@esbuild/freebsd-arm64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.5.tgz",
"integrity": "sha512-VbdXJkn2aI2pQ/wxNEjEcnEDwPpxt3CWWMFYmO7CcdFBoOsABRy2W8F3kjbF9F/pecEUDcI3b5i2w+By4VQFPg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
"integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
"dev": true,
"optional": true
},
"@esbuild/freebsd-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.5.tgz",
"integrity": "sha512-olgGYND1/XnnWxwhjtY3/ryjOG/M4WfcA6XH8dBTH1cxMeBemMODXSFhkw71Kf4TeZFFTN25YOomaNh0vq2iXg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
"integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
"dev": true,
"optional": true
},
"@esbuild/linux-arm": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.5.tgz",
"integrity": "sha512-YBdCyQwA3OQupi6W2/WO4FnI+NWFWe79cZEtlbqSESOHEg7a73htBIRiE6uHPQe7Yp5E4aALv+JxkRLGEUL7tw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
"integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
"dev": true,
"optional": true
},
"@esbuild/linux-arm64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.5.tgz",
"integrity": "sha512-8a0bqSwu3OlLCfu2FBbDNgQyBYdPJh1B9PvNX7jMaKGC9/KopgHs37t+pQqeMLzcyRqG6z55IGNQAMSlCpBuqg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
"integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
"dev": true,
"optional": true
},
"@esbuild/linux-ia32": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.5.tgz",
"integrity": "sha512-uCwm1r/+NdP7vndctgq3PoZrnmhmnecWAr114GWMRwg2QMFFX+kIWnp7IO220/JLgnXK/jP7VKAFBGmeOYBQYQ==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
"integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
"dev": true,
"optional": true
},
"@esbuild/linux-loong64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.5.tgz",
"integrity": "sha512-3YxhSBl5Sb6TtBjJu+HP93poBruFzgXmf3PVfIe4xOXMj1XpxboYZyw3W8BhoX/uwxzZz4K1I99jTE/5cgDT1g==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
"integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
"dev": true,
"optional": true
},
"@esbuild/linux-mips64el": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.5.tgz",
"integrity": "sha512-Hy5Z0YVWyYHdtQ5mfmfp8LdhVwGbwVuq8mHzLqrG16BaMgEmit2xKO+iDakHs+OetEx0EN/2mUzDdfdktI+Nmg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
"integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
"dev": true,
"optional": true
},
"@esbuild/linux-ppc64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.5.tgz",
"integrity": "sha512-5dbQvBLbU/Y3Q4ABc9gi23hww1mQcM7KZ9KBqabB7qhJswYMf8WrDDOSw3gdf3p+ffmijMd28mfVMvFucuECyg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
"integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
"dev": true,
"optional": true
},
"@esbuild/linux-riscv64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.5.tgz",
"integrity": "sha512-fp/KUB/ZPzEWGTEUgz9wIAKCqu7CjH1GqXUO2WJdik1UNBQ7Xzw7myIajpxztE4Csb9504ERiFMxZg5KZ6HlZQ==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
"integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
"dev": true,
"optional": true
},
"@esbuild/linux-s390x": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.5.tgz",
"integrity": "sha512-kRV3yw19YDqHTp8SfHXfObUFXlaiiw4o2lvT1XjsPZ++22GqZwSsYWJLjMi1Sl7j9qDlDUduWDze/nQx0d6Lzw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
"integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
"dev": true,
"optional": true
},
"@esbuild/linux-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.5.tgz",
"integrity": "sha512-vnxuhh9e4pbtABNLbT2ANW4uwQ/zvcHRCm1JxaYkzSehugoFd5iXyC4ci1nhXU13mxEwCnrnTIiiSGwa/uAF1g==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
"integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
"dev": true,
"optional": true
},
"@esbuild/netbsd-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.5.tgz",
"integrity": "sha512-cigBpdiSx/vPy7doUyImsQQBnBjV5f1M99ZUlaJckDAJjgXWl6y9W17FIfJTy8TxosEF6MXq+fpLsitMGts2nA==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
"integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
"dev": true,
"optional": true
},
"@esbuild/openbsd-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.5.tgz",
"integrity": "sha512-VdqRqPVIjjZfkf40LrqOaVuhw9EQiAZ/GNCSM2UplDkaIzYVsSnycxcFfAnHdWI8Gyt6dO15KHikbpxwx+xHbw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
"integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
"dev": true,
"optional": true
},
"@esbuild/sunos-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.5.tgz",
"integrity": "sha512-ItxPaJ3MBLtI4nK+mALLEoUs6amxsx+J1ibnfcYMkqaCqHST1AkF4aENpBehty3czqw64r/XqL+W9WqU6kc2Qw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
"integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
"dev": true,
"optional": true
},
"@esbuild/win32-arm64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.5.tgz",
"integrity": "sha512-4u2Q6qsJTYNFdS9zHoAi80spzf78C16m2wla4eJPh4kSbRv+BpXIfl6TmBSWupD8e47B1NrTfrOlEuco7mYQtg==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
"integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
"dev": true,
"optional": true
},
"@esbuild/win32-ia32": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.5.tgz",
"integrity": "sha512-KYlm+Xu9TXsfTWAcocLuISRtqxKp/Y9ZBVg6CEEj0O5J9mn7YvBKzAszo2j1ndyzUPk+op+Tie2PJeN+BnXGqQ==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
"integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
"dev": true,
"optional": true
},
"@esbuild/win32-x64": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.5.tgz",
"integrity": "sha512-XgA9qWRqby7xdYXuF6KALsn37QGBMHsdhmnpjfZtYxKxbTOwfnDM6MYi2WuUku5poNaX2n9XGVr20zgT/2QwCw==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
"integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
"dev": true,
"optional": true
},
@ -6088,33 +6088,33 @@
"dev": true
},
"esbuild": {
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.5.tgz",
"integrity": "sha512-Bu6WLCc9NMsNoMJUjGl3yBzTjVLXdysMltxQWiLAypP+/vQrf+3L1Xe8fCXzxaECus2cEJ9M7pk4yKatEwQMqQ==",
"version": "0.17.19",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
"integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
"dev": true,
"requires": {
"@esbuild/android-arm": "0.17.5",
"@esbuild/android-arm64": "0.17.5",
"@esbuild/android-x64": "0.17.5",
"@esbuild/darwin-arm64": "0.17.5",
"@esbuild/darwin-x64": "0.17.5",
"@esbuild/freebsd-arm64": "0.17.5",
"@esbuild/freebsd-x64": "0.17.5",
"@esbuild/linux-arm": "0.17.5",
"@esbuild/linux-arm64": "0.17.5",
"@esbuild/linux-ia32": "0.17.5",
"@esbuild/linux-loong64": "0.17.5",
"@esbuild/linux-mips64el": "0.17.5",
"@esbuild/linux-ppc64": "0.17.5",
"@esbuild/linux-riscv64": "0.17.5",
"@esbuild/linux-s390x": "0.17.5",
"@esbuild/linux-x64": "0.17.5",
"@esbuild/netbsd-x64": "0.17.5",
"@esbuild/openbsd-x64": "0.17.5",
"@esbuild/sunos-x64": "0.17.5",
"@esbuild/win32-arm64": "0.17.5",
"@esbuild/win32-ia32": "0.17.5",
"@esbuild/win32-x64": "0.17.5"
"@esbuild/android-arm": "0.17.19",
"@esbuild/android-arm64": "0.17.19",
"@esbuild/android-x64": "0.17.19",
"@esbuild/darwin-arm64": "0.17.19",
"@esbuild/darwin-x64": "0.17.19",
"@esbuild/freebsd-arm64": "0.17.19",
"@esbuild/freebsd-x64": "0.17.19",
"@esbuild/linux-arm": "0.17.19",
"@esbuild/linux-arm64": "0.17.19",
"@esbuild/linux-ia32": "0.17.19",
"@esbuild/linux-loong64": "0.17.19",
"@esbuild/linux-mips64el": "0.17.19",
"@esbuild/linux-ppc64": "0.17.19",
"@esbuild/linux-riscv64": "0.17.19",
"@esbuild/linux-s390x": "0.17.19",
"@esbuild/linux-x64": "0.17.19",
"@esbuild/netbsd-x64": "0.17.19",
"@esbuild/openbsd-x64": "0.17.19",
"@esbuild/sunos-x64": "0.17.19",
"@esbuild/win32-arm64": "0.17.19",
"@esbuild/win32-ia32": "0.17.19",
"@esbuild/win32-x64": "0.17.19"
}
},
"esbuild-android-64": {

View file

@ -7,7 +7,7 @@
"author": "Lea Anthony <lea.anthony@gmail.com>",
"license": "ISC",
"devDependencies": {
"esbuild": "^0.17.5",
"esbuild": "^0.17.19",
"happy-dom": "^8.1.5",
"nanoid": "^4.0.0",
"npm-check-updates": "^16.6.3",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -12,6 +12,6 @@ require (
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -3,9 +3,11 @@ package templates
import (
"embed"
"fmt"
"github.com/pterm/pterm"
"github.com/wailsapp/wails/v3/internal/debug"
"io/fs"
"os"
"path/filepath"
"github.com/wailsapp/wails/v3/internal/flags"
@ -154,7 +156,7 @@ func Install(options *flags.Init) error {
templateData := TemplateOptions{
options,
debug.LocalModulePath,
filepath.FromSlash(debug.LocalModulePath + "/"),
}
template, found := lo.Find(defaultTemplates, func(template TemplateData) bool {
return template.Name == options.TemplateName
@ -167,7 +169,7 @@ func Install(options *flags.Init) error {
templateData.ProjectDir = lo.Must(os.Getwd())
}
templateData.ProjectDir = fmt.Sprintf("%s/%s", options.ProjectDir, options.ProjectName)
fmt.Printf("Installing template '%s' into '%s'\n", options.TemplateName, options.ProjectDir)
pterm.Printf("Installing template '%s' into '%s'\n", options.TemplateName, filepath.FromSlash(options.ProjectDir))
tfs, err := fs.Sub(template.FS, options.TemplateName)
if err != nil {
return err

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -16,6 +16,6 @@ require (
golang.org/x/net v0.7.0 // indirect
)
{{if gt (len .LocalModulePath) 0}}
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}/v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}/v2
replace github.com/wailsapp/wails/v3 => {{.LocalModulePath}}v3
replace github.com/wailsapp/wails/v2 => {{.LocalModulePath}}v2
{{end}}

View file

@ -20,7 +20,7 @@ func main() {
},
})
// Create window
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{

View file

@ -1,6 +1,5 @@
package application
import "C"
import (
"log"
"net/http"
@ -9,6 +8,10 @@ import (
"strconv"
"sync"
"github.com/wailsapp/wails/v3/pkg/icons"
"github.com/samber/lo"
"github.com/wailsapp/wails/v2/pkg/assetserver"
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
assetserveroptions "github.com/wailsapp/wails/v2/pkg/options/assetserver"
@ -20,10 +23,17 @@ import (
var globalApplication *App
// isDebugMode is true if the application is running in debug mode
var isDebugMode func() bool
func init() {
runtime.LockOSThread()
}
type EventListener struct {
callback func()
}
func New(appOptions Options) *App {
if globalApplication != nil {
return globalApplication
@ -33,7 +43,8 @@ func New(appOptions Options) *App {
result := &App{
options: appOptions,
applicationEventListeners: make(map[uint][]func()),
applicationEventListeners: make(map[uint][]*EventListener),
windows: make(map[uint]*WebviewWindow),
systemTrays: make(map[uint]*SystemTray),
log: logger.New(appOptions.Logger.CustomLoggers...),
contextMenus: make(map[string]*Menu),
@ -45,6 +56,13 @@ func New(appOptions Options) *App {
result.log.AddOutput(&logger.Console{})
}
// Patch isDebug if we aren't in prod mode
if isDebugMode == nil {
isDebugMode = func() bool {
return true
}
}
result.Events = NewWailsEventProcessor(result.dispatchEventToWindows)
opts := assetserveroptions.Options{
@ -92,24 +110,32 @@ func mergeApplicationDefaults(o *Options) {
o.Description = "An application written using Wails"
}
if o.Icon == nil {
o.Icon = DefaultApplicationIcon
o.Icon = icons.ApplicationLightMode256
}
}
type platformApp interface {
run() error
destroy()
setApplicationMenu(menu *Menu)
name() string
getCurrentWindowID() uint
showAboutDialog(name string, description string, icon []byte)
setIcon(icon []byte)
on(id uint)
dispatchOnMainThread(id uint)
hide()
show()
}
type (
platformApp interface {
run() error
destroy()
setApplicationMenu(menu *Menu)
name() string
getCurrentWindowID() uint
showAboutDialog(name string, description string, icon []byte)
setIcon(icon []byte)
on(id uint)
dispatchOnMainThread(id uint)
hide()
show()
getPrimaryScreen() (*Screen, error)
getScreens() ([]*Screen, error)
}
runnable interface {
run()
}
)
// Messages sent from javascript get routed here
type windowMessage struct {
@ -152,7 +178,7 @@ var webviewRequests = make(chan *webViewAssetRequest)
type App struct {
options Options
applicationEventListeners map[uint][]func()
applicationEventListeners map[uint][]*EventListener
applicationEventListenersLock sync.RWMutex
// Windows
@ -170,7 +196,10 @@ type App struct {
menuItemsLock sync.Mutex
// Running
running bool
running bool
runLock sync.Mutex
pendingRun []runnable
bindings *Bindings
plugins *PluginManager
@ -187,7 +216,8 @@ type App struct {
contextMenus map[string]*Menu
contextMenusLock sync.Mutex
assets *assetserver.AssetServer
assets *assetserver.AssetServer
startURL string
// Hooks
windowCreatedCallbacks []func(window *WebviewWindow)
@ -213,17 +243,28 @@ func (a *App) deleteWindowByID(id uint) {
delete(a.windows, id)
}
func (a *App) On(eventType events.ApplicationEventType, callback func()) {
func (a *App) On(eventType events.ApplicationEventType, callback func()) func() {
eventID := uint(eventType)
a.applicationEventListenersLock.Lock()
defer a.applicationEventListenersLock.Unlock()
a.applicationEventListeners[eventID] = append(a.applicationEventListeners[eventID], callback)
listener := &EventListener{
callback: callback,
}
a.applicationEventListeners[eventID] = append(a.applicationEventListeners[eventID], listener)
if a.impl != nil {
go a.impl.on(eventID)
}
return func() {
// lock the map
a.applicationEventListenersLock.Lock()
defer a.applicationEventListenersLock.Unlock()
// Remove listener
a.applicationEventListeners[eventID] = lo.Without(a.applicationEventListeners[eventID], listener)
}
}
func (a *App) NewWebviewWindow() *WebviewWindow {
return a.NewWebviewWindowWithOptions(&WebviewWindowOptions{})
return a.NewWebviewWindowWithOptions(WebviewWindowOptions{})
}
func (a *App) GetPID() int {
@ -264,16 +305,10 @@ func (a *App) error(message string, args ...any) {
})
}
func (a *App) NewWebviewWindowWithOptions(windowOptions *WebviewWindowOptions) *WebviewWindow {
// Ensure we have sane defaults
if windowOptions == nil {
windowOptions = WebviewWindowDefaults
}
func (a *App) NewWebviewWindowWithOptions(windowOptions WebviewWindowOptions) *WebviewWindow {
newWindow := NewWindow(windowOptions)
id := newWindow.id
if a.windows == nil {
a.windows = make(map[uint]*WebviewWindow)
}
a.windowsLock.Lock()
a.windows[id] = newWindow
a.windowsLock.Unlock()
@ -283,9 +318,7 @@ func (a *App) NewWebviewWindowWithOptions(windowOptions *WebviewWindowOptions) *
hook(newWindow)
}
if a.running {
newWindow.run()
}
a.runOrDeferToAppRun(newWindow)
return newWindow
}
@ -293,13 +326,13 @@ func (a *App) NewWebviewWindowWithOptions(windowOptions *WebviewWindowOptions) *
func (a *App) NewSystemTray() *SystemTray {
id := a.getSystemTrayID()
newSystemTray := NewSystemTray(id)
a.systemTraysLock.Lock()
a.systemTrays[id] = newSystemTray
a.systemTraysLock.Unlock()
if a.running {
newSystemTray.Run()
}
a.runOrDeferToAppRun(newSystemTray)
return newSystemTray
}
@ -307,7 +340,6 @@ func (a *App) Run() error {
a.info("Starting application")
a.impl = newPlatformApp(a)
a.running = true
go func() {
for {
event := <-applicationEvents
@ -346,21 +378,21 @@ func (a *App) Run() error {
}
}()
// run windows
for _, window := range a.windows {
go window.run()
}
a.runLock.Lock()
a.running = true
// run system trays
for _, systray := range a.systemTrays {
go systray.Run()
for _, systray := range a.pendingRun {
go systray.run()
}
a.pendingRun = nil
a.runLock.Unlock()
// set the application menu
a.impl.setApplicationMenu(a.ApplicationMenu)
// set the application Icon
a.impl.setIcon(a.options.Icon)
if runtime.GOOS == "darwin" {
a.impl.setApplicationMenu(a.ApplicationMenu)
a.impl.setIcon(a.options.Icon)
}
err := a.impl.run()
if err != nil {
@ -380,7 +412,7 @@ func (a *App) handleApplicationEvent(event uint) {
return
}
for _, listener := range listeners {
go listener()
go listener.callback()
}
}
@ -514,11 +546,11 @@ func (a *App) SaveFileDialog() *SaveFileDialog {
}
func (a *App) GetPrimaryScreen() (*Screen, error) {
return getPrimaryScreen()
return a.impl.getPrimaryScreen()
}
func (a *App) GetScreens() ([]*Screen, error) {
return getScreens()
return a.impl.getScreens()
}
func (a *App) Clipboard() *Clipboard {
@ -599,3 +631,48 @@ func (a *App) GetWindowByName(name string) *WebviewWindow {
}
return nil
}
func (a *App) runOrDeferToAppRun(r runnable) {
a.runLock.Lock()
running := a.running
if !running {
a.pendingRun = append(a.pendingRun, r)
}
a.runLock.Unlock()
if running {
r.run()
}
}
func invokeSync(fn func()) {
var wg sync.WaitGroup
wg.Add(1)
globalApplication.dispatchOnMainThread(func() {
fn()
wg.Done()
})
wg.Wait()
}
func invokeSyncWithResult[T any](fn func() T) (res T) {
var wg sync.WaitGroup
wg.Add(1)
globalApplication.dispatchOnMainThread(func() {
res = fn()
wg.Done()
})
wg.Wait()
return res
}
func invokeSyncWithResultAndError[T any](fn func() (T, error)) (res T, err error) {
var wg sync.WaitGroup
wg.Add(1)
globalApplication.dispatchOnMainThread(func() {
res, err = fn()
wg.Done()
})
wg.Wait()
return res, err
}

View file

@ -7,9 +7,9 @@ package application
#cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c
#cgo LDFLAGS: -framework Cocoa -mmacosx-version-min=10.13
#include "application.h"
#include "app_delegate.h"
#include "webview_window.h"
#include "application_darwin.h"
#include "application_darwin_delegate.h"
#include "webview_window_darwin.h"
#include <stdlib.h>
extern void registerListener(unsigned int event);
@ -137,6 +137,10 @@ type macosApp struct {
parent *App
}
func getNativeApplication() *macosApp {
return globalApplication.impl.(*macosApp)
}
func (m *macosApp) hide() {
C.hide()
}
@ -182,6 +186,7 @@ func (m *macosApp) run() error {
C.setActivationPolicy(C.int(m.parent.options.Mac.ActivationPolicy))
C.activateIgnoringOtherApps()
})
m.setupCommonEvents()
// setup event listeners
for eventID := range m.parent.applicationEventListeners {
m.on(eventID)

View file

@ -1,5 +1,5 @@
//go:build darwin
#import "app_delegate.h"
#import "application_darwin_delegate.h"
#import "../events/events.h"
extern bool hasListeners(unsigned int);
@implementation AppDelegate

View file

@ -0,0 +1,10 @@
//go:build production
package application
// We use this to patch the application to production mode.
func init() {
isDebugMode = func() bool {
return false
}
}

View file

@ -0,0 +1,246 @@
//go:build windows
package application
import (
"os"
"syscall"
"unsafe"
"github.com/wailsapp/wails/v3/pkg/events"
"github.com/wailsapp/wails/v3/pkg/w32"
"github.com/samber/lo"
)
var windowClassName = lo.Must(syscall.UTF16PtrFromString("WailsWebviewWindow"))
type windowsApp struct {
parent *App
instance w32.HINSTANCE
windowMap map[w32.HWND]*windowsWebviewWindow
systrayMap map[w32.HMENU]*windowsSystemTray
mainThreadID w32.HANDLE
mainThreadWindowHWND w32.HWND
// Windows hidden by application.Hide()
hiddenWindows []*windowsWebviewWindow
focusedWindow w32.HWND
// system theme
isDarkMode bool
}
func getNativeApplication() *windowsApp {
return globalApplication.impl.(*windowsApp)
}
func (m *windowsApp) getPrimaryScreen() (*Screen, error) {
//TODO implement me
panic("implement me")
}
func (m *windowsApp) getScreens() ([]*Screen, error) {
//TODO implement me
panic("implement me")
}
func (m *windowsApp) hide() {
// Get the current focussed window
m.focusedWindow = w32.GetForegroundWindow()
// Iterate over all windows and hide them if they aren't already hidden
for _, window := range m.windowMap {
if window.isVisible() {
// Add to hidden windows
m.hiddenWindows = append(m.hiddenWindows, window)
window.hide()
}
}
// Switch focus to the next application
hwndNext := w32.GetWindow(m.mainThreadWindowHWND, w32.GW_HWNDNEXT)
w32.SetForegroundWindow(hwndNext)
}
func (m *windowsApp) show() {
// Iterate over all windows and show them if they were previously hidden
for _, window := range m.hiddenWindows {
window.show()
}
// Show the foreground window
w32.SetForegroundWindow(m.focusedWindow)
}
func (m *windowsApp) on(eventID uint) {
//C.registerListener(C.uint(eventID))
}
func (m *windowsApp) setIcon(icon []byte) {
//C.setApplicationIcon(unsafe.Pointer(&icon[0]), C.int(len(icon)))
}
func (m *windowsApp) name() string {
//appName := C.getAppName()
//defer C.free(unsafe.Pointer(appName))
//return C.GoString(appName)
return ""
}
func (m *windowsApp) getCurrentWindowID() uint {
//return uint(C.getCurrentWindowID())
return uint(0)
}
func (m *windowsApp) setApplicationMenu(menu *Menu) {
if menu == nil {
// Create a default menu for windows
menu = defaultApplicationMenu()
}
menu.Update()
// Convert impl to macosMenu object
//m.applicationMenu = (menu.impl).(*macosMenu).nsMenu
//C.setApplicationMenu(m.applicationMenu)
}
func (m *windowsApp) run() error {
// Add a hook to the ApplicationDidFinishLaunching event
//m.parent.On(events.Mac.ApplicationDidFinishLaunching, func() {
// C.setApplicationShouldTerminateAfterLastWindowClosed(C.bool(m.parent.options.Mac.ApplicationShouldTerminateAfterLastWindowClosed))
// C.setActivationPolicy(C.int(m.parent.options.Mac.ActivationPolicy))
// C.activateIgnoringOtherApps()
//})
// setup event listeners
for eventID := range m.parent.applicationEventListeners {
m.on(eventID)
}
_ = m.runMainLoop()
//C.run()
return nil
}
func (m *windowsApp) destroy() {
//C.destroyApp()
}
func (m *windowsApp) init() {
// Register the window class
icon := w32.LoadIconWithResourceID(m.instance, w32.IDI_APPLICATION)
var wc w32.WNDCLASSEX
wc.Size = uint32(unsafe.Sizeof(wc))
wc.Style = w32.CS_HREDRAW | w32.CS_VREDRAW
wc.WndProc = syscall.NewCallback(m.wndProc)
wc.Instance = m.instance
wc.Background = w32.COLOR_BTNFACE + 1
wc.Icon = icon
wc.Cursor = w32.LoadCursorWithResourceID(0, w32.IDC_ARROW)
wc.ClassName = windowClassName
wc.MenuName = nil
wc.IconSm = icon
if ret := w32.RegisterClassEx(&wc); ret == 0 {
panic(syscall.GetLastError())
}
m.isDarkMode = w32.IsCurrentlyDarkMode()
}
func (m *windowsApp) wndProc(hwnd w32.HWND, msg uint32, wParam, lParam uintptr) uintptr {
// Handle the invoke callback
if msg == wmInvokeCallback {
m.invokeCallback(wParam, lParam)
return 0
}
// If the WndProcInterceptor is set in options, pass the message on
if m.parent.options.Windows.WndProcInterceptor != nil {
returnValue, shouldReturn := m.parent.options.Windows.WndProcInterceptor(hwnd, msg, wParam, lParam)
if shouldReturn {
return returnValue
}
}
switch msg {
case w32.WM_SETTINGCHANGE:
settingChanged := w32.UTF16PtrToString((*uint16)(unsafe.Pointer(lParam)))
if settingChanged == "ImmersiveColorSet" {
isDarkMode := w32.IsCurrentlyDarkMode()
if isDarkMode != m.isDarkMode {
applicationEvents <- uint(events.Windows.SystemThemeChanged)
m.isDarkMode = isDarkMode
}
}
return 0
case w32.WM_POWERBROADCAST:
switch wParam {
case w32.PBT_APMPOWERSTATUSCHANGE:
applicationEvents <- uint(events.Windows.APMPowerStatusChange)
case w32.PBT_APMSUSPEND:
applicationEvents <- uint(events.Windows.APMSuspend)
case w32.PBT_APMRESUMEAUTOMATIC:
applicationEvents <- uint(events.Windows.APMResumeAutomatic)
case w32.PBT_APMRESUMESUSPEND:
applicationEvents <- uint(events.Windows.APMResumeSuspend)
case w32.PBT_POWERSETTINGCHANGE:
applicationEvents <- uint(events.Windows.APMPowerSettingChange)
}
return 0
}
if window, ok := m.windowMap[hwnd]; ok {
return window.WndProc(msg, wParam, lParam)
}
if systray, ok := m.systrayMap[hwnd]; ok {
return systray.wndProc(msg, wParam, lParam)
}
// Dispatch the message to the appropriate window
return w32.DefWindowProc(hwnd, msg, wParam, lParam)
}
func (m *windowsApp) registerWindow(result *windowsWebviewWindow) {
m.windowMap[result.hwnd] = result
}
func (m *windowsApp) registerSystemTray(result *windowsSystemTray) {
m.systrayMap[result.hwnd] = result
}
func (m *windowsApp) unregisterWindow(w *windowsWebviewWindow) {
delete(m.windowMap, w.hwnd)
// If this was the last window...
if len(m.windowMap) == 0 && !m.parent.options.Windows.DisableQuitOnLastWindowClosed {
w32.PostQuitMessage(0)
}
}
func newPlatformApp(app *App) *windowsApp {
err := w32.SetProcessDPIAware()
if err != nil {
println("Fatal error in application initialisation: ", err.Error())
os.Exit(1)
}
result := &windowsApp{
parent: app,
instance: w32.GetModuleHandle(""),
windowMap: make(map[w32.HWND]*windowsWebviewWindow),
systrayMap: make(map[w32.HWND]*windowsSystemTray),
}
result.init()
result.initMainLoop()
return result
}

View file

@ -1,7 +1,5 @@
package application
import "C"
type clipboardImpl interface {
setText(text string) bool
text() string

View file

@ -0,0 +1,28 @@
//go:build windows
package application
type windowsClipboard struct{}
func (m windowsClipboard) setText(text string) bool {
//clipboardLock.Lock()
//defer clipboardLock.Unlock()
//cText := C.CString(text)
//success := C.setClipboardText(cText)
//C.free(unsafe.Pointer(cText))
//return bool(success)
panic("implement me")
}
func (m windowsClipboard) text() string {
//clipboardLock.RLock()
//defer clipboardLock.RUnlock()
//clipboardText := C.getClipboardText()
//result := C.GoString(clipboardText)
//return result
panic("implement me")
}
func newClipboardImpl() *windowsClipboard {
return &windowsClipboard{}
}

View file

@ -1,6 +1,5 @@
package application
import "C"
import (
"strings"
"sync"
@ -49,11 +48,22 @@ type Button struct {
Label string
IsCancel bool
IsDefault bool
callback func()
Callback func()
}
func (b *Button) OnClick(callback func()) {
b.callback = callback
func (b *Button) OnClick(callback func()) *Button {
b.Callback = callback
return b
}
func (b *Button) SetAsDefault() *Button {
b.IsDefault = true
return b
}
func (b *Button) SetAsCancel() *Button {
b.IsCancel = true
return b
}
type messageDialogImpl interface {
@ -86,7 +96,6 @@ func newMessageDialog(dialogType DialogType) *MessageDialog {
return &MessageDialog{
MessageDialogOptions: MessageDialogOptions{
DialogType: dialogType,
Title: defaultTitles[dialogType],
},
impl: nil,
}
@ -101,7 +110,7 @@ func (d *MessageDialog) Show() {
if d.impl == nil {
d.impl = newDialogImpl(d)
}
d.impl.show()
invokeSync(d.impl.show)
}
func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog {
@ -249,7 +258,7 @@ func (d *OpenFileDialog) PromptForSingleSelection() (string, error) {
if d.impl == nil {
d.impl = newOpenFileDialogImpl(d)
}
selection, err := d.impl.show()
selection, err := invokeSyncWithResultAndError(d.impl.show)
var result string
if len(selection) > 0 {
result = selection[0]
@ -273,7 +282,7 @@ func (d *OpenFileDialog) PromptForMultipleSelection() ([]string, error) {
if d.impl == nil {
d.impl = newOpenFileDialogImpl(d)
}
return d.impl.show()
return invokeSyncWithResultAndError(d.impl.show)
}
func (d *OpenFileDialog) SetMessage(message string) *OpenFileDialog {
@ -338,10 +347,12 @@ type SaveFileDialogOptions struct {
AllowOtherFileTypes bool
HideExtension bool
TreatsFilePackagesAsDirectories bool
Title string
Message string
Directory string
Filename string
ButtonText string
Filters []FileFilter
}
type SaveFileDialog struct {
@ -356,10 +367,12 @@ type SaveFileDialog struct {
directory string
filename string
buttonText string
filters []FileFilter
window *WebviewWindow
impl saveFileDialogImpl
impl saveFileDialogImpl
title string
}
type saveFileDialogImpl interface {
@ -367,6 +380,7 @@ type saveFileDialogImpl interface {
}
func (d *SaveFileDialog) SetOptions(options *SaveFileDialogOptions) {
d.title = options.Title
d.canCreateDirectories = options.CanCreateDirectories
d.showHiddenFiles = options.ShowHiddenFiles
d.canSelectHiddenExtension = options.CanSelectHiddenExtension
@ -379,6 +393,16 @@ func (d *SaveFileDialog) SetOptions(options *SaveFileDialogOptions) {
d.buttonText = options.ButtonText
}
// AddFilter adds a filter to the dialog. The filter is a display name and a semicolon separated list of extensions.
// EG: AddFilter("Image Files", "*.jpg;*.png")
func (d *SaveFileDialog) AddFilter(displayName, pattern string) *SaveFileDialog {
d.filters = append(d.filters, FileFilter{
DisplayName: strings.TrimSpace(displayName),
Pattern: strings.TrimSpace(pattern),
})
return d
}
func (d *SaveFileDialog) CanCreateDirectories(canCreateDirectories bool) *SaveFileDialog {
d.canCreateDirectories = canCreateDirectories
return d
@ -413,7 +437,7 @@ func (d *SaveFileDialog) PromptForSingleSelection() (string, error) {
if d.impl == nil {
d.impl = newSaveFileDialogImpl(d)
}
return d.impl.show()
return invokeSyncWithResultAndError(d.impl.show)
}
func (d *SaveFileDialog) SetButtonText(text string) *SaveFileDialog {

View file

@ -9,7 +9,7 @@ package application
#import <Cocoa/Cocoa.h>
#import <UniformTypeIdentifiers/UTType.h>
#import "dialogs_delegate.h"
#import "dialogs_darwin_delegate.h"
extern void openFileDialogCallback(uint id, char* path);
extern void openFileDialogCallbackEnd(uint id);
@ -364,8 +364,8 @@ func (m *macosDialog) show() {
buttonPressed := int(C.dialogRunModal(m.nsDialog))
if len(m.dialog.Buttons) > buttonPressed {
button := reversedButtons[buttonPressed]
if button.callback != nil {
button.callback()
if button.Callback != nil {
button.Callback()
}
}
})

Some files were not shown because too many files have changed in this diff Show more