Skip to content

Commit dda2d5c

Browse files
committed
Ubuntu background on new branch fixed
1 parent 5d298b7 commit dda2d5c

6 files changed

Lines changed: 441 additions & 116 deletions

File tree

internal/ui/drag_linux.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ var (
1919

2020
// enableWindowDrag enables drag-to-reposition detection on Linux.
2121
//
22-
// Since the title bar is removed, most window managers still allow moving
23-
// via Alt+left-click drag or Super+drag. This function starts a background
24-
// poller that detects when the window position changes and calls onDragEnd
25-
// so the caller can persist the new coordinates.
22+
// On Wayland, direct window-move interception is not possible through the
23+
// protocol. Instead we poll the window position (via wmctrl or xdotool)
24+
// and detect when the user has moved the window using the compositor's
25+
// built-in drag mechanism (Super+drag or Alt+drag on GNOME).
2626
//
27-
// Requires xdotool to be installed.
27+
// On X11, the same polling approach is used with xdotool.
2828
func enableWindowDrag(onDragEnd func()) {
2929
linuxDragMu.Lock()
3030
defer linuxDragMu.Unlock()
@@ -39,7 +39,12 @@ func enableWindowDrag(onDragEnd func()) {
3939
linuxLastX, linuxLastY = getWindowPosition()
4040

4141
go pollWindowPosition(linuxDragStop)
42-
log.Println("Linux drag: position poller started")
42+
43+
if isWayland() {
44+
log.Println("Linux/Wayland: drag position poller started (use Super+drag or Alt+drag to reposition)")
45+
} else {
46+
log.Println("Linux/X11: drag position poller started")
47+
}
4348
}
4449

4550
// notifyLinuxMoveByUs should be called before programmatic moves so the
@@ -71,7 +76,6 @@ func pollWindowPosition(stop chan struct{}) {
7176
linuxDragMu.Unlock()
7277

7378
if movedByUs {
74-
// Update last known position but don't fire callback.
7579
linuxDragMu.Lock()
7680
linuxLastX = x
7781
linuxLastY = y
@@ -86,7 +90,7 @@ func pollWindowPosition(stop chan struct{}) {
8690
linuxDragMu.Unlock()
8791

8892
if cb != nil {
89-
log.Printf("Linux drag: position changed from (%d,%d) to (%d,%d), saving", lastX, lastY, x, y)
93+
log.Printf("Linux: position changed from (%d,%d) to (%d,%d), saving", lastX, lastY, x, y)
9094
cb()
9195
}
9296
}

internal/ui/manager.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,11 @@ type UIManager struct {
2424
}
2525

2626
// NewUIManager creates a new UIManager and its main widget window.
27-
// After the window is shown, call applyToolWindowStyle to set Win32
28-
// WS_EX_TOOLWINDOW and HWND_TOPMOST styles (no-op on non-Windows).
27+
// On Linux the window is created without decorations (borderless) via
28+
// CreateSplashWindow. On Windows decorations are removed post-creation
29+
// by applyToolWindowStyle using Win32 API calls.
2930
func NewUIManager(app fyne.App, lm *i18n.LocaleManager) *UIManager {
30-
w := app.NewWindow(widgetTitle)
31-
w.SetFixedSize(true)
32-
w.SetPadded(false)
31+
w := createWidgetWindow(app, widgetTitle)
3332

3433
return &UIManager{
3534
app: app,

internal/ui/theme.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ui
22

33
import (
44
"image/color"
5+
"runtime"
56
"sync/atomic"
67

78
"fyne.io/fyne/v2"
@@ -16,6 +17,14 @@ var transparencyKey = color.NRGBA{R: 1, G: 1, B: 1, A: 255}
1617
// transparencyActive is 1 when a color-key background should be used.
1718
var transparencyActive atomic.Int32
1819

20+
// linuxBgShade stores the background RGB value for Linux (0=black, 255=white).
21+
// Controlled by the opacity setting: 100% = fully dark (30), 25% = lighter (90).
22+
var linuxBgShade atomic.Int32
23+
24+
func init() {
25+
linuxBgShade.Store(30) // default: dark background
26+
}
27+
1928
// SetTransparencyActive switches the background color key on or off.
2029
func SetTransparencyActive(active bool) {
2130
if active {
@@ -25,8 +34,32 @@ func SetTransparencyActive(active bool) {
2534
}
2635
}
2736

28-
// widgetTheme is a Fyne theme that replaces the window background with the
29-
// color key when transparency is active, leaving all other colors unchanged.
37+
// SetLinuxBackgroundShade sets the Linux background darkness level.
38+
// opacityPercent maps to background shade:
39+
//
40+
// 100% → RGB(30,30,30) — fully dark, content most visible
41+
// 75% → RGB(50,50,50) — slightly lighter
42+
// 50% → RGB(70,70,70) — medium grey
43+
// 25% → RGB(90,90,90) — lighter grey
44+
func SetLinuxBackgroundShade(opacityPercent int) {
45+
var shade int
46+
switch {
47+
case opacityPercent >= 100:
48+
shade = 30
49+
case opacityPercent >= 75:
50+
shade = 50
51+
case opacityPercent >= 50:
52+
shade = 70
53+
default:
54+
shade = 90
55+
}
56+
linuxBgShade.Store(int32(shade))
57+
}
58+
59+
// widgetTheme is a Fyne theme that:
60+
// - On Windows: replaces the background with a color-key when transparency is active
61+
// - On Linux: always uses a dark background (ignoring system light/dark preference)
62+
// to ensure the widget looks consistent and native
3063
type widgetTheme struct {
3164
base fyne.Theme
3265
}
@@ -37,12 +70,33 @@ func NewWidgetTheme(base fyne.Theme) fyne.Theme {
3770
}
3871

3972
func (t *widgetTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
73+
// Windows: color-key transparency when active.
4074
if transparencyActive.Load() == 1 {
4175
switch name {
4276
case theme.ColorNameBackground, theme.ColorNameOverlayBackground:
4377
return transparencyKey
4478
}
4579
}
80+
81+
// Linux: always use dark variant colors for a consistent widget appearance,
82+
// regardless of the system theme (light/dark). The background shade is
83+
// controlled by the opacity setting.
84+
if runtime.GOOS == "linux" {
85+
switch name {
86+
case theme.ColorNameBackground, theme.ColorNameOverlayBackground:
87+
shade := uint8(linuxBgShade.Load())
88+
return color.NRGBA{R: shade, G: shade, B: shade, A: 255}
89+
case theme.ColorNameForeground:
90+
return color.NRGBA{R: 255, G: 255, B: 255, A: 255}
91+
case theme.ColorNameDisabled:
92+
return color.NRGBA{R: 180, G: 180, B: 180, A: 255}
93+
case theme.ColorNameSeparator:
94+
return color.NRGBA{R: 80, G: 80, B: 80, A: 255}
95+
}
96+
// For all other colors, force dark variant.
97+
return t.base.Color(name, theme.VariantDark)
98+
}
99+
46100
return t.base.Color(name, variant)
47101
}
48102

internal/ui/window_linux.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//go:build linux
2+
3+
package ui
4+
5+
import (
6+
"log"
7+
8+
"fyne.io/fyne/v2"
9+
"fyne.io/fyne/v2/driver/desktop"
10+
)
11+
12+
// createWidgetWindow creates the main widget window on Linux.
13+
//
14+
// On Linux (especially Wayland/GNOME), window decorations cannot be removed
15+
// after creation. We use Fyne's CreateSplashWindow() which sets the GLFW
16+
// Decorated hint to false before the window is created, producing a truly
17+
// borderless window on both Wayland and X11.
18+
func createWidgetWindow(app fyne.App, title string) fyne.Window {
19+
drv, ok := app.Driver().(desktop.Driver)
20+
if !ok {
21+
log.Println("Linux: desktop driver not available, falling back to standard window")
22+
w := app.NewWindow(title)
23+
w.SetFixedSize(true)
24+
w.SetPadded(false)
25+
return w
26+
}
27+
28+
w := drv.CreateSplashWindow()
29+
w.SetTitle(title)
30+
w.SetFixedSize(true)
31+
w.SetPadded(false)
32+
33+
log.Println("Linux: created undecorated widget window via CreateSplashWindow")
34+
return w
35+
}

internal/ui/window_other.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//go:build !linux
2+
3+
package ui
4+
5+
import (
6+
"fyne.io/fyne/v2"
7+
)
8+
9+
// createWidgetWindow creates the main widget window on non-Linux platforms.
10+
// On Windows, decorations are removed post-creation via Win32 API calls.
11+
func createWidgetWindow(app fyne.App, title string) fyne.Window {
12+
w := app.NewWindow(title)
13+
w.SetFixedSize(true)
14+
w.SetPadded(false)
15+
return w
16+
}

0 commit comments

Comments
 (0)