feat(window): add window opacity support
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
45fa6942f7
commit
4f4a4eb8e4
11 changed files with 79 additions and 1 deletions
|
|
@ -31,6 +31,7 @@ This document tracks the implementation of display server features that enable A
|
|||
- [x] `window_title_get` - Get current window title (returns window name)
|
||||
- [x] `window_always_on_top` - Pin window above others
|
||||
- [x] `window_background_colour` - Set window background color with alpha (transparency)
|
||||
- [x] `window_opacity` - Set window opacity
|
||||
- [x] `window_fullscreen` - Enter/exit fullscreen mode
|
||||
|
||||
---
|
||||
|
|
@ -235,7 +236,6 @@ This document tracks the implementation of display server features that enable A
|
|||
- [x] `tray_info` - Get tray status
|
||||
|
||||
### Phase 8 - Remaining Features (Future)
|
||||
- [ ] window_opacity (true opacity if Wails adds support)
|
||||
- [ ] layout_beside_editor, layout_suggest
|
||||
- [ ] webview_devtools_open, webview_devtools_close
|
||||
- [ ] clipboard_read_image, clipboard_write_image
|
||||
|
|
|
|||
|
|
@ -1168,6 +1168,15 @@ func (s *Service) SetWindowBackgroundColour(name string, r, g, b, a uint8) error
|
|||
return err
|
||||
}
|
||||
|
||||
// SetWindowOpacity updates a window's opacity via IPC.
|
||||
func (s *Service) SetWindowOpacity(name string, opacity float32) error {
|
||||
_, _, err := s.Core().PERFORM(window.TaskSetOpacity{
|
||||
Name: name,
|
||||
Opacity: opacity,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetFocusedWindow returns the name of the currently focused window.
|
||||
func (s *Service) GetFocusedWindow() string {
|
||||
infos := s.ListWindowInfos()
|
||||
|
|
|
|||
|
|
@ -356,6 +356,24 @@ func (s *Subsystem) windowBackgroundColour(_ context.Context, _ *mcp.CallToolReq
|
|||
return nil, WindowBackgroundColourOutput{Success: true}, nil
|
||||
}
|
||||
|
||||
// --- window_opacity ---
|
||||
|
||||
type WindowOpacityInput struct {
|
||||
Name string `json:"name"`
|
||||
Opacity float32 `json:"opacity"`
|
||||
}
|
||||
type WindowOpacityOutput struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) windowOpacity(_ context.Context, _ *mcp.CallToolRequest, input WindowOpacityInput) (*mcp.CallToolResult, WindowOpacityOutput, error) {
|
||||
_, _, err := s.core.PERFORM(window.TaskSetOpacity{Name: input.Name, Opacity: input.Opacity})
|
||||
if err != nil {
|
||||
return nil, WindowOpacityOutput{}, err
|
||||
}
|
||||
return nil, WindowOpacityOutput{Success: true}, nil
|
||||
}
|
||||
|
||||
// --- window_fullscreen ---
|
||||
|
||||
type WindowFullscreenInput struct {
|
||||
|
|
@ -394,5 +412,6 @@ func (s *Subsystem) registerWindowTools(server *mcp.Server) {
|
|||
mcp.AddTool(server, &mcp.Tool{Name: "window_visibility", Description: "Show or hide a window"}, s.windowVisibility)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "window_always_on_top", Description: "Pin a window above others"}, s.windowAlwaysOnTop)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "window_background_colour", Description: "Set a window background colour"}, s.windowBackgroundColour)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "window_opacity", Description: "Set a window opacity"}, s.windowOpacity)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "window_fullscreen", Description: "Set a window to fullscreen mode"}, s.windowFullscreen)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,6 +107,12 @@ type TaskSetBackgroundColour struct {
|
|||
Alpha uint8
|
||||
}
|
||||
|
||||
// TaskSetOpacity updates the window opacity as a value between 0 and 1.
|
||||
type TaskSetOpacity struct {
|
||||
Name string
|
||||
Opacity float32
|
||||
}
|
||||
|
||||
// TaskSetVisibility shows or hides a window.
|
||||
type TaskSetVisibility struct {
|
||||
Name string
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ type MockWindow struct {
|
|||
focused bool
|
||||
visible, alwaysOnTop bool
|
||||
backgroundColor [4]uint8
|
||||
opacity float32
|
||||
closed bool
|
||||
eventHandlers []func(WindowEvent)
|
||||
fileDropHandlers []func(paths []string, targetID string)
|
||||
|
|
@ -55,6 +56,7 @@ func (w *MockWindow) SetTitle(title string) { w.title = title }
|
|||
func (w *MockWindow) SetPosition(x, y int) { w.x = x; w.y = y }
|
||||
func (w *MockWindow) SetSize(width, height int) { w.width = width; w.height = height }
|
||||
func (w *MockWindow) SetBackgroundColour(r, g, b, a uint8) { w.backgroundColor = [4]uint8{r, g, b, a} }
|
||||
func (w *MockWindow) SetOpacity(opacity float32) { w.opacity = opacity }
|
||||
func (w *MockWindow) SetVisibility(visible bool) { w.visible = visible }
|
||||
func (w *MockWindow) SetAlwaysOnTop(alwaysOnTop bool) { w.alwaysOnTop = alwaysOnTop }
|
||||
func (w *MockWindow) Maximise() { w.maximised = true; w.minimised = false; w.visible = true }
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ type mockWindow struct {
|
|||
focused bool
|
||||
visible, alwaysOnTop bool
|
||||
backgroundColor [4]uint8
|
||||
opacity float32
|
||||
devtoolsOpen bool
|
||||
closed bool
|
||||
eventHandlers []func(WindowEvent)
|
||||
|
|
@ -55,6 +56,7 @@ func (w *mockWindow) SetTitle(title string) { w.title = title }
|
|||
func (w *mockWindow) SetPosition(x, y int) { w.x = x; w.y = y }
|
||||
func (w *mockWindow) SetSize(width, height int) { w.width = width; w.height = height }
|
||||
func (w *mockWindow) SetBackgroundColour(r, g, b, a uint8) { w.backgroundColor = [4]uint8{r, g, b, a} }
|
||||
func (w *mockWindow) SetOpacity(opacity float32) { w.opacity = opacity }
|
||||
func (w *mockWindow) SetVisibility(visible bool) { w.visible = visible }
|
||||
func (w *mockWindow) SetAlwaysOnTop(alwaysOnTop bool) { w.alwaysOnTop = alwaysOnTop }
|
||||
func (w *mockWindow) Maximise() { w.maximised = true; w.minimised = false; w.visible = true }
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ type PlatformWindow interface {
|
|||
SetPosition(x, y int)
|
||||
SetSize(width, height int)
|
||||
SetBackgroundColour(r, g, b, a uint8)
|
||||
SetOpacity(opacity float32)
|
||||
SetVisibility(visible bool)
|
||||
SetAlwaysOnTop(alwaysOnTop bool)
|
||||
|
||||
|
|
|
|||
|
|
@ -165,6 +165,8 @@ func (s *Service) handleTask(c *core.Core, t core.Task) (any, bool, error) {
|
|||
return nil, true, s.taskSetAlwaysOnTop(t.Name, t.AlwaysOnTop)
|
||||
case TaskSetBackgroundColour:
|
||||
return nil, true, s.taskSetBackgroundColour(t.Name, t.Red, t.Green, t.Blue, t.Alpha)
|
||||
case TaskSetOpacity:
|
||||
return nil, true, s.taskSetOpacity(t.Name, t.Opacity)
|
||||
case TaskSetVisibility:
|
||||
return nil, true, s.taskSetVisibility(t.Name, t.Visible)
|
||||
case TaskFullscreen:
|
||||
|
|
@ -371,6 +373,18 @@ func (s *Service) taskSetBackgroundColour(name string, red, green, blue, alpha u
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) taskSetOpacity(name string, opacity float32) error {
|
||||
if opacity < 0 || opacity > 1 {
|
||||
return fmt.Errorf("opacity must be between 0 and 1")
|
||||
}
|
||||
pw, ok := s.manager.Get(name)
|
||||
if !ok {
|
||||
return fmt.Errorf("window not found: %s", name)
|
||||
}
|
||||
pw.SetOpacity(opacity)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) taskSetVisibility(name string, visible bool) error {
|
||||
pw, ok := s.manager.Get(name)
|
||||
if !ok {
|
||||
|
|
|
|||
|
|
@ -218,6 +218,29 @@ func TestTaskSetBackgroundColour_Good(t *testing.T) {
|
|||
assert.Equal(t, [4]uint8{10, 20, 30, 40}, pw.(*mockWindow).backgroundColor)
|
||||
}
|
||||
|
||||
func TestTaskSetOpacity_Good(t *testing.T) {
|
||||
_, c := newTestWindowService(t)
|
||||
_, _, _ = c.PERFORM(TaskOpenWindow{Opts: []WindowOption{WithName("test")}})
|
||||
|
||||
_, handled, err := c.PERFORM(TaskSetOpacity{Name: "test", Opacity: 0.65})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, handled)
|
||||
|
||||
svc := core.MustServiceFor[*Service](c, "window")
|
||||
pw, ok := svc.Manager().Get("test")
|
||||
require.True(t, ok)
|
||||
assert.InDelta(t, 0.65, pw.(*mockWindow).opacity, 0.0001)
|
||||
}
|
||||
|
||||
func TestTaskSetOpacity_BadRange(t *testing.T) {
|
||||
_, c := newTestWindowService(t)
|
||||
_, _, _ = c.PERFORM(TaskOpenWindow{Opts: []WindowOption{WithName("test")}})
|
||||
|
||||
_, handled, err := c.PERFORM(TaskSetOpacity{Name: "test", Opacity: 1.5})
|
||||
require.Error(t, err)
|
||||
assert.True(t, handled)
|
||||
}
|
||||
|
||||
func TestTaskStackWindows_Good(t *testing.T) {
|
||||
_, c := newTestWindowService(t)
|
||||
_, _, _ = c.PERFORM(TaskOpenWindow{Opts: []WindowOption{WithName("one")}})
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ func (ww *wailsWindow) SetSize(width, height int) { ww.w.SetSize(width, height)
|
|||
func (ww *wailsWindow) SetBackgroundColour(r, g, b, a uint8) {
|
||||
ww.w.SetBackgroundColour(application.NewRGBA(r, g, b, a))
|
||||
}
|
||||
func (ww *wailsWindow) SetOpacity(opacity float32) { ww.w.SetOpacity(opacity) }
|
||||
func (ww *wailsWindow) SetVisibility(visible bool) {
|
||||
if visible {
|
||||
ww.w.Show()
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ func (w *WebviewWindow) SetSize(width, height int) {
|
|||
w.width, w.height = width, height
|
||||
}
|
||||
func (w *WebviewWindow) SetBackgroundColour(colour RGBA) {}
|
||||
func (w *WebviewWindow) SetOpacity(opacity float32) {}
|
||||
func (w *WebviewWindow) SetVisibility(visible bool) { w.visible = visible }
|
||||
func (w *WebviewWindow) SetAlwaysOnTop(alwaysOnTop bool) { w.alwaysOnTop = alwaysOnTop }
|
||||
func (w *WebviewWindow) Maximise() { w.maximised = true; w.minimised = false; w.visible = true }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue