refactor(ax): add atomic window bounds task
This commit is contained in:
parent
bd4dfa8027
commit
5ce1d67f70
8 changed files with 98 additions and 9 deletions
|
|
@ -632,10 +632,9 @@ func (s *Service) SetWindowSize(name string, width, height int) error {
|
|||
|
||||
// SetWindowBounds sets both position and size of a window via IPC.
|
||||
func (s *Service) SetWindowBounds(name string, x, y, width, height int) error {
|
||||
if _, err := s.performWindowTask("display.SetWindowBounds", window.TaskSetPosition{Name: name, X: x, Y: y}); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := s.performWindowTask("display.SetWindowBounds", window.TaskSetSize{Name: name, Width: width, Height: height})
|
||||
_, err := s.performWindowTask("display.SetWindowBounds", window.TaskSetBounds{
|
||||
Name: name, X: x, Y: y, Width: width, Height: height,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -252,6 +252,21 @@ func TestSetWindowSize_Good(t *testing.T) {
|
|||
assert.Equal(t, 768, info.Height)
|
||||
}
|
||||
|
||||
func TestSetWindowBounds_Good(t *testing.T) {
|
||||
c := newTestConclave(t)
|
||||
svc := core.MustServiceFor[*Service](c, "display")
|
||||
_ = svc.OpenWindow(window.Window{Name: "bounds-win"})
|
||||
|
||||
err := svc.SetWindowBounds("bounds-win", 100, 200, 1024, 768)
|
||||
assert.NoError(t, err)
|
||||
|
||||
info, _ := svc.GetWindowInfo("bounds-win")
|
||||
assert.Equal(t, 100, info.X)
|
||||
assert.Equal(t, 200, info.Y)
|
||||
assert.Equal(t, 1024, info.Width)
|
||||
assert.Equal(t, 768, info.Height)
|
||||
}
|
||||
|
||||
func TestMaximizeWindow_Good(t *testing.T) {
|
||||
c := newTestConclave(t)
|
||||
svc := core.MustServiceFor[*Service](c, "display")
|
||||
|
|
|
|||
|
|
@ -165,11 +165,9 @@ type WindowBoundsOutput struct {
|
|||
}
|
||||
|
||||
func (s *Subsystem) windowBounds(_ context.Context, _ *mcp.CallToolRequest, input WindowBoundsInput) (*mcp.CallToolResult, WindowBoundsOutput, error) {
|
||||
_, _, err := s.core.PERFORM(window.TaskSetPosition{Name: input.Name, X: input.X, Y: input.Y})
|
||||
if err != nil {
|
||||
return nil, WindowBoundsOutput{}, err
|
||||
}
|
||||
_, _, err = s.core.PERFORM(window.TaskSetSize{Name: input.Name, Width: input.Width, Height: input.Height})
|
||||
_, _, err := s.core.PERFORM(window.TaskSetBounds{
|
||||
Name: input.Name, X: input.X, Y: input.Y, Width: input.Width, Height: input.Height,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, WindowBoundsOutput{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ type TaskSetPosition struct {
|
|||
X, Y int
|
||||
}
|
||||
|
||||
type TaskSetBounds struct {
|
||||
Name string
|
||||
X, Y int
|
||||
Width, Height int
|
||||
}
|
||||
|
||||
type TaskSetSize struct {
|
||||
Name string
|
||||
Width, Height int
|
||||
|
|
|
|||
|
|
@ -63,6 +63,20 @@ func TestStateManager_UpdateSize_Good(t *testing.T) {
|
|||
assert.Equal(t, 200, got.Y)
|
||||
}
|
||||
|
||||
func TestStateManager_UpdateBounds_Good(t *testing.T) {
|
||||
sm := NewStateManagerWithDir(t.TempDir())
|
||||
sm.SetState("win", WindowState{X: 100, Y: 200, Width: 800, Height: 600})
|
||||
|
||||
sm.UpdateBounds("win", 300, 400, 1280, 720)
|
||||
|
||||
got, ok := sm.GetState("win")
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 300, got.X)
|
||||
assert.Equal(t, 400, got.Y)
|
||||
assert.Equal(t, 1280, got.Width)
|
||||
assert.Equal(t, 720, got.Height)
|
||||
}
|
||||
|
||||
func TestStateManager_UpdateMaximized_Good(t *testing.T) {
|
||||
sm := NewStateManagerWithDir(t.TempDir())
|
||||
sm.SetState("win", WindowState{Width: 800, Height: 600, Maximized: false})
|
||||
|
|
|
|||
|
|
@ -134,6 +134,8 @@ func (s *Service) handleTask(c *core.Core, t core.Task) (any, bool, error) {
|
|||
return nil, true, s.taskCloseWindow(t.Name)
|
||||
case TaskSetPosition:
|
||||
return nil, true, s.taskSetPosition(t.Name, t.X, t.Y)
|
||||
case TaskSetBounds:
|
||||
return nil, true, s.taskSetBounds(t.Name, t.X, t.Y, t.Width, t.Height)
|
||||
case TaskSetSize:
|
||||
return nil, true, s.taskSetSize(t.Name, t.Width, t.Height)
|
||||
case TaskMaximize:
|
||||
|
|
@ -282,6 +284,17 @@ func (s *Service) taskSetPosition(name string, x, y int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) taskSetBounds(name string, x, y, width, height int) error {
|
||||
platformWindow, err := s.requireWindow(name, "window.Service.taskSetBounds")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
platformWindow.SetPosition(x, y)
|
||||
platformWindow.SetSize(width, height)
|
||||
s.manager.State().UpdateBounds(name, x, y, width, height)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) taskSetSize(name string, width, height int) error {
|
||||
platformWindow, err := s.requireWindow(name, "window.Service.taskSetSize")
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -177,6 +177,36 @@ func TestTaskSetSize_Good(t *testing.T) {
|
|||
assert.Equal(t, 600, info.Height)
|
||||
}
|
||||
|
||||
func TestTaskSetBounds_Good(t *testing.T) {
|
||||
_, c := newTestWindowService(t)
|
||||
_, _, _ = c.PERFORM(TaskOpenWindow{Window: Window{Name: "test"}})
|
||||
|
||||
_, handled, err := c.PERFORM(TaskSetBounds{
|
||||
Name: "test",
|
||||
X: 100,
|
||||
Y: 200,
|
||||
Width: 800,
|
||||
Height: 600,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, handled)
|
||||
|
||||
result, _, _ := c.QUERY(QueryWindowByName{Name: "test"})
|
||||
info := result.(*WindowInfo)
|
||||
assert.Equal(t, 100, info.X)
|
||||
assert.Equal(t, 200, info.Y)
|
||||
assert.Equal(t, 800, info.Width)
|
||||
assert.Equal(t, 600, info.Height)
|
||||
|
||||
states, _, _ := c.QUERY(QuerySavedWindowStates{})
|
||||
saved := states.(map[string]WindowState)
|
||||
require.Contains(t, saved, "test")
|
||||
assert.Equal(t, 100, saved["test"].X)
|
||||
assert.Equal(t, 200, saved["test"].Y)
|
||||
assert.Equal(t, 800, saved["test"].Width)
|
||||
assert.Equal(t, 600, saved["test"].Height)
|
||||
}
|
||||
|
||||
func TestTaskMaximize_Good(t *testing.T) {
|
||||
_, c := newTestWindowService(t)
|
||||
_, _, _ = c.PERFORM(TaskOpenWindow{Window: Window{Name: "test"}})
|
||||
|
|
|
|||
|
|
@ -164,6 +164,20 @@ func (sm *StateManager) UpdateSize(name string, width, height int) {
|
|||
sm.scheduleSave()
|
||||
}
|
||||
|
||||
// UpdateBounds updates position and size in one state write.
|
||||
func (sm *StateManager) UpdateBounds(name string, x, y, width, height int) {
|
||||
sm.mu.Lock()
|
||||
s := sm.states[name]
|
||||
s.X = x
|
||||
s.Y = y
|
||||
s.Width = width
|
||||
s.Height = height
|
||||
s.UpdatedAt = time.Now().UnixMilli()
|
||||
sm.states[name] = s
|
||||
sm.mu.Unlock()
|
||||
sm.scheduleSave()
|
||||
}
|
||||
|
||||
// UpdateMaximized updates the maximized flag.
|
||||
func (sm *StateManager) UpdateMaximized(name string, maximized bool) {
|
||||
sm.mu.Lock()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue