diff --git a/pkg/display/display.go b/pkg/display/display.go index ec4f281..fe89557 100644 --- a/pkg/display/display.go +++ b/pkg/display/display.go @@ -615,6 +615,19 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { Workflow: workflow, Windows: names, }) + case "window:arrange-pair": + first, e := wsRequire(msg.Data, "first") + if e != nil { + return nil, false, e + } + second, e := wsRequire(msg.Data, "second") + if e != nil { + return nil, false, e + } + result, handled, err = s.Core().PERFORM(window.TaskArrangePair{ + First: first, + Second: second, + }) case "layout:suggest": windowCount := 0 if count, ok := msg.Data["windowCount"].(float64); ok { @@ -639,6 +652,19 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { ScreenWidth: screenWidth, ScreenHeight: screenHeight, }) + case "screen:find-space": + width := 0 + if w, ok := msg.Data["width"].(float64); ok { + width = int(w) + } + height := 0 + if h, ok := msg.Data["height"].(float64); ok { + height = int(h) + } + result, handled, err = s.Core().QUERY(window.QueryFindSpace{ + Width: width, + Height: height, + }) case "screen:list": result, handled, err = s.Core().QUERY(screen.QueryAll{}) case "screen:get": @@ -1422,6 +1448,32 @@ func (s *Service) ApplyWorkflowLayout(workflow window.WorkflowLayout) error { return ws.Manager().ApplyWorkflow(workflow, ws.Manager().List(), screenWidth, screenHeight) } +// ArrangeWindowPair places two windows side by side using the window manager's balanced split. +func (s *Service) ArrangeWindowPair(first, second string) error { + ws := s.windowService() + if ws == nil { + return fmt.Errorf("window service not available") + } + screenWidth, screenHeight := s.primaryScreenSize() + return ws.Manager().ArrangePair(first, second, screenWidth, screenHeight) +} + +// FindSpace returns a free placement suggestion for a new window. +func (s *Service) FindSpace(width, height int) (window.SpaceInfo, error) { + ws := s.windowService() + if ws == nil { + return window.SpaceInfo{}, fmt.Errorf("window service not available") + } + screenWidth, screenHeight := s.primaryScreenSize() + if width <= 0 { + width = screenWidth / 2 + } + if height <= 0 { + height = screenHeight / 2 + } + return ws.Manager().FindSpace(screenWidth, screenHeight, width, height), nil +} + // --- Screen management --- // GetScreens returns all known screens. diff --git a/pkg/display/display_test.go b/pkg/display/display_test.go index 6932525..344742a 100644 --- a/pkg/display/display_test.go +++ b/pkg/display/display_test.go @@ -919,6 +919,36 @@ func TestHandleWSMessage_Extended_Good(t *testing.T) { assert.True(t, handled) }) + t.Run("window arrange pair", func(t *testing.T) { + _, handled, err := svc.handleWSMessage(WSMessage{ + Action: "window:arrange-pair", + Data: map[string]any{ + "first": "editor", + "second": "assistant", + }, + }) + require.NoError(t, err) + assert.True(t, handled) + }) + + t.Run("screen find space", func(t *testing.T) { + result, handled, err := svc.handleWSMessage(WSMessage{ + Action: "screen:find-space", + Data: map[string]any{ + "width": float64(400), + "height": float64(300), + }, + }) + require.NoError(t, err) + assert.True(t, handled) + space, ok := result.(window.SpaceInfo) + require.True(t, ok) + assert.Equal(t, 2560, space.ScreenWidth) + assert.Equal(t, 1440, space.ScreenHeight) + assert.Equal(t, 400, space.Width) + assert.Equal(t, 300, space.Height) + }) + t.Run("clipboard image read", func(t *testing.T) { result, handled, err := svc.handleWSMessage(WSMessage{Action: "clipboard:read-image"}) require.NoError(t, err) diff --git a/pkg/window/mock_test.go b/pkg/window/mock_test.go index df23b21..caa7f9a 100644 --- a/pkg/window/mock_test.go +++ b/pkg/window/mock_test.go @@ -36,6 +36,7 @@ type mockWindow struct { maximised, focused bool visible, alwaysOnTop bool backgroundColor [4]uint8 + devtoolsOpen bool closed bool eventHandlers []func(WindowEvent) fileDropHandlers []func(paths []string, targetID string) @@ -62,8 +63,8 @@ func (w *mockWindow) Show() { w.visible = true } func (w *mockWindow) Hide() { w.visible = false } func (w *mockWindow) Fullscreen() {} func (w *mockWindow) UnFullscreen() {} -func (w *mockWindow) OpenDevTools() {} -func (w *mockWindow) CloseDevTools() {} +func (w *mockWindow) OpenDevTools() { w.devtoolsOpen = true } +func (w *mockWindow) CloseDevTools() { w.devtoolsOpen = false } func (w *mockWindow) OnWindowEvent(handler func(WindowEvent)) { w.eventHandlers = append(w.eventHandlers, handler) } diff --git a/pkg/window/wails.go b/pkg/window/wails.go index 8c439b9..a6424f3 100644 --- a/pkg/window/wails.go +++ b/pkg/window/wails.go @@ -88,7 +88,7 @@ func (ww *wailsWindow) Hide() { ww.w.Hide() } func (ww *wailsWindow) Fullscreen() { ww.w.Fullscreen() } func (ww *wailsWindow) UnFullscreen() { ww.w.UnFullscreen() } func (ww *wailsWindow) OpenDevTools() { ww.w.OpenDevTools() } -func (ww *wailsWindow) CloseDevTools() {} +func (ww *wailsWindow) CloseDevTools() { ww.w.CloseDevTools() } func (ww *wailsWindow) OnWindowEvent(handler func(event WindowEvent)) { name := ww.w.Name() diff --git a/pkg/window/window_test.go b/pkg/window/window_test.go index f0b5d73..e5c8a0d 100644 --- a/pkg/window/window_test.go +++ b/pkg/window/window_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/wailsapp/wails/v3/pkg/application" ) func TestWindowDefaults(t *testing.T) { @@ -162,6 +163,21 @@ func TestManager_Remove_Good(t *testing.T) { assert.False(t, ok) } +func TestWailsWindow_DevToolsToggle_Good(t *testing.T) { + app := application.NewApp() + platform := NewWailsPlatform(app) + + pw := platform.CreateWindow(PlatformWindowOptions{Name: "devtools"}) + ww, ok := pw.(*wailsWindow) + require.True(t, ok) + + ww.OpenDevTools() + assert.True(t, ww.w.DevToolsOpen()) + + ww.CloseDevTools() + assert.False(t, ww.w.DevToolsOpen()) +} + // --- StateManager Tests --- // newTestStateManager creates a clean StateManager with a temp dir for testing. diff --git a/stubs/wails/v3/pkg/application/application.go b/stubs/wails/v3/pkg/application/application.go index db44bb3..c4ef368 100644 --- a/stubs/wails/v3/pkg/application/application.go +++ b/stubs/wails/v3/pkg/application/application.go @@ -136,6 +136,8 @@ func (w *WebviewWindow) Hide() { w.visible = false } func (w *WebviewWindow) Fullscreen() { w.fullscreen = true } func (w *WebviewWindow) UnFullscreen() { w.fullscreen = false } func (w *WebviewWindow) OpenDevTools() { w.devtoolsOpen = true } +func (w *WebviewWindow) CloseDevTools() { w.devtoolsOpen = false } +func (w *WebviewWindow) DevToolsOpen() bool { return w.devtoolsOpen } func (w *WebviewWindow) OnWindowEvent(eventType events.WindowEventType, callback func(event *WindowEvent)) func() { w.mu.Lock()