diff --git a/docs/src/content/docs/features/platform/dock.mdx b/docs/src/content/docs/features/platform/dock.mdx index 2638f7bf2..3cc5d9a92 100644 --- a/docs/src/content/docs/features/platform/dock.mdx +++ b/docs/src/content/docs/features/platform/dock.mdx @@ -131,6 +131,11 @@ Remove the badge from the application icon: dockService.RemoveBadge() ``` +### Getting the set badge +```go +dockService.GetBadge() +``` + ## Platform Considerations @@ -220,6 +225,7 @@ dockService.RemoveBadge() | `SetBadge(label string) error` | Sets a badge with the specified label | | `SetCustomBadge(label string, options BadgeOptions) error` | Sets a badge with the specified label and custom styling options (Windows only) | | `RemoveBadge() error` | Removes the badge from the application icon | +| `GetBadge() *string` | Gets the current badge | ### Structs and Types diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md index 8e4648038..2244540f3 100644 --- a/v3/UNRELEASED_CHANGELOG.md +++ b/v3/UNRELEASED_CHANGELOG.md @@ -17,12 +17,14 @@ After processing, the content will be moved to the main changelog and this file ## Added +- Add `GetBadge` method to the dock service ## Changed ## Fixed +- Fix dock badge methods consistency on macOS ## Deprecated diff --git a/v3/pkg/services/dock/badge_ios.go b/v3/pkg/services/dock/badge_ios.go index 62dda0cb7..136771839 100644 --- a/v3/pkg/services/dock/badge_ios.go +++ b/v3/pkg/services/dock/badge_ios.go @@ -8,7 +8,8 @@ import ( "github.com/wailsapp/wails/v3/pkg/application" ) -type iosDock struct{} +type iosDock struct { +} // New creates a new Dock Service. // On iOS, this returns a stub implementation. @@ -61,4 +62,10 @@ func (d *iosDock) SetCustomBadge(label string, options BadgeOptions) error { func (d *iosDock) RemoveBadge() error { // iOS badge removal would go here via native bridge return nil -} \ No newline at end of file +} + +// GetBadge retrieves the badge from the iOS app icon. +func (d *iosDock) GetBadge() *string { + // iOS badge retrieval would go here via native bridge + return nil +} diff --git a/v3/pkg/services/dock/dock.go b/v3/pkg/services/dock/dock.go index eb11f59ca..3f9890c67 100644 --- a/v3/pkg/services/dock/dock.go +++ b/v3/pkg/services/dock/dock.go @@ -20,6 +20,7 @@ type platformDock interface { SetBadge(label string) error SetCustomBadge(label string, options BadgeOptions) error RemoveBadge() error + GetBadge() *string } // Service represents the dock service @@ -75,3 +76,8 @@ func (d *DockService) SetCustomBadge(label string, options BadgeOptions) error { func (d *DockService) RemoveBadge() error { return d.impl.RemoveBadge() } + +// GetBadge returns the badge label on the application icon. +func (d *DockService) GetBadge() *string { + return d.impl.GetBadge() +} diff --git a/v3/pkg/services/dock/dock_darwin.go b/v3/pkg/services/dock/dock_darwin.go index c42ae6b0c..361e3ef8a 100644 --- a/v3/pkg/services/dock/dock_darwin.go +++ b/v3/pkg/services/dock/dock_darwin.go @@ -8,42 +8,59 @@ package dock #import void hideDockIcon() { - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_sync(dispatch_get_main_queue(), ^{ [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; }); } void showDockIcon() { - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_sync(dispatch_get_main_queue(), ^{ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; }); } -static void setBadge(const char *label) { - dispatch_async(dispatch_get_main_queue(), ^{ +bool setBadge(const char *label) { + __block bool success = false; + dispatch_sync(dispatch_get_main_queue(), ^{ + // Ensure the app is in Regular activation policy (dock icon visible) + NSApplicationActivationPolicy currentPolicy = [NSApp activationPolicy]; + if (currentPolicy != NSApplicationActivationPolicyRegular) { + success = false; + return; + } + NSString *nsLabel = nil; if (label != NULL) { nsLabel = [NSString stringWithUTF8String:label]; } [[NSApp dockTile] setBadgeLabel:nsLabel]; [[NSApp dockTile] display]; + success = true; }); + return success; } */ import "C" import ( "context" + "fmt" + "sync" "unsafe" "github.com/wailsapp/wails/v3/pkg/application" ) -type darwinDock struct{} +type darwinDock struct { + mu sync.RWMutex + Badge *string +} // Creates a new Dock Service. func New() *DockService { return &DockService{ - impl: &darwinDock{}, + impl: &darwinDock{ + Badge: nil, + }, } } @@ -67,22 +84,42 @@ func (d *darwinDock) HideAppIcon() { } // ShowAppIcon shows the app icon in the macOS Dock. +// Note: After showing the dock icon, you may need to call SetBadge again +// to reapply any previously set badge, as changing activation policies clears the badge. func (d *darwinDock) ShowAppIcon() { C.showDockIcon() } -// SetBadge sets the badge label on the application icon. -func (d *darwinDock) SetBadge(label string) error { - // Always pick a label (use “●” if empty), then allocate + free exactly once. - value := label - if value == "" { - value = "●" // Default badge character - } - cLabel := C.CString(value) - defer C.free(unsafe.Pointer(cLabel)) +// setBadge handles the C call and updates the internal badge state with locking. +func (d *darwinDock) setBadge(label *string) error { + var cLabel *C.char + if label != nil { + cLabel = C.CString(*label) + defer C.free(unsafe.Pointer(cLabel)) + } - C.setBadge(cLabel) - return nil + success := C.setBadge(cLabel) + if !success { + return fmt.Errorf("failed to set badge") + } + + d.mu.Lock() + d.Badge = label + d.mu.Unlock() + + return nil +} + +// SetBadge sets the badge label on the application icon. +// Available default badge labels: +// Single space " " empty badge +// Empty string "" dot "●" indeterminate badge +func (d *darwinDock) SetBadge(label string) error { + // Always pick a label (use "●" if empty), then allocate + free exactly once. + if label == "" { + label = "●" // Default badge character + } + return d.setBadge(&label) } // SetCustomBadge is not supported on macOS, SetBadge is called instead. @@ -92,6 +129,12 @@ func (d *darwinDock) SetCustomBadge(label string, options BadgeOptions) error { // RemoveBadge removes the badge label from the application icon. func (d *darwinDock) RemoveBadge() error { - C.setBadge(nil) - return nil + return d.setBadge(nil) +} + +// GetBadge returns the badge label on the application icon. +func (d *darwinDock) GetBadge() *string { + d.mu.RLock() + defer d.mu.RUnlock() + return d.Badge } diff --git a/v3/pkg/services/dock/dock_linux.go b/v3/pkg/services/dock/dock_linux.go index af0311c67..633e6b005 100644 --- a/v3/pkg/services/dock/dock_linux.go +++ b/v3/pkg/services/dock/dock_linux.go @@ -68,3 +68,8 @@ func (l *linuxDock) RemoveBadge() error { // No-op: Linux doesn't have standardized badge support return nil } + +func (l *linuxDock) GetBadge() *string { + // No-op: Linux doesn't have standardized badge support + return nil +} \ No newline at end of file diff --git a/v3/pkg/services/dock/dock_windows.go b/v3/pkg/services/dock/dock_windows.go index 26f40d259..0a0c45efe 100644 --- a/v3/pkg/services/dock/dock_windows.go +++ b/v3/pkg/services/dock/dock_windows.go @@ -24,6 +24,7 @@ type windowsDock struct { badgeSize int fontManager *FontManager badgeOptions BadgeOptions + badge *string } var defaultOptions = BadgeOptions{ @@ -48,6 +49,7 @@ func NewWithOptions(options BadgeOptions) *DockService { return &DockService{ impl: &windowsDock{ badgeOptions: options, + badge: nil, }, } } @@ -121,6 +123,7 @@ func (w *windowsDock) SetBadge(label string) error { } defer w32.DestroyIcon(hicon) + w.badge = &label return w.taskbar.SetOverlayIcon(hwnd, hicon, nil) }) } @@ -182,6 +185,7 @@ func (w *windowsDock) SetCustomBadge(label string, options BadgeOptions) error { } defer w32.DestroyIcon(hicon) + w.badge = &label return w.taskbar.SetOverlayIcon(hwnd, hicon, nil) }) } @@ -209,6 +213,7 @@ func (w *windowsDock) RemoveBadge() error { } hwnd := uintptr(nativeWindow) + w.badge = nil return w.taskbar.SetOverlayIcon(hwnd, 0, nil) }) } @@ -394,3 +399,7 @@ func (w *windowsDock) createBadge() { w.badgeImg = img } + +func (w *windowsDock) GetBadge() *string { + return w.badge +} \ No newline at end of file