diff --git a/cmd/demo/main.go b/cmd/demo/main.go index f40db6e..ebfb086 100644 --- a/cmd/demo/main.go +++ b/cmd/demo/main.go @@ -8,7 +8,8 @@ import ( func main() { w := webview2.NewWithOptions(webview2.WebViewOptions{ - Debug: true, + Debug: true, + AutoFocus: true, WindowOptions: webview2.WindowOptions{ Title: "Minimal webview example", }, diff --git a/internal/w32/w32.go b/internal/w32/w32.go index f765aeb..9b3acee 100644 --- a/internal/w32/w32.go +++ b/internal/w32/w32.go @@ -63,6 +63,7 @@ const ( WMDestroy = 0x0002 WMMove = 0x0003 WMSize = 0x0005 + WMActivate = 0x0006 WMClose = 0x0010 WMQuit = 0x0012 WMGetMinMaxInfo = 0x0024 @@ -91,6 +92,12 @@ const ( WSOverlappedWindow = (WSOverlapped | WSCaption | WSSysMenu | WSThickFrame | WSMinimizeBox | WSMaximizeBox) ) +const ( + WAInactive = 0 + WAActive = 1 + WAActiveClick = 2 +) + type WndClassExW struct { CbSize uint32 Style uint32 diff --git a/pkg/edge/COREWEBVIEW2_MOVE_FOCUS_REASON.go b/pkg/edge/COREWEBVIEW2_MOVE_FOCUS_REASON.go new file mode 100644 index 0000000..e901101 --- /dev/null +++ b/pkg/edge/COREWEBVIEW2_MOVE_FOCUS_REASON.go @@ -0,0 +1,9 @@ +package edge + +type COREWEBVIEW2_MOVE_FOCUS_REASON uint32 + +const ( + COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC = 0 + COREWEBVIEW2_MOVE_FOCUS_REASON_NEXT = 1 + COREWEBVIEW2_MOVE_FOCUS_REASON_PREVIOUS = 2 +) diff --git a/pkg/edge/ICoreWebView2Controller.go b/pkg/edge/ICoreWebView2Controller.go index 53f1819..4b428a7 100644 --- a/pkg/edge/ICoreWebView2Controller.go +++ b/pkg/edge/ICoreWebView2Controller.go @@ -118,3 +118,15 @@ func (i *ICoreWebView2Controller) NotifyParentWindowPositionChanged() error { } return nil } + +func (i *ICoreWebView2Controller) MoveFocus(reason uintptr) error { + var err error + _, _, err = i.vtbl.MoveFocus.Call( + uintptr(unsafe.Pointer(i)), + uintptr(reason), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} diff --git a/pkg/edge/chromium.go b/pkg/edge/chromium.go index 6c7c0d6..321ac6a 100644 --- a/pkg/edge/chromium.go +++ b/pkg/edge/chromium.go @@ -16,6 +16,7 @@ import ( type Chromium struct { hwnd uintptr + focusOnInit bool controller *ICoreWebView2Controller webview *ICoreWebView2 inited uintptr @@ -216,6 +217,10 @@ func (e *Chromium) CreateCoreWebView2ControllerCompleted(res uintptr, controller atomic.StoreUintptr(&e.inited, 1) + if e.focusOnInit { + e.Focus() + } + return 0 } @@ -340,3 +345,11 @@ func (e *Chromium) NotifyParentWindowPositionChanged() error { } return e.controller.NotifyParentWindowPositionChanged() } + +func (e *Chromium) Focus() { + if e.controller == nil { + e.focusOnInit = true + return + } + _ = e.controller.MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC) +} diff --git a/webview.go b/webview.go index 591b698..553696d 100644 --- a/webview.go +++ b/webview.go @@ -42,12 +42,14 @@ type browser interface { Init(script string) Eval(script string) NotifyParentWindowPositionChanged() error + Focus() } type webview struct { hwnd uintptr mainthread uintptr browser browser + autofocus bool maxsz w32.Point minsz w32.Point m sync.Mutex @@ -60,10 +62,19 @@ type WindowOptions struct { } type WebViewOptions struct { - Window unsafe.Pointer - Debug bool + Window unsafe.Pointer + Debug bool + + // DataPath specifies the datapath for the WebView2 runtime to use for the + // browser instance. DataPath string + // AutoFocus will try to keep the WebView2 widget focused when the window + // is focused. + AutoFocus bool + + // WindowOptions customizes the window that is created to embed the + // WebView2 widget. WindowOptions WindowOptions } @@ -81,6 +92,7 @@ func NewWindow(debug bool, window unsafe.Pointer) WebView { func NewWithOptions(options WebViewOptions) WebView { w := &webview{} w.bindings = map[string]interface{}{} + w.autofocus = options.AutoFocus chromium := edge.NewChromium() chromium.MessageCallback = w.msgcb @@ -198,6 +210,13 @@ func wndproc(hwnd, msg, wp, lp uintptr) uintptr { return r case w32.WMSize: w.browser.Resize() + case w32.WMActivate: + if wp == w32.WAInactive { + break + } + if w.autofocus { + w.browser.Focus() + } case w32.WMClose: _, _, _ = w32.User32DestroyWindow.Call(hwnd) case w32.WMDestroy: