fix(v3): Dock ops sync and add GetBadge method (#4838)

* dock fixes and get method

* update changelog

* async -> sync

* cleanup iOS and darwin set call

* handle potential errors
This commit is contained in:
Zach Botterman 2026-02-09 02:03:45 -08:00 committed by GitHub
commit 1a5c6dceee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 98 additions and 20 deletions

View file

@ -131,6 +131,11 @@ Remove the badge from the application icon:
dockService.RemoveBadge()
```
### Getting the set badge
```go
dockService.GetBadge()
```
## Platform Considerations
<Tabs>
@ -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

View file

@ -17,12 +17,14 @@ After processing, the content will be moved to the main changelog and this file
## Added
<!-- New features, capabilities, or enhancements -->
- Add `GetBadge` method to the dock service
## Changed
<!-- Changes in existing functionality -->
## Fixed
<!-- Bug fixes -->
- Fix dock badge methods consistency on macOS
## Deprecated
<!-- Soon-to-be removed features -->

View file

@ -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
}
}
// 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
}

View file

@ -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()
}

View file

@ -8,42 +8,59 @@ package dock
#import <Cocoa/Cocoa.h>
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
}

View file

@ -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
}

View file

@ -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
}