mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-26 06:19:08 +01:00
add shortcuts in tray for quit and settings item
This commit is contained in:
@@ -208,7 +208,7 @@ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|||||||
|
|
||||||
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (
|
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (
|
||||||
<span
|
<span
|
||||||
className={cn("ml-auto text-xs tracking-widest text-nb-gray-400 opacity-60", className)}
|
className={cn("ml-auto text-xs tracking-widest text-nb-gray-100 opacity-60", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
ArrowUpCircleIcon,
|
ArrowUpCircleIcon,
|
||||||
@@ -15,24 +15,34 @@ import {
|
|||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/DropdownMenu";
|
} from "@/components/DropdownMenu";
|
||||||
import { IconButton } from "@/components/IconButton";
|
import { IconButton } from "@/components/IconButton";
|
||||||
import { ProfileDropdown } from "@/components/ProfileDropdown";
|
import { ProfileDropdown } from "@/components/ProfileDropdown";
|
||||||
import { useClientVersion } from "@/modules/auto-update/ClientVersionContext";
|
import { useClientVersion } from "@/modules/auto-update/ClientVersionContext";
|
||||||
import { cn } from "@/lib/cn";
|
import { cn } from "@/lib/cn";
|
||||||
|
import { formatShortcut, useKeyboardShortcut } from "@/lib/useKeyboardShortcut";
|
||||||
import { useViewMode, type ViewMode } from "@/lib/viewMode";
|
import { useViewMode, type ViewMode } from "@/lib/viewMode";
|
||||||
|
|
||||||
|
const SETTINGS_SHORTCUT = { key: ",", cmd: true } as const;
|
||||||
|
|
||||||
export const Header = () => {
|
export const Header = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
const { viewMode, setViewMode } = useViewMode();
|
const { viewMode, setViewMode } = useViewMode();
|
||||||
const { updateAvailable } = useClientVersion();
|
const { updateAvailable } = useClientVersion();
|
||||||
|
|
||||||
const openSettings = () => {
|
const openSettings = useCallback(() => {
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
void WindowManager.OpenSettings("").catch(() => {});
|
void WindowManager.OpenSettings("").catch(() => {});
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
|
// Mirror the tray's Settings accelerator so the keystroke works while
|
||||||
|
// the main window has focus too. The tray's SetAccelerator paints the
|
||||||
|
// glyph on macOS/Linux but only fires the menu item — it can't reach the
|
||||||
|
// webview's input loop, hence the parallel React-side listener.
|
||||||
|
useKeyboardShortcut(SETTINGS_SHORTCUT, openSettings);
|
||||||
|
|
||||||
const openAbout = () => {
|
const openAbout = () => {
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
@@ -79,9 +89,12 @@ export const Header = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<DropdownMenuItem onClick={openSettings}>
|
<DropdownMenuItem onClick={openSettings}>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 w-full">
|
||||||
<Settings size={14} />
|
<Settings size={14} />
|
||||||
{t("header.menu.settings")}
|
<span className="flex-1">{t("header.menu.settings")}</span>
|
||||||
|
<DropdownMenuShortcut>
|
||||||
|
{formatShortcut(SETTINGS_SHORTCUT)}
|
||||||
|
</DropdownMenuShortcut>
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
|
|||||||
+20
-2
@@ -372,6 +372,20 @@ func (t *Tray) buildMenu() *application.Menu {
|
|||||||
// The tray icon's left-click handler is intentionally unbound (see
|
// The tray icon's left-click handler is intentionally unbound (see
|
||||||
// NewTray for the rationale), so expose the window through an explicit
|
// NewTray for the rationale), so expose the window through an explicit
|
||||||
// menu entry on every platform.
|
// menu entry on every platform.
|
||||||
|
//
|
||||||
|
// Accelerators are wired on the Settings and Quit entries below.
|
||||||
|
// Cross-platform behaviour in Wails v3 alpha.95:
|
||||||
|
// - macOS: SetAccelerator calls NSMenuItem.setKeyEquivalent — the
|
||||||
|
// glyph row paints to the right of the label and the combo fires
|
||||||
|
// when the menu is open OR while the app is the frontmost app.
|
||||||
|
// - Linux (GTK): SetAccelerator binds the GTK accel — the combo
|
||||||
|
// fires while the menu is open and the label paints the row. On
|
||||||
|
// XEmbed/AppIndicator hosts the visual hint may not render but
|
||||||
|
// activation through the keyboard still resolves.
|
||||||
|
// - Windows: SetAccelerator is a no-op in alpha.95 (the impl is
|
||||||
|
// commented out in menuitem_windows.go), so the row is plain
|
||||||
|
// text. We still call it for forward compatibility — a future
|
||||||
|
// Wails release picks the labels up without churn here.
|
||||||
menu.Add(t.loc.T("tray.menu.open")).OnClick(func(*application.Context) { t.ShowWindow() })
|
menu.Add(t.loc.T("tray.menu.open")).OnClick(func(*application.Context) { t.ShowWindow() })
|
||||||
|
|
||||||
menu.AddSeparator()
|
menu.AddSeparator()
|
||||||
@@ -396,7 +410,9 @@ func (t *Tray) buildMenu() *application.Menu {
|
|||||||
// surfaces the day-to-day actions. The trailing ellipsis on the label
|
// surfaces the day-to-day actions. The trailing ellipsis on the label
|
||||||
// (i18n string) follows the macOS HIG convention for menu items that
|
// (i18n string) follows the macOS HIG convention for menu items that
|
||||||
// open a dialog/window rather than performing an inline action.
|
// open a dialog/window rather than performing an inline action.
|
||||||
t.settingsItem = menu.Add(t.loc.T("tray.menu.settings")).OnClick(func(*application.Context) { t.svc.WindowManager.OpenSettings("") })
|
t.settingsItem = menu.Add(t.loc.T("tray.menu.settings")).
|
||||||
|
SetAccelerator("CmdOrCtrl+,").
|
||||||
|
OnClick(func(*application.Context) { t.svc.WindowManager.OpenSettings("") })
|
||||||
|
|
||||||
aboutLabel := t.loc.T("tray.menu.about")
|
aboutLabel := t.loc.T("tray.menu.about")
|
||||||
about := menu.AddSubmenu(aboutLabel)
|
about := menu.AddSubmenu(aboutLabel)
|
||||||
@@ -427,7 +443,9 @@ func (t *Tray) buildMenu() *application.Menu {
|
|||||||
t.updater.attach(updateItem)
|
t.updater.attach(updateItem)
|
||||||
|
|
||||||
menu.AddSeparator()
|
menu.AddSeparator()
|
||||||
menu.Add(t.loc.T("tray.menu.quit")).OnClick(func(*application.Context) { t.app.Quit() })
|
menu.Add(t.loc.T("tray.menu.quit")).
|
||||||
|
SetAccelerator("CmdOrCtrl+Q").
|
||||||
|
OnClick(func(*application.Context) { t.app.Quit() })
|
||||||
|
|
||||||
return menu
|
return menu
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user