Add playback controls to dock menu on MacOS
This commit is contained in:
@@ -3,11 +3,20 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
void installReopenDelegate();
|
void installReopenDelegate();
|
||||||
|
void dockMenuBegin();
|
||||||
|
void dockMenuAddItem(const char* title, int index);
|
||||||
|
void dockMenuAddSeparator();
|
||||||
|
void dockMenuCommit();
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import "fyne.io/fyne/v2"
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
)
|
||||||
|
|
||||||
var darwinAppDelegateReopenWindow fyne.Window
|
var darwinAppDelegateReopenWindow fyne.Window
|
||||||
|
|
||||||
@@ -38,3 +47,35 @@ func appReopened() {
|
|||||||
func appShouldTerminate() {
|
func appShouldTerminate() {
|
||||||
darwinQuitting = true
|
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 appReopened(void);
|
||||||
extern void appShouldTerminate(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>
|
@interface ReopenDelegate : NSObject<NSApplicationDelegate>
|
||||||
@property (nonatomic, strong) id<NSApplicationDelegate> wrapped;
|
@property (nonatomic, strong) id<NSApplicationDelegate> wrapped;
|
||||||
@@ -34,6 +56,10 @@ extern void appShouldTerminate(void);
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
|
||||||
|
return dockMenu;
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)respondsToSelector:(SEL)sel {
|
- (BOOL)respondsToSelector:(SEL)sel {
|
||||||
return [super respondsToSelector:sel] ||
|
return [super respondsToSelector:sel] ||
|
||||||
[self.wrapped respondsToSelector:sel];
|
[self.wrapped respondsToSelector:sel];
|
||||||
@@ -50,3 +76,28 @@ void installReopenDelegate(void) {
|
|||||||
d.wrapped = [NSApp delegate];
|
d.wrapped = [NSApp delegate];
|
||||||
[NSApp setDelegate:d];
|
[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 {
|
func isRealQuit() bool {
|
||||||
return false
|
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()),
|
theme: myTheme.NewMyTheme(&app.Config.Theme, app.ThemesDir()),
|
||||||
}
|
}
|
||||||
installReopenHandler(m.Window)
|
installReopenHandler(m.Window)
|
||||||
|
installDockMenu(m.createSystemTrayAndDockMenu(false))
|
||||||
fynetooltip.SetToolTipTextSizeName(myTheme.SizeNameSubText)
|
fynetooltip.SetToolTipTextSizeName(myTheme.SizeNameSubText)
|
||||||
|
|
||||||
m.theme.NormalFont = app.Config.Application.FontNormalTTF
|
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) {
|
func (m *MainWindow) SetupSystemTrayMenu(appName string, fyneApp fyne.App) {
|
||||||
if desk, ok := fyneApp.(desktop.App); ok {
|
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() {
|
fyne.NewMenuItem(fmt.Sprintf("%s/%s", lang.L("Play"), lang.L("Pause")), func() {
|
||||||
m.App.PlaybackManager.PlayPause()
|
m.App.PlaybackManager.PlayPause()
|
||||||
}),
|
}),
|
||||||
@@ -427,19 +440,13 @@ func (m *MainWindow) SetupSystemTrayMenu(appName string, fyneApp fyne.App) {
|
|||||||
vol = vol - int(float64(vol)*0.1)
|
vol = vol - int(float64(vol)*0.1)
|
||||||
m.App.PlaybackManager.SetVolume(vol)
|
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)
|
if includeShowAndHide {
|
||||||
desk.SetSystemTrayIcon(res.ResAppicon256Png)
|
menu.Items = append(menu.Items, fyne.NewMenuItemSeparator())
|
||||||
if runtime.GOOS != "darwin" {
|
menu.Items = append(menu.Items, fyne.NewMenuItem(lang.L("Show"), m.Window.Show))
|
||||||
// Left-click opening systray menu instead of raising window
|
menu.Items = append(menu.Items, fyne.NewMenuItem(lang.L("Hide"), m.Window.Hide))
|
||||||
// is standard behavior on Mac.
|
|
||||||
desk.SetSystemTrayWindow(m.Window)
|
|
||||||
}
|
|
||||||
m.haveSystemTray = true
|
|
||||||
}
|
}
|
||||||
|
return menu
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MainWindow) HaveSystemTray() bool {
|
func (m *MainWindow) HaveSystemTray() bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user