Add playback controls to dock menu on MacOS
This commit is contained in:
@@ -3,11 +3,20 @@
|
||||
package ui
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
void installReopenDelegate();
|
||||
void dockMenuBegin();
|
||||
void dockMenuAddItem(const char* title, int index);
|
||||
void dockMenuAddSeparator();
|
||||
void dockMenuCommit();
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
var darwinAppDelegateReopenWindow fyne.Window
|
||||
|
||||
@@ -38,3 +47,35 @@ func appReopened() {
|
||||
func appShouldTerminate() {
|
||||
darwinQuitting = true
|
||||
}
|
||||
|
||||
// darwinDockMenuCallbacks is indexed by the NSMenuItem tag set in
|
||||
// dockMenuAddItem, dispatched back to Go via dockMenuItemClicked.
|
||||
var darwinDockMenuCallbacks []func()
|
||||
|
||||
func installDockMenu(menu *fyne.Menu) {
|
||||
darwinDockMenuCallbacks = darwinDockMenuCallbacks[:0]
|
||||
|
||||
C.dockMenuBegin()
|
||||
for _, item := range menu.Items {
|
||||
if item.IsSeparator {
|
||||
C.dockMenuAddSeparator()
|
||||
continue
|
||||
}
|
||||
cTitle := C.CString(item.Label)
|
||||
C.dockMenuAddItem(cTitle, C.int(len(darwinDockMenuCallbacks)))
|
||||
C.free(unsafe.Pointer(cTitle))
|
||||
darwinDockMenuCallbacks = append(darwinDockMenuCallbacks, item.Action)
|
||||
}
|
||||
C.dockMenuCommit()
|
||||
}
|
||||
|
||||
//export dockMenuItemClicked
|
||||
func dockMenuItemClicked(index C.int) {
|
||||
i := int(index)
|
||||
if i < 0 || i >= len(darwinDockMenuCallbacks) {
|
||||
return
|
||||
}
|
||||
if cb := darwinDockMenuCallbacks[i]; cb != nil {
|
||||
go func() { fyne.Do(cb) }()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,28 @@
|
||||
|
||||
extern void appReopened(void);
|
||||
extern void appShouldTerminate(void);
|
||||
extern void dockMenuItemClicked(int index);
|
||||
|
||||
// Generic action target for Dock menu items - the menu contents (titles,
|
||||
// separators, count) are fully defined from the Go side. Each item's tag
|
||||
// is the index of its Go-side callback, dispatched through the single
|
||||
// dockMenuItemClicked export.
|
||||
@interface DockMenuTarget : NSObject
|
||||
- (void)itemClicked:(id)sender;
|
||||
@end
|
||||
|
||||
@implementation DockMenuTarget
|
||||
- (void)itemClicked:(id)sender {
|
||||
dockMenuItemClicked((int)[(NSMenuItem *)sender tag]);
|
||||
}
|
||||
@end
|
||||
|
||||
// dockMenuBuilding is assembled by dockMenuBegin/AddItem/AddSeparator and
|
||||
// published to dockMenu (returned from -applicationDockMenu:) by
|
||||
// dockMenuCommit, so an in-progress rebuild is never seen half-built.
|
||||
static DockMenuTarget *dockMenuTarget;
|
||||
static NSMenu *dockMenuBuilding;
|
||||
static NSMenu *dockMenu;
|
||||
|
||||
@interface ReopenDelegate : NSObject<NSApplicationDelegate>
|
||||
@property (nonatomic, strong) id<NSApplicationDelegate> wrapped;
|
||||
@@ -34,6 +56,10 @@ extern void appShouldTerminate(void);
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
|
||||
return dockMenu;
|
||||
}
|
||||
|
||||
- (BOOL)respondsToSelector:(SEL)sel {
|
||||
return [super respondsToSelector:sel] ||
|
||||
[self.wrapped respondsToSelector:sel];
|
||||
@@ -50,3 +76,28 @@ void installReopenDelegate(void) {
|
||||
d.wrapped = [NSApp delegate];
|
||||
[NSApp setDelegate:d];
|
||||
}
|
||||
|
||||
void dockMenuBegin(void) {
|
||||
if (!dockMenuTarget) {
|
||||
dockMenuTarget = [DockMenuTarget new];
|
||||
}
|
||||
dockMenuBuilding = [[NSMenu alloc] initWithTitle:@""];
|
||||
}
|
||||
|
||||
void dockMenuAddItem(const char *title, int index) {
|
||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSString stringWithUTF8String:title]
|
||||
action:@selector(itemClicked:)
|
||||
keyEquivalent:@""];
|
||||
item.target = dockMenuTarget;
|
||||
item.tag = index;
|
||||
[dockMenuBuilding addItem:item];
|
||||
}
|
||||
|
||||
void dockMenuAddSeparator(void) {
|
||||
[dockMenuBuilding addItem:[NSMenuItem separatorItem]];
|
||||
}
|
||||
|
||||
void dockMenuCommit(void) {
|
||||
dockMenu = dockMenuBuilding;
|
||||
dockMenuBuilding = nil;
|
||||
}
|
||||
|
||||
@@ -10,3 +10,6 @@ func installReopenHandler(w fyne.Window) {
|
||||
func isRealQuit() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func installDockMenu(menu *fyne.Menu) {
|
||||
}
|
||||
|
||||
+19
-12
@@ -62,6 +62,7 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string,
|
||||
theme: myTheme.NewMyTheme(&app.Config.Theme, app.ThemesDir()),
|
||||
}
|
||||
installReopenHandler(m.Window)
|
||||
installDockMenu(m.createSystemTrayAndDockMenu(false))
|
||||
fynetooltip.SetToolTipTextSizeName(myTheme.SizeNameSubText)
|
||||
|
||||
m.theme.NormalFont = app.Config.Application.FontNormalTTF
|
||||
@@ -405,7 +406,19 @@ func (m *MainWindow) RunOnServerConnectedTasks(serverConf *backend.ServerConfig,
|
||||
|
||||
func (m *MainWindow) SetupSystemTrayMenu(appName string, fyneApp fyne.App) {
|
||||
if desk, ok := fyneApp.(desktop.App); ok {
|
||||
menu := fyne.NewMenu(appName,
|
||||
desk.SetSystemTrayMenu(m.createSystemTrayAndDockMenu(true))
|
||||
desk.SetSystemTrayIcon(res.ResAppicon256Png)
|
||||
if runtime.GOOS != "darwin" {
|
||||
// Left-click opening systray menu instead of raising window
|
||||
// is standard behavior on Mac.
|
||||
desk.SetSystemTrayWindow(m.Window)
|
||||
}
|
||||
m.haveSystemTray = true
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MainWindow) createSystemTrayAndDockMenu(includeShowAndHide bool) *fyne.Menu {
|
||||
menu := fyne.NewMenu("",
|
||||
fyne.NewMenuItem(fmt.Sprintf("%s/%s", lang.L("Play"), lang.L("Pause")), func() {
|
||||
m.App.PlaybackManager.PlayPause()
|
||||
}),
|
||||
@@ -427,19 +440,13 @@ func (m *MainWindow) SetupSystemTrayMenu(appName string, fyneApp fyne.App) {
|
||||
vol = vol - int(float64(vol)*0.1)
|
||||
m.App.PlaybackManager.SetVolume(vol)
|
||||
}),
|
||||
fyne.NewMenuItemSeparator(),
|
||||
fyne.NewMenuItem(lang.L("Show"), m.Window.Show),
|
||||
fyne.NewMenuItem(lang.L("Hide"), m.Window.Hide),
|
||||
)
|
||||
desk.SetSystemTrayMenu(menu)
|
||||
desk.SetSystemTrayIcon(res.ResAppicon256Png)
|
||||
if runtime.GOOS != "darwin" {
|
||||
// Left-click opening systray menu instead of raising window
|
||||
// is standard behavior on Mac.
|
||||
desk.SetSystemTrayWindow(m.Window)
|
||||
}
|
||||
m.haveSystemTray = true
|
||||
if includeShowAndHide {
|
||||
menu.Items = append(menu.Items, fyne.NewMenuItemSeparator())
|
||||
menu.Items = append(menu.Items, fyne.NewMenuItem(lang.L("Show"), m.Window.Show))
|
||||
menu.Items = append(menu.Items, fyne.NewMenuItem(lang.L("Hide"), m.Window.Hide))
|
||||
}
|
||||
return menu
|
||||
}
|
||||
|
||||
func (m *MainWindow) HaveSystemTray() bool {
|
||||
|
||||
Reference in New Issue
Block a user