mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Add systray Show, Hide and Destroy
This commit is contained in:
parent
ee24099be8
commit
4caf6d6e50
10 changed files with 311 additions and 13 deletions
|
|
@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Add function `application.NewServiceWithOptions` to initialise services with additional configuration by [@leaanthony](https://github.com/leaanthony) in [#4024](https://github.com/wailsapp/wails/pull/4024)
|
||||
- More documentation by [@leaanthony](https://github.com/leaanthony)
|
||||
- Support cancellation of events in standard event listeners by [@leaanthony](https://github.com/leaanthony)
|
||||
- Systray `Hide`, `Show` and `Destroy` support by [@leaanthony](https://github.com/leaanthony)
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
title: Customizing Windows in Wails
|
||||
title: Customising Windows in Wails
|
||||
sidebar:
|
||||
order: 10
|
||||
---
|
||||
|
|
@ -205,6 +205,7 @@ INF I always run after hooks!
|
|||
<Tabs>
|
||||
<TabItem label="macOS" icon="fa-brands:apple">
|
||||
| Event Name | Common Event | Description |
|
||||
|------------|--------------|-------------|
|
||||
| WindowDidBecomeKey | WindowFocus | Window became key window |
|
||||
| WindowDidBecomeMain | - | Window became main window |
|
||||
| WindowDidBeginSheet | - | Sheet began |
|
||||
|
|
@ -233,7 +234,7 @@ INF I always run after hooks!
|
|||
| WindowDidExitVersionBrowser | - | Window exited version browser |
|
||||
| WindowDidExpose | - | Window exposed |
|
||||
| WindowDidFocus | WindowFocus | Window gained focus |
|
||||
| WindowDidMiniaturise | WindowMinimise | Window minimised |
|
||||
| WindowDidMiniaturize | WindowMinimise | Window minimised |
|
||||
| WindowDidMove | WindowDidMove | Window moved |
|
||||
| WindowDidOrderOffScreen | - | Window ordered off screen |
|
||||
| WindowDidOrderOnScreen | - | Window ordered on screen |
|
||||
|
|
@ -255,7 +256,7 @@ INF I always run after hooks!
|
|||
| WindowMaximise | WindowMaximise | Window maximised |
|
||||
| WindowShouldClose | WindowClosing | Window should close |
|
||||
| WindowShow | WindowShow | Window shown |
|
||||
| WindowUnMaximise | WindowUnMaximise | Window unmaximised |
|
||||
| WindowUnMaximize | WindowUnMaximise | Window unmaximised |
|
||||
| WindowZoomIn | WindowZoomIn | Window zoomed in |
|
||||
| WindowZoomOut | WindowZoomOut | Window zoomed out |
|
||||
| WindowZoomReset | WindowZoomReset | Window zoom reset |
|
||||
|
|
|
|||
212
docs/src/content/docs/learn/systray.mdx
Normal file
212
docs/src/content/docs/learn/systray.mdx
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
---
|
||||
title: System Tray
|
||||
description: Learn how to create and use system tray icons in Wails
|
||||
---
|
||||
import {Badge} from '@astrojs/starlight/components';
|
||||
|
||||
## Introduction
|
||||
|
||||
The system tray (also known as the notification area) is a section of the desktop environment where applications can display icons and menus. In Wails, you can easily add a system tray icon to your application with full control over its appearance and behavior.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
To create a basic system tray icon:
|
||||
|
||||
```go
|
||||
app := application.New(options)
|
||||
systray := app.NewSystemTray()
|
||||
systray.SetLabel("My App")
|
||||
systray.SetIcon(iconBytes)
|
||||
systray.Run()
|
||||
```
|
||||
|
||||
## Setting the Icon
|
||||
|
||||
The system tray icon can be set using embedded image files. First, import the `embed` package and declare your icon files:
|
||||
|
||||
```go
|
||||
import "embed"
|
||||
|
||||
//go:embed assets/icon.png assets/icon-dark.png
|
||||
var iconFS embed.FS
|
||||
```
|
||||
|
||||
Then read and set the icons:
|
||||
|
||||
```go
|
||||
// Read icon data
|
||||
iconBytes, _ := iconFS.ReadFile("assets/icon.png")
|
||||
darkModeIconBytes, _ := iconFS.ReadFile("assets/icon-dark.png")
|
||||
|
||||
// Set icons
|
||||
systray.SetIcon(iconBytes)
|
||||
systray.SetDarkModeIcon(darkModeIconBytes)
|
||||
```
|
||||
|
||||
Supported image formats include PNG and JPEG. For best results, use icons with appropriate sizes:
|
||||
- Windows: 16x16 or 32x32 pixels
|
||||
- macOS: 18x18 to 128x128 pixels
|
||||
- Linux: Varies by desktop environment
|
||||
|
||||
On macOS, you can mark the icon as a template image for automatic dark/light mode adaptation:
|
||||
|
||||
```go
|
||||
systray.SetTemplateIcon(iconBytes)
|
||||
```
|
||||
|
||||
For more details on creating template icons, read this [great article](https://bjango.com/articles/designingmenubarextras/).
|
||||
|
||||
## Setting the Label <Badge text="macOS" variant="success" />
|
||||
|
||||
You can set a text label for your system tray icon:
|
||||
|
||||
```go
|
||||
systray.SetLabel("My App")
|
||||
```
|
||||
|
||||
The label will appear next to the icon in the system tray. On some platforms, this text may be truncated if it's too long.
|
||||
|
||||
## Adding a Menu
|
||||
|
||||
You can add a menu to your system tray icon:
|
||||
|
||||
```go
|
||||
menu := application.NewMenu()
|
||||
menu.Add("Open").OnClick(func() {
|
||||
// Handle click
|
||||
})
|
||||
menu.Add("Quit").OnClick(func() {
|
||||
app.Quit()
|
||||
})
|
||||
|
||||
systray.SetMenu(menu)
|
||||
```
|
||||
|
||||
## Attaching a Window
|
||||
|
||||
You can attach a window to a system tray icon to gain a number of desirable features:
|
||||
- The attached window will start hidden
|
||||
- Left-clicking on the system tray icon will toggle the visibility of the attached window
|
||||
- Right-clicking on the system tray icon will show the system tray menu, if given
|
||||
|
||||
Here's a complete example:
|
||||
|
||||
```go
|
||||
app := application.New()
|
||||
|
||||
// Create system tray
|
||||
systray := app.NewSystemTray()
|
||||
systray.SetLabel("My App")
|
||||
|
||||
// Create a window
|
||||
window := app.NewWebviewWindow()
|
||||
|
||||
// Attach the window to the system tray
|
||||
systray.AttachWindow(window)
|
||||
|
||||
// Optional: Set window offset from tray icon
|
||||
systray.WindowOffset(10)
|
||||
|
||||
// Optional: Set debounce time for window show/hide
|
||||
systray.WindowDebounce(200 * time.Millisecond)
|
||||
|
||||
// Add a menu (optional)
|
||||
menu := application.NewMenu()
|
||||
menu.Add("Open").OnClick(func() {
|
||||
window.Show()
|
||||
})
|
||||
menu.Add("Quit").OnClick(func() {
|
||||
app.Quit()
|
||||
})
|
||||
systray.SetMenu(menu)
|
||||
|
||||
systray.Run()
|
||||
```
|
||||
|
||||
## Icon Position <Badge text="macOS" variant="success" />
|
||||
|
||||
On macOS, you can control the position of the system tray icon relative to other icons:
|
||||
|
||||
```go
|
||||
systray.SetIconPosition(application.IconPositionRight)
|
||||
```
|
||||
|
||||
Available positions:
|
||||
- `NSImageNone`
|
||||
- `NSImageOnly`
|
||||
- `NSImageLeft`
|
||||
- `NSImageRight`
|
||||
- `NSImageBelow`
|
||||
- `NSImageAbove`
|
||||
- `NSImageOverlaps`
|
||||
- `NSImageLeading`
|
||||
- `NSImageTrailing`
|
||||
|
||||
## Destroying the System Tray
|
||||
|
||||
When you're done with the system tray, you should destroy it to release resources:
|
||||
|
||||
```go
|
||||
systray.Destroy()
|
||||
```
|
||||
|
||||
## Platform Considerations
|
||||
|
||||
- **macOS**: Icons support template images for automatic dark/light mode
|
||||
- **Windows**: Icons should be 16x16 or 32x32 pixels
|
||||
- **Linux**: Uses the StatusNotifierItem specification
|
||||
|
||||
## Examples
|
||||
|
||||
Explore these examples for more advanced usage:
|
||||
|
||||
- [Basic System Tray](/examples/systray-basic)
|
||||
- [System Tray with Menu](/examples/systray-menu)
|
||||
- [Custom System Tray](/examples/systray-custom)
|
||||
|
||||
## API Reference
|
||||
|
||||
### Core Methods
|
||||
| Method | Description |
|
||||
|--------------------------------|--------------------------------------------|
|
||||
| `NewSystemTray()` | Creates a new system tray instance |
|
||||
| `Run()` | Starts the system tray |
|
||||
| `SetLabel(label string)` | Sets the text label |
|
||||
| `SetIcon(icon []byte)` | Sets the icon image |
|
||||
| `SetDarkModeIcon(icon []byte)` | Sets the dark mode variant of the icon |
|
||||
| `SetTemplateIcon(icon []byte)` | Marks the icon as a template image (macOS) |
|
||||
| `SetIconPosition(position int)`| Sets the icon position (macOS) |
|
||||
| `Destroy()` | Destroys the system tray |
|
||||
|
||||
### Menu Management
|
||||
| Method | Description |
|
||||
|-----------------------|----------------------------------|
|
||||
| `SetMenu(menu *Menu)` | Attaches a menu to the tray icon |
|
||||
| `OpenMenu()` | Programmatically opens the menu |
|
||||
|
||||
### Event Handlers
|
||||
| Method | Description |
|
||||
|--------------------------------------|-----------------------------------|
|
||||
| `OnClick(handler func())` | Handles left-click events |
|
||||
| `OnRightClick(handler func())` | Handles right-click events |
|
||||
| `OnDoubleClick(handler func())` | Handles left-double-click events |
|
||||
| `OnRightDoubleClick(handler func())` | Handles right-double-click events |
|
||||
| `OnMouseEnter(handler func())` | Handles mouse enter events |
|
||||
| `OnMouseLeave(handler func())` | Handles mouse leave events |
|
||||
|
||||
### Window Attachment
|
||||
|
||||
| Method | Description |
|
||||
|------------------------------------------|---------------------------------------------|
|
||||
| `AttachWindow(window *WebviewWindow)` | Associates a window with the tray icon |
|
||||
| `WindowOffset(offset int)` | Sets the offset between the tray and window |
|
||||
| `WindowDebounce(debounce time.Duration)` | Sets the debounce time for window show/hide |
|
||||
|
||||
### Visibility Control
|
||||
|
||||
| Method | Description |
|
||||
|----------|-----------------------------|
|
||||
| `Show()` | Makes the tray icon visible |
|
||||
| `Hide()` | Hides the tray icon |
|
||||
|
||||
See the [SystemTray API Reference](/api/systemtray) for complete documentation.
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
"log"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"github.com/wailsapp/wails/v3/pkg/icons"
|
||||
|
|
@ -90,7 +91,12 @@ func main() {
|
|||
myMenu.AddRadio("Radio 1", true).OnClick(radioCallback)
|
||||
myMenu.AddRadio("Radio 2", false).OnClick(radioCallback)
|
||||
myMenu.AddRadio("Radio 3", false).OnClick(radioCallback)
|
||||
|
||||
myMenu.AddSeparator()
|
||||
myMenu.Add("Hide System tray for 3 seconds...").OnClick(func(ctx *application.Context) {
|
||||
systemTray.Hide()
|
||||
time.Sleep(3 * time.Second)
|
||||
systemTray.Show()
|
||||
})
|
||||
myMenu.AddSeparator()
|
||||
myMenu.Add("Quit").OnClick(func(ctx *application.Context) {
|
||||
app.Quit()
|
||||
|
|
|
|||
|
|
@ -799,6 +799,14 @@ func (a *App) OnShutdown(f func()) {
|
|||
a.shutdownTasks = append(a.shutdownTasks, f)
|
||||
}
|
||||
|
||||
func (a *App) destroySystemTray(tray *SystemTray) {
|
||||
// Remove the system tray from the a.systemTrays map
|
||||
a.systemTraysLock.Lock()
|
||||
delete(a.systemTrays, tray.id)
|
||||
a.systemTraysLock.Unlock()
|
||||
tray.destroy()
|
||||
}
|
||||
|
||||
func (a *App) cleanup() {
|
||||
if a.performingShutdown {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ type systemTrayImpl interface {
|
|||
run()
|
||||
setIcon(icon []byte)
|
||||
setMenu(menu *Menu)
|
||||
setIconPosition(position int)
|
||||
setIconPosition(position IconPosition)
|
||||
setTemplateIcon(icon []byte)
|
||||
destroy()
|
||||
setDarkModeIcon(icon []byte)
|
||||
|
|
@ -36,10 +36,8 @@ type systemTrayImpl interface {
|
|||
getScreen() (*Screen, error)
|
||||
positionWindow(window *WebviewWindow, offset int) error
|
||||
openMenu()
|
||||
}
|
||||
|
||||
type PositionOptions struct {
|
||||
Buffer int
|
||||
Show()
|
||||
Hide()
|
||||
}
|
||||
|
||||
type SystemTray struct {
|
||||
|
|
@ -47,7 +45,7 @@ type SystemTray struct {
|
|||
label string
|
||||
icon []byte
|
||||
darkModeIcon []byte
|
||||
iconPosition int
|
||||
iconPosition IconPosition
|
||||
|
||||
clickHandler func()
|
||||
rightClickHandler func()
|
||||
|
|
@ -162,7 +160,7 @@ func (s *SystemTray) SetMenu(menu *Menu) *SystemTray {
|
|||
return s
|
||||
}
|
||||
|
||||
func (s *SystemTray) SetIconPosition(iconPosition int) *SystemTray {
|
||||
func (s *SystemTray) SetIconPosition(iconPosition IconPosition) *SystemTray {
|
||||
if s.impl == nil {
|
||||
s.iconPosition = iconPosition
|
||||
} else {
|
||||
|
|
@ -185,6 +183,10 @@ func (s *SystemTray) SetTemplateIcon(icon []byte) *SystemTray {
|
|||
return s
|
||||
}
|
||||
|
||||
func (s *SystemTray) Destroy() {
|
||||
globalApplication.destroySystemTray(s)
|
||||
}
|
||||
|
||||
func (s *SystemTray) destroy() {
|
||||
if s.impl == nil {
|
||||
return
|
||||
|
|
@ -222,6 +224,24 @@ func (s *SystemTray) OnMouseLeave(handler func()) *SystemTray {
|
|||
return s
|
||||
}
|
||||
|
||||
func (s *SystemTray) Show() {
|
||||
if s.impl == nil {
|
||||
return
|
||||
}
|
||||
InvokeSync(func() {
|
||||
s.impl.Show()
|
||||
})
|
||||
}
|
||||
|
||||
func (s *SystemTray) Hide() {
|
||||
if s.impl == nil {
|
||||
return
|
||||
}
|
||||
InvokeSync(func() {
|
||||
s.impl.Hide()
|
||||
})
|
||||
}
|
||||
|
||||
type WindowAttachConfig struct {
|
||||
// Window is the window to attach to the system tray. If it's null, the request to attach will be ignored.
|
||||
Window *WebviewWindow
|
||||
|
|
|
|||
|
|
@ -9,6 +9,24 @@ package application
|
|||
#include "Cocoa/Cocoa.h"
|
||||
#include "menuitem_darwin.h"
|
||||
#include "systemtray_darwin.h"
|
||||
|
||||
// Show the system tray icon
|
||||
static void systemTrayShow(void* nsStatusItem) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Get the NSStatusItem
|
||||
NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem;
|
||||
[statusItem setVisible:YES];
|
||||
});
|
||||
}
|
||||
|
||||
// Hide the system tray icon
|
||||
static void systemTrayHide(void* nsStatusItem) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSStatusItem *statusItem = (NSStatusItem *)nsStatusItem;
|
||||
[statusItem setVisible:NO];
|
||||
});
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
|
|
@ -27,12 +45,26 @@ type macosSystemTray struct {
|
|||
nsStatusItem unsafe.Pointer
|
||||
nsImage unsafe.Pointer
|
||||
nsMenu unsafe.Pointer
|
||||
iconPosition int
|
||||
iconPosition IconPosition
|
||||
isTemplateIcon bool
|
||||
parent *SystemTray
|
||||
lastClickedScreen unsafe.Pointer
|
||||
}
|
||||
|
||||
func (s *macosSystemTray) Show() {
|
||||
if s.nsStatusItem == nil {
|
||||
return
|
||||
}
|
||||
C.systemTrayShow(s.nsStatusItem)
|
||||
}
|
||||
|
||||
func (s *macosSystemTray) Hide() {
|
||||
if s.nsStatusItem == nil {
|
||||
return
|
||||
}
|
||||
C.systemTrayHide(s.nsStatusItem)
|
||||
}
|
||||
|
||||
func (s *macosSystemTray) openMenu() {
|
||||
if s.nsMenu == nil {
|
||||
return
|
||||
|
|
@ -61,7 +93,7 @@ func systrayClickCallback(id C.long, buttonID C.int) {
|
|||
systemTray.processClick(button(buttonID))
|
||||
}
|
||||
|
||||
func (s *macosSystemTray) setIconPosition(position int) {
|
||||
func (s *macosSystemTray) setIconPosition(position IconPosition) {
|
||||
s.iconPosition = position
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -724,6 +724,16 @@ func (s *linuxSystemTray) SecondaryActivate(x int32, y int32) (err *dbus.Error)
|
|||
return
|
||||
}
|
||||
|
||||
// Show is a no-op for Linux
|
||||
func (s *linuxSystemTray) Show() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
// Hide is a no-op for Linux
|
||||
func (s *linuxSystemTray) Hide() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
// tooltip is our data for a tooltip property.
|
||||
// Param names need to match the generated code...
|
||||
type tooltip = struct {
|
||||
|
|
|
|||
|
|
@ -394,3 +394,11 @@ func (s *windowsSystemTray) destroy() {
|
|||
globalApplication.debug(syscall.GetLastError().Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *windowsSystemTray) Show() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
func (s *windowsSystemTray) Hide() {
|
||||
// No-op
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue