diff --git a/desk.go b/desk.go index 1d30348e..05a1ad50 100644 --- a/desk.go +++ b/desk.go @@ -9,6 +9,7 @@ type Desktop interface { RecentApps() []AppData Settings() DeskSettings ContentBoundsPixels(*Screen) (x, y, w, h uint32) + RootSizePixels() (w, h uint32) Screens() ScreenList IconProvider() ApplicationProvider diff --git a/internal/notify/desktop.go b/internal/notify/desktop.go new file mode 100644 index 00000000..1ed3ce33 --- /dev/null +++ b/internal/notify/desktop.go @@ -0,0 +1,6 @@ +package notify + +// DesktopNotify allows modules to be informed when user changes virtual desktop +type DesktopNotify interface { + DesktopChangeNotify(int) +} diff --git a/internal/ui/desk.go b/internal/ui/desk.go index dbd4d09e..32d919b4 100644 --- a/internal/ui/desk.go +++ b/internal/ui/desk.go @@ -5,7 +5,10 @@ import ( "os/exec" "strconv" + "fyshos.com/fynedesk/internal/notify" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" deskDriver "fyne.io/fyne/v2/driver/desktop" @@ -46,13 +49,35 @@ func (l *desktop) Desktop() int { } func (l *desktop) SetDesktop(id int) { + diff := id - l.desk l.desk = id - for _, item := range l.wm.Windows() { - if item.Desktop() == id { - item.Uniconify() - } else { - item.Iconify() + _, height := l.RootSizePixels() + offPix := float32(diff * -int(height)) + wins := l.wm.Windows() + + starts := make([]fyne.Position, len(wins)) + deltas := make([]fyne.Delta, len(wins)) + for i, win := range wins { + starts[i] = win.Position() + + display := l.Screens().ScreenForWindow(win) + off := offPix / display.Scale + deltas[i] = fyne.NewDelta(0, off) + } + + fyne.NewAnimation(canvas.DurationStandard, func(f float32) { + for i, item := range l.wm.Windows() { + newX := starts[i].X + deltas[i].DX*f + newY := starts[i].Y + deltas[i].DY*f + + item.Move(fyne.NewPos(newX, newY)) + } + }).Start() + + for _, m := range l.Modules() { + if desk, ok := m.(notify.DesktopNotify); ok { + desk.DesktopChangeNotify(id) } } } @@ -171,6 +196,22 @@ func (l *desktop) ContentBoundsPixels(screen *fynedesk.Screen) (x, y, w, h uint3 return 0, 0, screenW, screenH } +func (l *desktop) RootSizePixels() (w, h uint32) { + for _, screen := range l.Screens().Screens() { + right := uint32(screen.X + screen.Width) + bottom := uint32(screen.Y + screen.Height) + + if right > w { + w = right + } + if bottom > h { + h = bottom + } + } + + return w, h +} + func (l *desktop) IconProvider() fynedesk.ApplicationProvider { return l.icons } diff --git a/internal/x11/win/client.go b/internal/x11/win/client.go index 7aa97918..3106b6ba 100644 --- a/internal/x11/win/client.go +++ b/internal/x11/win/client.go @@ -6,14 +6,15 @@ package win import ( "image" - "fyne.io/fyne/v2" - "github.com/BurntSushi/xgb/xproto" "github.com/BurntSushi/xgbutil/ewmh" "github.com/BurntSushi/xgbutil/icccm" "github.com/BurntSushi/xgbutil/xevent" "github.com/BurntSushi/xgbutil/xprop" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyshos.com/fynedesk" "fyshos.com/fynedesk/internal/x11" "fyshos.com/fynedesk/wm" @@ -125,12 +126,20 @@ func (c *client) SetDesktop(id int) { } d := fynedesk.Instance() + diff := id - c.desk c.desk = id - if id == d.Desktop() { - c.Uniconify() - } else { - c.Iconify() - } + + _, height := d.RootSizePixels() + offPix := float32(diff * -int(height)) + display := d.Screens().ScreenForWindow(c) + off := offPix / display.Scale + + start := c.Position() + fyne.NewAnimation(canvas.DurationStandard, func(f float32) { + newY := start.Y + off*f + + c.Move(fyne.NewPos(start.X, newY)) + }).Start() } func (c *client) Expose() { @@ -193,6 +202,14 @@ func (c *client) Maximized() bool { return c.maximized } +func (c *client) Move(pos fyne.Position) { + screen := fynedesk.Instance().Screens().ScreenForWindow(c) + + targetX := int16(pos.X * screen.CanvasScale()) + targetY := int16(pos.Y * screen.CanvasScale()) + c.frame.updateGeometry(targetX, targetY, c.frame.width, c.frame.height, false) +} + func (c *client) NotifyBorderChange() { c.props.refreshCache() if c.Properties().Decorated() { diff --git a/modules/desktops/desktops.go b/modules/desktops/desktops.go index 4d518a6a..d3d89bd7 100644 --- a/modules/desktops/desktops.go +++ b/modules/desktops/desktops.go @@ -20,6 +20,11 @@ type desktops struct { gui *pager } +func (d *desktops) DesktopChangeNotify(id int) { + d.current = id + d.gui.refresh() +} + func (d *desktops) Destroy() { } diff --git a/test/desktop.go b/test/desktop.go index 7a10e3d1..af53ac11 100644 --- a/test/desktop.go +++ b/test/desktop.go @@ -45,7 +45,12 @@ func (*Desktop) Capture() image.Image { // ContentBoundsPixels returns a default value for how much space maximised apps should use func (*Desktop) ContentBoundsPixels(_ *fynedesk.Screen) (x, y, w, h uint32) { - return 0, 0, uint32(320), uint32(240) + return 0, 0, 320, 240 +} + +// RootSizePixels returns the total number of pixels required to fit all the screens +func (*Desktop) RootSizePixels() (w, h uint32) { + return 320, 240 } // Desktop returns the index of the current desktop (in test this is always 0) diff --git a/test/window.go b/test/window.go index f518d801..1842f6a3 100644 --- a/test/window.go +++ b/test/window.go @@ -3,8 +3,9 @@ package test import ( "image" - "fyne.io/fyne/v2" "fyshos.com/fynedesk" + + "fyne.io/fyne/v2" ) // Window is an in-memory virtual window for test purposes @@ -80,6 +81,9 @@ func (w *Window) Maximized() bool { return false } +// Move the window, does nothing in test windows +func (w *Window) Move(fyne.Position) {} + // Parent returns a window that this should be positioned within, if set. func (w *Window) Parent() fynedesk.Window { return w.parent diff --git a/window.go b/window.go index 1c5c6758..0761655a 100644 --- a/window.go +++ b/window.go @@ -30,6 +30,7 @@ type Window interface { Parent() Window Properties() WindowProperties // Request the properties set on this window Position() fyne.Position + Move(position fyne.Position) Desktop() int SetDesktop(int)