дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
feat(workbench): add merged main menu style (#561)
* feat(workbench): add merged main menu style * fix(workbench): restore focus on a second Alt and keep closed menus closed
Цей коміт міститься в:
1 джерело
bdadecdf91
коміт
e7740e4032
10 files changed
+562
-59
No files matched your search
@@ -0,0 +1,46 @@
|
||||
import { act, fireEvent, render, screen } from "@testing-library/react";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { usePlatform } from "@/platform/platformInfo";
|
||||
import { validateAppearanceExtras } from "../model/behaviorSchema";
|
||||
import { useSettingsDialog } from "../model/staging";
|
||||
import { BEHAVIOR_PAGES } from "../pages/behavior";
|
||||
|
||||
const savedPlatform = usePlatform.getState();
|
||||
const savedDialog = useSettingsDialog.getState();
|
||||
afterEach(() => {
|
||||
usePlatform.setState(savedPlatform);
|
||||
useSettingsDialog.setState(savedDialog);
|
||||
});
|
||||
const option = BEHAVIOR_PAGES.find((p) => p.id === "appearance")!
|
||||
.sections!.find((s) => s.title === "UI Options")!
|
||||
.options.find((o) => o.kind === "custom")!;
|
||||
if (option.kind !== "custom") throw new Error("UI Options missing");
|
||||
const MainMenuStyleRow = option.component;
|
||||
|
||||
describe("Main menu appearance setting", () => {
|
||||
it.each(["windows", "linux"])("offers both styles on a frameless %s title bar", async (os) => {
|
||||
usePlatform.setState({ os, framelessTitleBar: true });
|
||||
render(<MainMenuStyleRow />);
|
||||
const select = screen.getByRole("button", { name: "Main menu" });
|
||||
expect(select).toHaveTextContent("Hamburger button");
|
||||
fireEvent.click(select);
|
||||
fireEvent.click(await screen.findByRole("option", { name: "Merged with main toolbar" }));
|
||||
expect(select).toHaveTextContent("Merged with main toolbar");
|
||||
expect(useSettingsDialog.getState().staged["global|appearance.mainMenuStyle"]?.value).toBe("merged");
|
||||
});
|
||||
|
||||
it.each(["macos", "web", "linux"])("hides the setting without the frameless menu (%s)", (os) => {
|
||||
usePlatform.setState({ os, framelessTitleBar: false });
|
||||
render(<MainMenuStyleRow />);
|
||||
expect(screen.queryByText("Main menu")).toBeNull();
|
||||
expect(screen.queryByRole("button", { name: "Main menu" })).toBeNull();
|
||||
act(() => usePlatform.setState({ os: "windows", framelessTitleBar: true }));
|
||||
expect(screen.getByRole("button", { name: "Main menu" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("preserves the hamburger default for existing or invalid settings", () => {
|
||||
expect(validateAppearanceExtras({}).mainMenuStyle).toBe("hamburger");
|
||||
expect(validateAppearanceExtras({ mainMenuStyle: "unknown" }).mainMenuStyle).toBe("hamburger");
|
||||
expect(validateAppearanceExtras({ mainMenuStyle: "merged" }).mainMenuStyle).toBe("merged");
|
||||
});
|
||||
});
|
||||
@@ -27,6 +27,7 @@ export interface AppearanceExtras {
|
||||
projectColorsInToolbar: boolean;
|
||||
dndWithAltOnly: boolean;
|
||||
menuIcons: boolean;
|
||||
mainMenuStyle: "hamburger" | "merged";
|
||||
ideAntialiasing: Antialiasing;
|
||||
editorAntialiasing: Antialiasing;
|
||||
showToolWindowBars: boolean;
|
||||
@@ -78,6 +79,7 @@ export const DEFAULT_APPEARANCE_EXTRAS: AppearanceExtras = {
|
||||
projectColorsInToolbar: true,
|
||||
dndWithAltOnly: false,
|
||||
menuIcons: true,
|
||||
mainMenuStyle: "hamburger",
|
||||
ideAntialiasing: "greyscale",
|
||||
editorAntialiasing: "greyscale",
|
||||
showToolWindowBars: true,
|
||||
@@ -127,6 +129,7 @@ export function validateAppearanceExtras(raw: unknown): AppearanceExtras {
|
||||
projectColorsInToolbar: b("projectColorsInToolbar"),
|
||||
dndWithAltOnly: b("dndWithAltOnly"),
|
||||
menuIcons: b("menuIcons"),
|
||||
mainMenuStyle: oneOf(a.mainMenuStyle, ["hamburger", "merged"], d.mainMenuStyle),
|
||||
ideAntialiasing: oneOf(a.ideAntialiasing, AA, d.ideAntialiasing),
|
||||
editorAntialiasing: oneOf(a.editorAntialiasing, AA, d.editorAntialiasing),
|
||||
showToolWindowBars: b("showToolWindowBars"),
|
||||
|
||||
@@ -9,6 +9,7 @@ import { FontPicker } from "../components/FontPicker";
|
||||
import { useId, useRef, useState, type ReactNode } from "react";
|
||||
import clsx from "clsx";
|
||||
import { shortcutLabel } from "@/platform/keymap";
|
||||
import { usePlatform } from "@/platform/platformInfo";
|
||||
import { isTauri } from "@/platform/ipc";
|
||||
import { SCHEMES as EDITOR_SCHEMES, listSchemes, customSchemes } from "@/platform/colorScheme";
|
||||
import { formatTimestamp } from "@/platform/dateFormat";
|
||||
@@ -223,6 +224,25 @@ function Grid({ left, right }: { left: ReactNode; right: ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
function MainMenuStyleRow() {
|
||||
const frameless = usePlatform((s) => s.framelessTitleBar);
|
||||
const [mainMenuStyle, setMainMenuStyle] = useSetting<"hamburger" | "merged">("appearance.mainMenuStyle");
|
||||
if (!frameless) return null;
|
||||
return (
|
||||
<Row label="Main menu">
|
||||
<Select
|
||||
value={mainMenuStyle ?? "hamburger"}
|
||||
options={[
|
||||
{ value: "hamburger", label: "Hamburger button" },
|
||||
{ value: "merged", label: "Merged with main toolbar" },
|
||||
]}
|
||||
onChange={setMainMenuStyle}
|
||||
aria-label="Main menu"
|
||||
/>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
function UiOptionsGrid() {
|
||||
return (
|
||||
<Grid
|
||||
@@ -521,6 +541,12 @@ export const BEHAVIOR_PAGES: PageSpec[] = [
|
||||
{
|
||||
title: "UI Options",
|
||||
options: [
|
||||
{
|
||||
kind: "custom",
|
||||
label: "Main menu",
|
||||
keywords: ["Hamburger button", "Merged with main toolbar"],
|
||||
component: MainMenuStyleRow,
|
||||
},
|
||||
{
|
||||
kind: "custom",
|
||||
label: "Compact mode",
|
||||
|
||||
Reference in new issue
Заблокувати користувача