diff --git a/internal/ui/desk.go b/internal/ui/desk.go index 73830626..6324f01f 100644 --- a/internal/ui/desk.go +++ b/internal/ui/desk.go @@ -68,8 +68,7 @@ func (l *desktop) SetDesktop(id int) { fyne.NewAnimation(canvas.DurationStandard, func(f float32) { for i, item := range l.wm.Windows() { - // TODO move this to floating once we support them - if item.Properties().SkipTaskbar() { + if item.Pinned() { continue } diff --git a/internal/x11/win/client.go b/internal/x11/win/client.go index 922202a6..4fe1a167 100644 --- a/internal/x11/win/client.go +++ b/internal/x11/win/client.go @@ -26,6 +26,7 @@ type client struct { full bool iconic bool maximized bool + pinned bool props *clientProperties restoreX, restoreY int16 @@ -129,6 +130,10 @@ func (c *client) SetDesktop(id int) { diff := id - c.desk c.desk = id + if c.pinned { + return + } + _, height := d.RootSizePixels() offPix := float32(diff * -int(height)) display := d.Screens().ScreenForWindow(c) @@ -312,6 +317,16 @@ func (c *client) Parent() fynedesk.Window { return nil } +func (c *client) Pin() { + c.pinned = true + d := fynedesk.Instance() + c.SetDesktop(d.Desktop()) +} + +func (c *client) Pinned() bool { + return c.pinned +} + func (c *client) Position() fyne.Position { screen := fynedesk.Instance().Screens().ScreenForWindow(c) @@ -409,6 +424,15 @@ func (c *client) Unmaximize() { c.maximizeMessage(x11.WindowStateActionRemove) } +func (c *client) Unpin() { + c.pinned = false + d := fynedesk.Instance() + id := d.Desktop() + c.desk = id + + c.SetDesktop(id) +} + func (c *client) fullscreenMessage(action x11.WindowStateAction) { err := ewmh.WmStateReq(c.wm.X(), c.win, int(action), "_NET_WM_STATE_FULLSCREEN") if err != nil { diff --git a/modules/desktops/pager.go b/modules/desktops/pager.go index e21dd8d1..57ce7a21 100644 --- a/modules/desktops/pager.go +++ b/modules/desktops/pager.go @@ -91,8 +91,13 @@ func (p *pager) refreshFrom(oldID int) { continue } - yPad := theme.Padding() * float32(win.Desktop()-oldID) + deskID := win.Desktop() + yPad := theme.Padding() * float32(deskID-oldID) screen := fynedesk.Instance().Screens().ScreenForWindow(win) + if win.Pinned() { + yPad = theme.Padding() * float32(desk.Desktop()-oldID) + yPad -= float32(oldID-desk.Desktop()) * pivot.Size().Height + } var obj fyne.CanvasObject obj = canvas.NewRectangle(theme.DisabledColor()) diff --git a/modules/quaketerm/term.go b/modules/quaketerm/term.go index f24886cb..12e72e18 100644 --- a/modules/quaketerm/term.go +++ b/modules/quaketerm/term.go @@ -139,6 +139,7 @@ func (t *term) toggle() { if !t.shown { t.win = t.getHandle() + t.win.Pin() t.show() } else { t.hide() diff --git a/test/window.go b/test/window.go index 7ae6f133..acd48242 100644 --- a/test/window.go +++ b/test/window.go @@ -12,7 +12,7 @@ import ( type Window struct { props dummyProperties - iconic, focused, fullscreen, maximized, raised bool + iconic, focused, fullscreen, maximized, raised, pinned bool parent fynedesk.Window x, y, desk int @@ -89,6 +89,16 @@ func (w *Window) Parent() fynedesk.Window { return w.parent } +// Pin requests that the window be visible on all desktops +func (w *Window) Pin() { + w.pinned = true +} + +// Pinned returns true if the window should be visible on all desktops +func (w *Window) Pinned() bool { + return w.pinned +} + // Position returns 0, 0 for test windows func (w *Window) Position() fyne.Position { return fyne.NewPos(0, 0) @@ -165,3 +175,9 @@ func (w *Window) Uniconify() { func (w *Window) Unmaximize() { w.maximized = false } + +// Unpin resets the state of being visible on all windows. +// The window will return to being visible on its specified desktop. +func (w *Window) Unpin() { + w.pinned = false +} diff --git a/window.go b/window.go index 2d2188f4..18c6cca2 100644 --- a/window.go +++ b/window.go @@ -36,6 +36,9 @@ type Window interface { Desktop() int SetDesktop(int) + Pin() + Pinned() bool + Unpin() } // WindowProperties encapsulates the metadata that a window can provide. diff --git a/wm/border.go b/wm/border.go index b9530dad..2e394ca2 100644 --- a/wm/border.go +++ b/wm/border.go @@ -142,6 +142,9 @@ func (c *Border) showMenu(from fyne.CanvasObject) { if c.win.Maximized() { max.Checked = true } + + pos := c.win.Position() + menuPos := pos.Add(from.Position()) menu := fyne.NewMenu("", title, fyne.NewMenuItemSeparator(), @@ -150,26 +153,45 @@ func (c *Border) showMenu(from fyne.CanvasObject) { }), max, fyne.NewMenuItemSeparator(), - c.makeDesktopMenu(), + c.makeDesktopMenu(menuPos), fyne.NewMenuItemSeparator(), fyne.NewMenuItem("Close", func() { c.win.Close() })) - pos := c.win.Position() - fynedesk.Instance().ShowMenuAt(menu, pos.Add(from.Position())) + fynedesk.Instance().ShowMenuAt(menu, menuPos) } -func (c *Border) makeDesktopMenu() *fyne.MenuItem { +func (c *Border) makeDesktopMenu(pos fyne.Position) *fyne.MenuItem { desks := make([]*fyne.MenuItem, 4) for i := 0; i < 4; i++ { deskID := i desks[i] = fyne.NewMenuItem(fmt.Sprintf("Desktop %d", i+1), func() { + if c.win.Pinned() { + c.win.Unpin() + } + c.win.SetDesktop(deskID) }) } - ret := fyne.NewMenuItem("Move to Desktop", nil) - ret.ChildMenu = fyne.NewMenu("", desks...) + pin := fyne.NewMenuItem("All Desktops", func() { + if c.win.Pinned() { + return + } + + c.win.Pin() + }) + if c.win.Pinned() { + pin.Checked = true + } + desks = append(desks, pin) + + ret := fyne.NewMenuItem("Move to Desktop", func() { + fynedesk.Instance().ShowMenuAt(fyne.NewMenu("", desks...), + pos.Add(fyne.NewSize(40, 120))) + + }) + ret.ChildMenu = fyne.NewMenu("") // No-op to add the arrow... return ret }