mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-12 15:09:33 +08:00
render label on windows and sensible defaults
This commit is contained in:
parent
d1d57c132c
commit
7287c1eb5c
@ -36,14 +36,14 @@ app := application.New(application.Options{
|
||||
Set a badge on the application tile/dock icon:
|
||||
|
||||
```go
|
||||
// Set a default badge
|
||||
badgeService.SetBadge("")
|
||||
|
||||
// Set a numeric badge
|
||||
badgeService.SetBadge("3")
|
||||
|
||||
// Set a text badge
|
||||
badgeService.SetBadge("New")
|
||||
|
||||
// Set a symbol badge
|
||||
badgeService.SetBadge("●")
|
||||
```
|
||||
|
||||
### Removing a Badge
|
||||
@ -52,11 +52,6 @@ Remove the badge from the application icon:
|
||||
|
||||
```go
|
||||
badgeService.RemoveBadge()
|
||||
|
||||
// or
|
||||
|
||||
// Set an empty string
|
||||
badgeService.SetBadge("")
|
||||
```
|
||||
|
||||
## Platform Considerations
|
||||
@ -67,9 +62,10 @@ badgeService.SetBadge("")
|
||||
On macOS, badges:
|
||||
|
||||
- Are displayed directly on the dock icon
|
||||
- Support both text and numeric values
|
||||
- Support text values
|
||||
- Automatically handle dark/light mode appearance
|
||||
- Use the standard macOS dock badge styling
|
||||
- Automatically handle label overflow
|
||||
|
||||
</TabItem>
|
||||
|
||||
@ -78,10 +74,10 @@ badgeService.SetBadge("")
|
||||
On Windows, badges:
|
||||
|
||||
- Are displayed as an overlay icon in the taskbar
|
||||
- Currently implemented as a red circle with a white center
|
||||
- Do not currently support displaying text or numbers
|
||||
- Support text values
|
||||
- Adapt to Windows theme settings
|
||||
- Require the application to have a window
|
||||
- Does not handle label overflow
|
||||
|
||||
</TabItem>
|
||||
|
||||
|
@ -49,6 +49,7 @@ require (
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/flopp/go-findfont v0.1.0 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
)
|
||||
|
||||
|
@ -117,6 +117,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/flopp/go-findfont v0.1.0 h1:lPn0BymDUtJo+ZkV01VS3661HL6F4qFlkhcJN55u6mU=
|
||||
github.com/flopp/go-findfont v0.1.0/go.mod h1:wKKxRDjD024Rh7VMwoU90i6ikQRCr+JTHB5n4Ejkqvw=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
|
@ -45,6 +45,8 @@ func (d *darwinBadge) SetBadge(label string) error {
|
||||
if label != "" {
|
||||
cLabel = C.CString(label)
|
||||
defer C.free(unsafe.Pointer(cLabel))
|
||||
} else {
|
||||
cLabel = C.CString("●") // Default badge character
|
||||
}
|
||||
C.setBadge(cLabel)
|
||||
return nil
|
||||
|
@ -8,11 +8,17 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/flopp/go-findfont"
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"github.com/wailsapp/wails/v3/pkg/w32"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/opentype"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -102,7 +108,9 @@ func (t *ITaskbarList3) SetOverlayIcon(hwnd syscall.Handle, hIcon syscall.Handle
|
||||
}
|
||||
|
||||
type windowsBadge struct {
|
||||
taskbar *ITaskbarList3
|
||||
taskbar *ITaskbarList3
|
||||
badgeImg *image.RGBA
|
||||
badgeSize int
|
||||
}
|
||||
|
||||
func New() *Service {
|
||||
@ -150,13 +158,19 @@ func (w *windowsBadge) SetBadge(label string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if label == "" {
|
||||
return w.taskbar.SetOverlayIcon(syscall.Handle(hwnd), 0, nil)
|
||||
}
|
||||
w.createBadge()
|
||||
|
||||
hicon, err := createBadgeIcon()
|
||||
if err != nil {
|
||||
return err
|
||||
var hicon w32.HICON
|
||||
if label == "" {
|
||||
hicon, err = w.createBadgeIcon()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
hicon, err = w.createBadgeIconWithText(label)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer w32.DestroyIcon(hicon)
|
||||
|
||||
@ -186,17 +200,99 @@ func (w *windowsBadge) RemoveBadge() error {
|
||||
return w.taskbar.SetOverlayIcon(syscall.Handle(hwnd), 0, nil)
|
||||
}
|
||||
|
||||
func createBadgeIcon() (w32.HICON, error) {
|
||||
const size = 32
|
||||
func (w *windowsBadge) createBadgeIcon() (w32.HICON, error) {
|
||||
radius := w.badgeSize / 2
|
||||
centerX, centerY := radius, radius
|
||||
white := color.RGBA{255, 255, 255, 255}
|
||||
innerRadius := w.badgeSize / 5
|
||||
|
||||
img := image.NewRGBA(image.Rect(0, 0, size, size))
|
||||
for y := 0; y < w.badgeSize; y++ {
|
||||
for x := 0; x < w.badgeSize; x++ {
|
||||
dx := float64(x - centerX)
|
||||
dy := float64(y - centerY)
|
||||
|
||||
if dx*dx+dy*dy < float64(innerRadius*innerRadius) {
|
||||
w.badgeImg.Set(x, y, white)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, w.badgeImg); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
hicon, err := w32.CreateSmallHIconFromImage(buf.Bytes())
|
||||
return hicon, err
|
||||
}
|
||||
|
||||
func (w *windowsBadge) createBadgeIconWithText(label string) (w32.HICON, error) {
|
||||
fontPath := ""
|
||||
for _, path := range findfont.List() {
|
||||
if strings.Contains(strings.ToLower(path), "segoeuib.ttf") || // Segoe UI Bold
|
||||
strings.Contains(strings.ToLower(path), "arialbd.ttf") {
|
||||
fontPath = path
|
||||
break
|
||||
}
|
||||
}
|
||||
if fontPath == "" {
|
||||
return w.createBadgeIcon()
|
||||
}
|
||||
|
||||
fontBytes, err := os.ReadFile(fontPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ttf, err := opentype.Parse(fontBytes)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
fontSize := 18.0
|
||||
if len(label) > 1 {
|
||||
fontSize = 14.0
|
||||
}
|
||||
|
||||
face, err := opentype.NewFace(ttf, &opentype.FaceOptions{
|
||||
Size: fontSize,
|
||||
DPI: 96,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer face.Close()
|
||||
|
||||
d := &font.Drawer{
|
||||
Dst: w.badgeImg,
|
||||
Src: image.NewUniform(color.White),
|
||||
Face: face,
|
||||
}
|
||||
|
||||
textWidth := d.MeasureString(label).Ceil()
|
||||
d.Dot = fixed.P((w.badgeSize-textWidth)/2, int(float64(w.badgeSize)/2+fontSize/2))
|
||||
d.DrawString(label)
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, w.badgeImg); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return w32.CreateSmallHIconFromImage(buf.Bytes())
|
||||
}
|
||||
|
||||
func (w *windowsBadge) createBadge() {
|
||||
w.badgeSize = 32
|
||||
|
||||
img := image.NewRGBA(image.Rect(0, 0, w.badgeSize, w.badgeSize))
|
||||
|
||||
red := color.RGBA{255, 0, 0, 255}
|
||||
radius := size / 2
|
||||
radius := w.badgeSize / 2
|
||||
centerX, centerY := radius, radius
|
||||
|
||||
for y := 0; y < size; y++ {
|
||||
for x := 0; x < size; x++ {
|
||||
for y := 0; y < w.badgeSize; y++ {
|
||||
for x := 0; x < w.badgeSize; x++ {
|
||||
dx := float64(x - centerX)
|
||||
dy := float64(y - centerY)
|
||||
|
||||
@ -206,25 +302,5 @@ func createBadgeIcon() (w32.HICON, error) {
|
||||
}
|
||||
}
|
||||
|
||||
white := color.RGBA{255, 255, 255, 255}
|
||||
innerRadius := size / 5
|
||||
|
||||
for y := 0; y < size; y++ {
|
||||
for x := 0; x < size; x++ {
|
||||
dx := float64(x - centerX)
|
||||
dy := float64(y - centerY)
|
||||
|
||||
if dx*dx+dy*dy < float64(innerRadius*innerRadius) {
|
||||
img.Set(x, y, white)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, img); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
hicon, err := w32.CreateSmallHIconFromImage(buf.Bytes())
|
||||
return hicon, err
|
||||
w.badgeImg = img
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user