From 3e3d9261c760e37c04c5ffc0581e85205a6c6f10 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 8 Feb 2025 13:50:15 +0000 Subject: [PATCH] Update to use InnerWindow for the border painting Apply our position setting for real and embedded border buttons. --- go.mod | 10 +-- go.sum | 20 ++--- internal/x11/win/frame.go | 5 +- wm/border.go | 181 ++++++++------------------------------ 4 files changed, 54 insertions(+), 162 deletions(-) diff --git a/go.mod b/go.mod index 67906bbc..feaa77c2 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module fyshos.com/fynedesk go 1.19 require ( - fyne.io/fyne/v2 v2.5.3 + fyne.io/fyne/v2 v2.5.5-0.20250208132135-967702162d53 github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e github.com/FyshOS/appie v0.0.0-20250103211310-00f097d8e19d @@ -20,7 +20,7 @@ require ( github.com/BurntSushi/toml v1.4.0 // indirect github.com/fyne-io/image v0.0.0-20240417123036-dc0ee9e7c964 // indirect github.com/jackmordaunt/icns v1.0.1-0.20200413110149-9e181b441ab2 // indirect - github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 // indirect + github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 // indirect github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect github.com/rymdport/portal v0.3.0 // indirect howett.net/plist v1.0.1 // indirect @@ -50,11 +50,11 @@ require ( github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect github.com/yuin/goldmark v1.7.1 // indirect - golang.org/x/image v0.18.0 // indirect + golang.org/x/image v0.23.0 // indirect golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.16.0 + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.21.0 gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index df0ea84a..780f9461 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -fyne.io/fyne/v2 v2.5.3 h1:k6LjZx6EzRZhClsuzy6vucLZBstdH2USDGHSGWq8ly8= -fyne.io/fyne/v2 v2.5.3/go.mod h1:0GOXKqyvNwk3DLmsFu9v0oYM0ZcD1ysGnlHCerKoAmo= +fyne.io/fyne/v2 v2.5.5-0.20250208132135-967702162d53 h1:qXq2uL+bCtlg9uNnlXGdj+9eamAjMhuL4fQVDJrHVqo= +fyne.io/fyne/v2 v2.5.5-0.20250208132135-967702162d53/go.mod h1:6/uEYg4FEhspAcWgsokutm9wFMHDNSYuEHCKTYWSho8= fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= github.com/ActiveState/termtest/conpty v0.5.0 h1:JLUe6YDs4Jw4xNPCU+8VwTpniYOGeKzQg4SM2YHQNA8= @@ -221,8 +221,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackmordaunt/icns v1.0.1-0.20200413110149-9e181b441ab2 h1:2bRhR5GcMudCdaY4p8ip89hsvSyxYehLSicCNtygyVY= github.com/jackmordaunt/icns v1.0.1-0.20200413110149-9e181b441ab2/go.mod h1:Hj3TV9xrdt+g9apvBagVi/VzE41gSliEBypxaQDq5QA= -github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 h1:Po+wkNdMmN+Zj1tDsJQy7mJlPlwGNQd9JZoPjObagf8= -github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49/go.mod h1:YiutDnxPRLk5DLUFj6Rw4pRBBURZY07GFr54NdV9mQg= +github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc= +github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -350,8 +350,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= -golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= +golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -486,8 +486,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -497,8 +497,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/internal/x11/win/frame.go b/internal/x11/win/frame.go index c75c7a98..4beccf2f 100644 --- a/internal/x11/win/frame.go +++ b/internal/x11/win/frame.go @@ -381,6 +381,9 @@ func (f *frame) drawDecoration(pidTop xproto.Pixmap, drawTop xproto.Gcontext, pi f.canvas = canvas } else { b := f.canvas.Content().(*wm.Border) + b.CloseIntercept = func() { + f.client.Close() + } b.SetTitle(f.client.props.Title()) b.SetMaximized(f.client.maximized) b.SetIcon(f.client.Properties().Icon()) @@ -394,7 +397,7 @@ func (f *frame) drawDecoration(pidTop xproto.Pixmap, drawTop xproto.Gcontext, pi winPixWidth := f.borderTopWidth + rightWidthPix winPtWidth := float32(winPixWidth) / scale drawWidth := fyne.Max(minWidth, winPtWidth) - f.canvas.Resize(fyne.NewSize(drawWidth, wmTheme.TitleHeight)) + f.canvas.Resize(fyne.NewSize(drawWidth, wmTheme.TitleHeight+16)) widthPix := uint16(drawWidth*f.canvas.Scale()) - rightWidthPix img := f.canvas.Capture() diff --git a/wm/border.go b/wm/border.go index 67a3f38d..65c88a29 100644 --- a/wm/border.go +++ b/wm/border.go @@ -4,107 +4,69 @@ import ( "fmt" "image/color" + "fyshos.com/fynedesk" "fyshos.com/fynedesk/internal/icon" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" - - "fyshos.com/fynedesk" - wmTheme "fyshos.com/fynedesk/theme" ) -// makeFiller creates a 0-width object to force padding on the edge of a container -func makeFiller() fyne.CanvasObject { - filler := canvas.NewRectangle(color.Transparent) - filler.SetMinSize(fyne.NewSize(0, 2)) - - return filler -} - // NewBorder creates a new window border for the given window details func NewBorder(win fynedesk.Window, ico fyne.Resource, canMaximize bool) *Border { desk := fynedesk.Instance() + border := &Border{win: win} + border.ExtendBaseWidget(border) + border.SetTitle(win.Properties().Title()) + border.SetContent(canvas.NewRectangle(color.Transparent)) + height := border.Theme().Size(theme.SizeNameWindowTitleBarHeight) if ico == nil { iconTheme := desk.Settings().IconTheme() app := icon.FindAppFromWinInfo(win, desk.IconProvider()) if app != nil { - ico = app.Icon(iconTheme, int(wmTheme.TitleHeight*2)) + ico = app.Icon(iconTheme, int(height*2)) } } - max := &widget.Button{Icon: wmTheme.MaximizeIcon, Importance: widget.LowImportance, OnTapped: func() { - if win.Maximized() { - win.Unmaximize() - } else { - win.Maximize() + if canMaximize { + border.OnMaximized = func() { + if win.Maximized() { + win.Unmaximize() + } else { + win.Maximize() + } } - }} - - if win.Maximized() { - max.Icon = theme.ViewRestoreIcon() } - if !canMaximize { - max.Disable() + if win.Maximized() { + border.SetMaximized(true) } - min := &widget.Button{Icon: wmTheme.IconifyIcon, Importance: widget.LowImportance, OnTapped: func() { + border.OnMinimized = func() { win.Iconify() - }} - - title := canvas.NewText(win.Properties().Title(), theme.Color(theme.ColorNameForeground)) - buttonPos := fynedesk.Instance().Settings().BorderButtonPosition() - - var titleBar *Border - if buttonPos == "Right" { - titleBar = newColoredHBox(win.Focused(), win, - title, - layout.NewSpacer(), - min, - max, - newCloseButton(win), - makeFiller(), - ) - } else { - titleBar = newColoredHBox(win.Focused(), win, makeFiller(), - newCloseButton(win), - max, - min, - title, - layout.NewSpacer(), - ) } - titleBar.title = title - titleBar.max = max - - appIcon := &widget.Button{Icon: ico, Importance: widget.LowImportance} - appIcon.OnTapped = func() { - titleBar.showMenu(appIcon) + buttonAlign := widget.ButtonAlignLeading + if fynedesk.Instance().Settings().BorderButtonPosition() == "Right" { + buttonAlign = widget.ButtonAlignTrailing } + border.Alignment = buttonAlign - if buttonPos == "Right" { - titleBar.prepend(appIcon) - titleBar.prepend(makeFiller()) - } else { - titleBar.append(appIcon) - titleBar.append(makeFiller()) + border.OnTappedIcon = func() { + border.showMenu(border) // TODO appIcon position (estimate) } - titleBar.appIcon = appIcon - return titleBar + + border.Icon = ico + border.Refresh() + return border } // Border represents a window border. It draws the title bar and provides functions to manipulate it. type Border struct { - widget.BaseWidget + container.InnerWindow content *fyne.Container - appIcon *widget.Button - title *canvas.Text - max *widget.Button win fynedesk.Window } @@ -117,18 +79,19 @@ func (c *Border) DoubleTapped(*fyne.PointEvent) { c.win.Maximize() } -func (c *Border) prepend(obj fyne.CanvasObject) { - c.content.Objects = append([]fyne.CanvasObject{obj}, c.content.Objects...) +// SetIcon updates the icon used in the window border. +func (c *Border) SetIcon(icon fyne.Resource) { + c.Icon = icon c.Refresh() } -func (c *Border) append(obj fyne.CanvasObject) { - c.content.Add(obj) - c.Refresh() +func (c *Border) Hide() { + c.win.Close() + c.InnerWindow.Hide() } func (c *Border) showMenu(from fyne.CanvasObject) { - name := c.title.Text + name := c.win.Properties().Title() if len(name) > 25 { name = name[:25] + "..." } @@ -196,77 +159,3 @@ func (c *Border) makeDesktopMenu(pos fyne.Position) *fyne.MenuItem { ret.ChildMenu = fyne.NewMenu("") // No-op to add the arrow... return ret } - -// CreateRenderer creates a new renderer for this border -// -// Implements: fyne.Widget -func (c *Border) CreateRenderer() fyne.WidgetRenderer { - render := &coloredBoxRenderer{b: c, bg: canvas.NewRectangle(theme.Color(theme.ColorNameBackground))} - return render -} - -// SetIcon tells the border to change the icon that should be used -func (c *Border) SetIcon(icon fyne.Resource) { - if icon == nil { - c.appIcon.Icon = nil - return - } - - c.appIcon.Icon = icon -} - -// SetMaximized updates the state of the border maximize indicators and refreshes -func (c *Border) SetMaximized(isMax bool) { - if isMax { - c.max.Icon = theme.ViewRestoreIcon() - } else { - c.max.Icon = wmTheme.MaximizeIcon - } -} - -// SetTitle updates the title portion of this border and refreshes. -func (c *Border) SetTitle(title string) { - c.title.Text = title -} - -func newColoredHBox(focused bool, win fynedesk.Window, objs ...fyne.CanvasObject) *Border { - ret := &Border{win: win} - ret.content = container.NewHBox(objs...) - ret.ExtendBaseWidget(ret) - - return ret -} - -type coloredBoxRenderer struct { - b *Border - bg *canvas.Rectangle -} - -func (r *coloredBoxRenderer) Destroy() { -} - -func (r *coloredBoxRenderer) Layout(size fyne.Size) { - r.bg.Resize(size) - r.b.content.Resize(size) -} - -func (r *coloredBoxRenderer) MinSize() fyne.Size { - return r.b.content.MinSize() -} - -func (r *coloredBoxRenderer) Objects() []fyne.CanvasObject { - return []fyne.CanvasObject{r.bg, r.b.content} -} - -func (r *coloredBoxRenderer) Refresh() { - r.bg.Resize(r.b.Size()) // Not sure why this resize is needed, but it is... - if r.b.win.Focused() { - r.bg.FillColor = theme.Color(theme.ColorNameBackground) - } else { - r.bg.FillColor = theme.Color(theme.ColorNameDisabledButton) - } - r.bg.Refresh() - - r.b.title.Color = theme.Color(theme.ColorNameForeground) - r.b.content.Refresh() -}