Skip to content

Commit 0b2b6d1

Browse files
committed
Ubuntu 26 opacity reveresed
1 parent 4f4d706 commit 0b2b6d1

5 files changed

Lines changed: 126 additions & 83 deletions

File tree

internal/ui/background_linux.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//go:build linux
2+
3+
package ui
4+
5+
import (
6+
"fyne.io/fyne/v2"
7+
)
8+
9+
// SetLinuxBgOpacity is a no-op on Linux — opacity is handled via the theme.
10+
func SetLinuxBgOpacity(_ int) {}
11+
12+
// wrapWithPlatformBackground returns content unchanged on Linux.
13+
// The dark background is handled by the Fyne theme (dark variant forced).
14+
func wrapWithPlatformBackground(content fyne.CanvasObject) fyne.CanvasObject {
15+
return content
16+
}
17+
18+
// refreshPlatformBackground is a no-op on Linux.
19+
func refreshPlatformBackground(_ fyne.CanvasObject) {}

internal/ui/background_other.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//go:build !linux
2+
3+
package ui
4+
5+
import "fyne.io/fyne/v2"
6+
7+
// SetLinuxBgOpacity is a no-op on non-Linux platforms.
8+
func SetLinuxBgOpacity(_ int) {}
9+
10+
// wrapWithPlatformBackground returns the content unchanged on non-Linux platforms.
11+
// On Windows, transparency is handled via Win32 color-key in the theme.
12+
func wrapWithPlatformBackground(content fyne.CanvasObject) fyne.CanvasObject {
13+
return content
14+
}
15+
16+
// refreshPlatformBackground is a no-op on non-Linux platforms.
17+
func refreshPlatformBackground(_ fyne.CanvasObject) {}

internal/ui/manager.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ func (u *UIManager) ShowWidget(cities []config.CityConfig) {
6969
}
7070

7171
grid := container.NewGridWithColumns(count, objects...)
72-
u.widget.SetContent(grid)
72+
73+
// On Linux, wrap the grid in a rounded dark background.
74+
// On Windows, the grid is used directly (transparency via color-key).
75+
content := wrapWithPlatformBackground(grid)
76+
u.widget.SetContent(content)
7377

7478
w, h, _ := CalculateLayout(count)
7579
u.widget.Resize(fyne.NewSize(float32(w), float32(h)))
@@ -157,9 +161,12 @@ func (u *UIManager) GetPosition() (int, int) {
157161
// SetOpacity applies background-only transparency to the widget window.
158162
// opacityPercent should be 25, 50, 75, or 100.
159163
// On Windows the background color becomes transparent via Win32 color-key;
160-
// on Linux the whole window opacity is adjusted via _NET_WM_WINDOW_OPACITY
161-
// with mapped values to keep content readable.
164+
// on Linux the rounded background's alpha is adjusted, keeping text/icons
165+
// fully opaque.
162166
func (u *UIManager) SetOpacity(opacityPercent int) {
163167
setWindowOpacity(opacityPercent)
164-
u.widget.Canvas().Refresh(u.widget.Content())
168+
if u.widget.Content() != nil {
169+
refreshPlatformBackground(u.widget.Content())
170+
u.widget.Canvas().Refresh(u.widget.Content())
171+
}
165172
}

internal/ui/theme.go

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@ var transparencyKey = color.NRGBA{R: 1, G: 1, B: 1, A: 255}
1717
// transparencyActive is 1 when a color-key background should be used.
1818
var transparencyActive atomic.Int32
1919

20-
// linuxOpacityAlpha stores the alpha value for Linux background transparency.
21-
// Range: 0 (fully transparent) to 255 (fully opaque). Default is 255.
22-
var linuxOpacityAlpha atomic.Int32
20+
// linuxBgShade stores the background RGB value for Linux (0=black, 255=white).
21+
var linuxBgShade atomic.Int32
22+
23+
// linuxBgAlpha stores the background alpha value for Linux (0=transparent, 255=opaque).
24+
// Controlled by the opacity setting: 100% = fully opaque (255), 25% = mostly transparent (64).
25+
var linuxBgAlpha atomic.Int32
2326

2427
func init() {
25-
linuxOpacityAlpha.Store(255)
28+
linuxBgShade.Store(30) // default: dark background
29+
linuxBgAlpha.Store(255) // default: fully opaque
2630
}
2731

2832
// SetTransparencyActive switches the background color key on or off.
@@ -34,21 +38,30 @@ func SetTransparencyActive(active bool) {
3438
}
3539
}
3640

