Refactor the old menu bar plumbing to use standard TextButtons (#3444)

* Refactor the old menu bar plumbing to use standard TextButtons

* WIP: Fix Mac native menu bar

* WIP: fix desktop menu bar mac

* Refactor menu bar definitions to use the builder pattern

* WIP: fixup desktop

* cleanup

* fix linux

* Remove dead code that was failing to lint

---------

Co-authored-by: Timon Schelling <me@timon.zip>
This commit is contained in:
Keavon Chambers
2025-12-03 12:41:54 +00:00
committed by GitHub
co-authored by Timon Schelling
parent 3fd0460d03
commit 600fb5c28f
34 changed files with 1222 additions and 1355 deletions
@@ -2,12 +2,11 @@
import { getContext, onMount } from "svelte";
import type { Editor } from "@graphite/editor";
import { type KeyRaw, type LayoutKeysGroup, type MenuBarEntry, type MenuListEntry, UpdateMenuBarLayout } from "@graphite/messages";
import { defaultWidgetLayout, patchWidgetLayout, UpdateMenuBarLayout } from "@graphite/messages";
import type { AppWindowState } from "@graphite/state-providers/app-window";
import { operatingSystem } from "@graphite/utility-functions/platform";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte";
import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte";
import WindowButtonsLinux from "@graphite/components/window/title-bar/WindowButtonsLinux.svelte";
import WindowButtonsWeb from "@graphite/components/window/title-bar/WindowButtonsWeb.svelte";
import WindowButtonsWindows from "@graphite/components/window/title-bar/WindowButtonsWindows.svelte";
@@ -15,44 +14,12 @@
const appWindow = getContext<AppWindowState>("appWindow");
const editor = getContext<Editor>("editor");
// TODO: Apparently, Safari does not support the Keyboard.lock() API but does relax its authority over certain keyboard shortcuts in fullscreen mode, which we should take advantage of
const ACCEL_KEY = operatingSystem() === "Mac" ? "Command" : "Control";
const LOCK_REQUIRING_SHORTCUTS: KeyRaw[][] = [
[ACCEL_KEY, "KeyW"],
[ACCEL_KEY, "KeyN"],
[ACCEL_KEY, "Shift", "KeyN"],
[ACCEL_KEY, "KeyT"],
[ACCEL_KEY, "Shift", "KeyT"],
];
let entries: MenuListEntry[] = [];
let menuBarLayout = defaultWidgetLayout();
onMount(() => {
const arraysEqual = (a: KeyRaw[], b: KeyRaw[]): boolean => a.length === b.length && a.every((aValue, i) => aValue === b[i]);
const shortcutRequiresLock = (shortcut: LayoutKeysGroup): boolean => {
const shortcutKeys = shortcut.map((keyWithLabel) => keyWithLabel.key);
// If this shortcut matches any of the browser-reserved shortcuts
return LOCK_REQUIRING_SHORTCUTS.some((lockKeyCombo) => arraysEqual(shortcutKeys, lockKeyCombo));
};
editor.subscriptions.subscribeJsMessage(UpdateMenuBarLayout, (updateMenuBarLayout) => {
const menuBarEntryToMenuListEntry = (entry: MenuBarEntry): MenuListEntry => ({
// From `MenuEntryCommon`
...entry,
// Shared names with fields that need to be converted from the type used in `MenuBarEntry` to that of `MenuListEntry`
action: () => editor.handle.widgetValueCommitAndUpdate(updateMenuBarLayout.layoutTarget, entry.action.widgetId, undefined),
children: entry.children ? entry.children.map((entries) => entries.map((entry) => menuBarEntryToMenuListEntry(entry))) : undefined,
// New fields in `MenuListEntry`
shortcutRequiresLock: entry.shortcut ? shortcutRequiresLock(entry.shortcut.keys) : undefined,
value: "",
disabled: entry.disabled ?? undefined,
font: undefined,
});
entries = updateMenuBarLayout.layout.map(menuBarEntryToMenuListEntry);
patchWidgetLayout(menuBarLayout, updateMenuBarLayout);
menuBarLayout = menuBarLayout;
});
});
</script>
@@ -61,9 +28,7 @@
<!-- Menu bar -->
<LayoutRow>
{#if $appWindow.platform !== "Mac"}
{#each entries as entry}
<TextButton label={entry.label} icon={entry.icon} menuListChildren={entry.children} action={entry.action} flush={true} />
{/each}
<WidgetLayout layout={menuBarLayout} />
{/if}
</LayoutRow>
<!-- Spacer -->
@@ -88,6 +53,14 @@
> .layout-row {
flex: 0 0 auto;
> .widget-span {
--row-height: 28px;
> * {
--widget-height: 28px;
}
}
&.spacer {
flex: 1 1 100%;
}