Skip to content

Commit

Permalink
Update to use InnerWindow for the border painting
Browse files Browse the repository at this point in the history
Apply our position setting for real and embedded border buttons.
  • Loading branch information
andydotxyz committed Feb 8, 2025
1 parent b38a711 commit 3e3d926
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 162 deletions.
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
)

Expand Down
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand All @@ -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=
Expand Down
5 changes: 4 additions & 1 deletion internal/x11/win/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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()

Expand Down
181 changes: 35 additions & 146 deletions wm/border.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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() {

Check failure on line 88 in wm/border.go

View workflow job for this annotation

GitHub Actions / checks

exported method Border.Hide should have comment or be unexported
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] + "..."
}
Expand Down Expand Up @@ -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()
}

0 comments on commit 3e3d926

Please sign in to comment.