37-
// SetLinuxOpacityAlpha sets the background alpha value for Linux transparency.
38-
// opacityPercent should be 0–100. This is converted to a 0–255 alpha value.
39-
func SetLinuxOpacityAlpha(opacityPercent int) {
41+
// SetLinuxBackgroundShade sets the Linux background transparency level.
42+
// opacityPercent controls the alpha channel of the dark background:
43+
//
44+
// 100% → A=255 (fully opaque dark background)
45+
// 75% → A=191 (slightly transparent)
46+
// 50% → A=128 (semi-transparent)
47+
// 25% → A=64 (mostly transparent — desktop shows through)
48+
func SetLinuxBackgroundShade(opacityPercent int) {
49+
// Map opacity percentage directly to alpha value (0–255).
4050
alpha := opacityPercent * 255 / 100
41-
if alpha < 0 {
42-
alpha = 0
43-
}
4451
if alpha > 255 {
4552
alpha = 255
4653
}
47-
linuxOpacityAlpha.Store(int32(alpha))
54+
if alpha < 0 {
55+
alpha = 0
56+
}
57+
linuxBgShade.Store(30) // keep background dark (RGB 30,30,30)
58+
linuxBgAlpha.Store(int32(alpha))
4859
}
4960

50-
// widgetTheme is a Fyne theme that replaces the window background with the
51-
// color key when transparency is active, leaving all other colors unchanged.
61+
// widgetTheme is a Fyne theme that:
62+
// - On Windows: replaces the background with a color-key when transparency is active
63+
// - On Linux: always uses a dark background (ignoring system light/dark preference)
64+
// to ensure the widget looks consistent and native
5265
type widgetTheme struct {
5366
base fyne.Theme
5467
}
@@ -59,21 +72,35 @@ func NewWidgetTheme(base fyne.Theme) fyne.Theme {
5972
}
6073

6174
func (t *widgetTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
75+
// Windows: color-key transparency when active.
6276
if transparencyActive.Load() == 1 {
6377
switch name {
6478
case theme.ColorNameBackground, theme.ColorNameOverlayBackground:
65-
if runtime.GOOS == "linux" {
66-
// On Linux/Wayland, use actual alpha transparency.
67-
// The Fyne OpenGL/EGL renderer respects the alpha channel,
68-
// giving us background-only transparency (text/icons stay opaque).
69-
alpha := uint8(linuxOpacityAlpha.Load())
70-
return color.NRGBA{R: 30, G: 30, B: 30, A: alpha}
71-
}
72-
// On Windows, use the color-key approach (Win32 LWA_COLORKEY
73-
// makes this exact color invisible).
7479
return transparencyKey
7580
}
7681
}
82+
83+
// Linux: always use dark variant colors for a consistent widget appearance,
84+
// regardless of the system theme (light/dark). The background alpha is
85+
// controlled by the opacity setting for true see-through transparency.
86+
if runtime.GOOS == "linux" {
87+
switch name {
88+
case theme.ColorNameBackground, theme.ColorNameOverlayBackground:
89+
shade := uint8(linuxBgShade.Load())
90+
alpha := uint8(linuxBgAlpha.Load())
91+
return color.NRGBA{R: shade, G: shade, B: shade, A: alpha}
92+
case theme.ColorNameForeground:
93+
// Always white text on Linux for readability on dark background.
94+
return color.NRGBA{R: 255, G: 255, B: 255, A: 255}
95+
case theme.ColorNameDisabled:
96+
return color.NRGBA{R: 180, G: 180, B: 180, A: 255}
97+
case theme.ColorNameSeparator:
98+
return color.NRGBA{R: 80, G: 80, B: 80, A: 255}
99+
}
100+
// For all other colors, force dark variant.
101+
return t.base.Color(name, theme.VariantDark)
102+
}
103+
77104
return t.base.Color(name, variant)
78105
}
79106

internal/ui/x11_linux.go

Lines changed: 30 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -446,12 +446,13 @@ func getWindowPositionXdotool() (int, int) {
446446

447447
// setWindowOpacity applies transparency to the widget window.
448448
//
449-
// On Wayland, Fyne renders via EGL/OpenGL and the compositor handles
450-
// transparency through the alpha channel. We use the Fyne theme's
451-
// background color alpha to achieve background-only transparency (similar
452-
// to Windows color-key approach) rather than whole-window opacity.
449+
// On both Wayland and X11, transparency is achieved by setting the alpha
450+
// channel of the Fyne theme's background color. This provides background-only
451+
// transparency — the desktop shows through the background while text and
452+
// icons remain fully opaque.
453453
//
454-
// On X11 we fall back to _NET_WM_WINDOW_OPACITY via xprop.
454+
// On X11, any existing _NET_WM_WINDOW_OPACITY hint is removed to prevent
455+
// the compositor from double-applying whole-window transparency.
455456
func setWindowOpacity(opacityPercent int) {
456457
if isWayland() {
457458
setWindowOpacityWayland(opacityPercent)
@@ -460,75 +461,47 @@ func setWindowOpacity(opacityPercent int) {
460461
}
461462
}
462463

463-
// setWindowOpacityWayland uses Fyne's theme alpha channel for transparency.
464-
// On Wayland, the compositor respects the alpha channel of the EGL/OpenGL
465-
// surface. We set the background color's alpha value to achieve background-only
466-
// transparency — text, icons, and other content remain fully opaque.
467-
// This is equivalent to the Windows color-key approach in visual result.
464+
// setWindowOpacityWayland controls background transparency on Wayland.
465+
// The opacity setting controls the alpha channel of the dark background,
466+
// allowing the desktop to show through while keeping text/icons fully opaque.
467+
//
468+
// 100% → fully opaque dark background
469+
// 75% → slightly transparent
470+
// 50% → semi-transparent
471+
// 25% → mostly transparent (desktop visible through background)
468472
func setWindowOpacityWayland(opacityPercent int) {
469-
if opacityPercent >= 100 {
470-
SetTransparencyActive(false)
471-
SetLinuxOpacityAlpha(100)
472-
log.Printf("Linux/Wayland: transparency disabled (100%% opaque)")
473-
return
474-
}
475-
476-
// Set the alpha value for the background color.
477-
SetLinuxOpacityAlpha(opacityPercent)
478-
SetTransparencyActive(true)
479-
log.Printf("Linux/Wayland: background transparency set to %d%% alpha", opacityPercent)
473+
SetLinuxBackgroundShade(opacityPercent)
474+
log.Printf("Linux/Wayland: background alpha set for opacity %d%%", opacityPercent)
480475
}
481476

482-
// setWindowOpacityX11 applies whole-window transparency on X11 via xprop.
477+
// setWindowOpacityX11 controls background transparency on X11.
478+
// Like Wayland, we use the Fyne theme's background alpha channel for
479+
// background-only transparency, keeping text and icons fully opaque.
480+
// The _NET_WM_WINDOW_OPACITY hint is removed so the compositor doesn't
481+
// double-apply whole-window transparency.
483482
func setWindowOpacityX11(opacityPercent int) {
484-
x11Percent := mapOpacityForDisplay(opacityPercent)
483+
SetLinuxBackgroundShade(opacityPercent)
485484

486485
go func() {
487486
time.Sleep(600 * time.Millisecond)
488487

489488
if _, err := exec.LookPath("xprop"); err != nil {
490-
log.Printf("Linux/X11: xprop not found, cannot set opacity")
491-
return
492-
}
493-
494-
if x11Percent >= 100 {
495-
cmd := exec.Command("xprop", "-name", widgetTitle, "-remove", "_NET_WM_WINDOW_OPACITY")
496-
if err := cmd.Run(); err != nil {
497-
log.Printf("Linux/X11: failed to remove _NET_WM_WINDOW_OPACITY: %v", err)
498-
}
489+
log.Printf("Linux/X11: xprop not found, relying on theme alpha only")
499490
return
500491
}
501492

502-
opacity := uint64(x11Percent) * 0xFFFFFFFF / 100
503-
val := strconv.FormatUint(opacity, 10)
504-
505-
cmd := exec.Command("xprop", "-name", widgetTitle,
506-
"-f", "_NET_WM_WINDOW_OPACITY", "32c",
507-
"-set", "_NET_WM_WINDOW_OPACITY", val)
493+
// Remove any whole-window opacity hint so only the background alpha
494+
// (set via the theme) controls transparency.
495+
cmd := exec.Command("xprop", "-name", widgetTitle, "-remove", "_NET_WM_WINDOW_OPACITY")
508496
if err := cmd.Run(); err != nil {
509-
log.Printf("Linux/X11: xprop opacity failed: %v", err)
510-
return
497+
log.Printf("Linux/X11: failed to remove _NET_WM_WINDOW_OPACITY: %v", err)
511498
}
512-
log.Printf("Linux/X11: set window opacity to %d%% (user: %d%%)", x11Percent, opacityPercent)
499+
log.Printf("Linux/X11: background alpha set for opacity %d%%", opacityPercent)
513500
}()
514501
}
515502

516-
// mapOpacityForDisplay maps user-facing opacity percentages to display
517-
// values that keep content readable when using whole-window opacity.
518-
func mapOpacityForDisplay(opacityPercent int) int {
519-
switch {
520-
case opacityPercent >= 100:
521-
return 100
522-
case opacityPercent >= 75:
523-
return 85
524-
case opacityPercent >= 50:
525-
return 70
526-
case opacityPercent >= 25:
527-
return 55
528-
default:
529-
return 55
530-
}
531-
}
503+
// (mapOpacityForDisplay removed — no longer needed; transparency is now
504+
// handled via the theme's background alpha channel on both X11 and Wayland.)
532505

533506
// ---------------------------------------------------------------------------
534507
// Monitor enumeration

0 commit comments

Comments
 (0)