Файли
openidea/src/features/settings/model/behaviorSchema.ts
T
lleviathe 8dd29bad41 feat(workbench): toggle the toolbar, status bar and tool window bars from View › Appearance (#2206)
The main toolbar and status bar could not be hidden, Tool Window Bars was a Settings row only, and widgets could be switched only from the status bar's context menu. View > Appearance now has Toolbar, Tool Window Bars, Status Bar and Status Bar Widgets, with check marks in the macOS and in-app menus, plus Settings toggles. Both new settings default to shown. With the toolbar hidden, the window keeps a compact header with the title, which Distraction Free now shares.

Controls stay reachable with a bar hidden: the Git branches popup and background tasks have their own hosts, and background tasks has an action.

Co-authored-by: lleviathe <[email protected]>
2026-11-09 07:20:54 +08:00

208 рядки
7.9 KiB
TypeScript
Неформатований Анотація Історія

Цей файл містить неоднозначні символи Юнікоду
Цей файл містить символи Юнікоду, які можна сплутати з іншими символами. Якщо ви вважаєте, що це зроблено навмисно, можете сміливо ігнорувати це попередження. Щоб показати їх, скористайтеся кнопкою Escape.
/**
* Appearance & Behavior settings beyond the basics in `schema.ts`: extra
* Appearance / System Settings fields and the Notifications page. Mirrors the
* `ide-core::settings` defaults; validation never throws.
*/
import { isThemeChoiceId, type ThemeChoiceId } from "@/platform/themes";
import type { FileIconThemeOptions } from "@/bindings";
import { ZOOM_LIMITS } from "@/platform/zoom";
import { validateIconThemeOptionsMap } from "@/platform/iconThemeOptions";
export type Antialiasing = "subpixel" | "greyscale" | "none";
export type OpenProjectIn = "ask" | "new" | "same" | "attach";
export type NotificationDisplay = "balloon" | "sticky" | "none";
/** Themes selectable as "preferred" when syncing with the OS. */
export type ConcreteTheme = ThemeChoiceId;
export interface AppearanceExtras {
syncWithOs: boolean;
preferredLightTheme: ConcreteTheme;
preferredDarkTheme: ConcreteTheme;
/** A plugin's file icon theme (`<pluginId>/<themeId>`); empty for the built-in icons. */
fileIconTheme: string;
fileIconThemeOptions: Record<string, FileIconThemeOptions>;
zoom: number;
useCustomFont: boolean;
uiFontFamily: string;
uiFontFallback: string;
contrastScrollbars: boolean;
redGreenVision: boolean;
differentToolWindowBackground: boolean;
compactMode: boolean;
/** IDEA "Use smaller indents in trees": tool window trees indent less per level. */
smallTreeIndents: boolean;
fullPathInTitle: boolean;
projectColorsInToolbar: boolean;
dndWithAltOnly: boolean;
menuIcons: boolean;
mainMenuStyle: "hamburger" | "merged";
/** A lone Alt press focuses or opens the main menu (IDEA `ide.windows.main.menu.focus.on.alt`). */
mainMenuAlt: boolean;
ideAntialiasing: Antialiasing;
editorAntialiasing: Antialiasing;
showToolWindowBars: boolean;
showToolWindowNames: boolean;
showToolWindowBadges: boolean;
widescreenToolWindows: boolean;
/** View › Appearance › Toolbar; hidden leaves IDEA's compact window header. */
showMainToolbar: boolean;
showStatusBar: boolean;
}
export interface DateFormatValid {
overrideSystem: boolean;
pattern: string;
use24Hour: boolean;
pretty: boolean;
}
export type UpdateSourceSetting = "auto" | "github" | "cnb";
export interface GeneralExtras {
openProjectIn: OpenProjectIn;
updateSource: UpdateSourceSetting;
defaultProjectDirectory: string;
saveOnIdle: boolean;
idleSeconds: number;
dateFormat: DateFormatValid;
}
export interface NotificationGroupValid {
display: NotificationDisplay;
log: boolean;
}
export interface NotificationsValid {
balloons: boolean;
sound: boolean;
groups: Record<string, NotificationGroupValid>;
}
export const IDLE_LIMITS = { min: 1, max: 3600 } as const;
export const DEFAULT_APPEARANCE_EXTRAS: AppearanceExtras = {
syncWithOs: false,
preferredLightTheme: "light",
preferredDarkTheme: "dark",
fileIconTheme: "",
fileIconThemeOptions: {},
zoom: 100,
useCustomFont: false,
uiFontFamily: "Inter",
uiFontFallback: "",
contrastScrollbars: false,
redGreenVision: false,
differentToolWindowBackground: false,
compactMode: false,
smallTreeIndents: false,
fullPathInTitle: false,
projectColorsInToolbar: true,
dndWithAltOnly: false,
menuIcons: true,
mainMenuStyle: "hamburger",
mainMenuAlt: true,
ideAntialiasing: "greyscale",
editorAntialiasing: "greyscale",
showToolWindowBars: true,
showToolWindowNames: false,
showToolWindowBadges: true,
widescreenToolWindows: false,
showMainToolbar: true,
showStatusBar: true,
};
export const DEFAULT_GENERAL_EXTRAS: GeneralExtras = {
openProjectIn: "ask",
updateSource: "auto",
defaultProjectDirectory: "",
saveOnIdle: false,
idleSeconds: 15,
dateFormat: { overrideSystem: false, pattern: "dd.MM.yyyy", use24Hour: true, pretty: true },
};
export const DEFAULT_NOTIFICATIONS: NotificationsValid = { balloons: true, sound: false, groups: {} };
type Obj = Record<string, unknown>;
const obj = (v: unknown): Obj => (v && typeof v === "object" && !Array.isArray(v) ? (v as Obj) : {});
const bool = (v: unknown, d: boolean) => (typeof v === "boolean" ? v : d);
const oneOf = <T extends string>(v: unknown, allowed: readonly T[], d: T): T =>
typeof v === "string" && (allowed as readonly string[]).includes(v) ? (v as T) : d;
const int = (v: unknown, d: number, { min, max }: { min: number; max: number }) =>
typeof v === "number" && Number.isFinite(v) ? Math.round(Math.min(max, Math.max(min, v))) : d;
const AA: readonly Antialiasing[] = ["subpixel", "greyscale", "none"];
const concrete = (v: unknown, d: ConcreteTheme): ConcreteTheme => (isThemeChoiceId(v) ? v : d);
const DISPLAYS: readonly NotificationDisplay[] = ["balloon", "sticky", "none"];
export function validateAppearanceExtras(raw: unknown): AppearanceExtras {
const a = obj(raw);
const d = DEFAULT_APPEARANCE_EXTRAS;
const b = (k: keyof AppearanceExtras) => bool(a[k], d[k] as boolean);
return {
// A legacy `theme = "system"` means syncing too.
syncWithOs: bool(a.syncWithOs, d.syncWithOs) || a.theme === "system",
preferredLightTheme: concrete(a.preferredLightTheme, d.preferredLightTheme),
preferredDarkTheme: concrete(a.preferredDarkTheme, d.preferredDarkTheme),
fileIconTheme:
typeof a.fileIconTheme === "string" && /^[^/\s]+\/[^/\s]+$/.test(a.fileIconTheme) ? a.fileIconTheme : "",
fileIconThemeOptions: validateIconThemeOptionsMap(a.fileIconThemeOptions),
zoom: int(a.zoom, d.zoom, ZOOM_LIMITS),
useCustomFont: b("useCustomFont"),
uiFontFamily: typeof a.uiFontFamily === "string" && a.uiFontFamily.trim() ? a.uiFontFamily.trim() : d.uiFontFamily,
uiFontFallback: typeof a.uiFontFallback === "string" ? a.uiFontFallback.trim() : d.uiFontFallback,
contrastScrollbars: b("contrastScrollbars"),
redGreenVision: b("redGreenVision"),
differentToolWindowBackground: b("differentToolWindowBackground"),
compactMode: b("compactMode"),
smallTreeIndents: b("smallTreeIndents"),
fullPathInTitle: b("fullPathInTitle"),
projectColorsInToolbar: b("projectColorsInToolbar"),
dndWithAltOnly: b("dndWithAltOnly"),
menuIcons: b("menuIcons"),
mainMenuStyle: oneOf(a.mainMenuStyle, ["hamburger", "merged"], d.mainMenuStyle),
mainMenuAlt: b("mainMenuAlt"),
ideAntialiasing: oneOf(a.ideAntialiasing, AA, d.ideAntialiasing),
editorAntialiasing: oneOf(a.editorAntialiasing, AA, d.editorAntialiasing),
showToolWindowBars: b("showToolWindowBars"),
showToolWindowNames: b("showToolWindowNames"),
showToolWindowBadges: b("showToolWindowBadges"),
widescreenToolWindows: b("widescreenToolWindows"),
showMainToolbar: b("showMainToolbar"),
showStatusBar: b("showStatusBar"),
};
}
export function validateGeneralExtras(raw: unknown): GeneralExtras {
const g = obj(raw);
const d = DEFAULT_GENERAL_EXTRAS;
const df = obj(g.dateFormat);
return {
openProjectIn: oneOf(g.openProjectIn, ["ask", "new", "same", "attach"] as const, d.openProjectIn),
updateSource: oneOf(g.updateSource, ["auto", "github", "cnb"] as const, d.updateSource),
defaultProjectDirectory: typeof g.defaultProjectDirectory === "string" ? g.defaultProjectDirectory : "",
saveOnIdle: bool(g.saveOnIdle, d.saveOnIdle),
idleSeconds: int(g.idleSeconds, d.idleSeconds, IDLE_LIMITS),
dateFormat: {
overrideSystem: bool(df.overrideSystem, d.dateFormat.overrideSystem),
pattern: typeof df.pattern === "string" && df.pattern.trim() ? df.pattern : d.dateFormat.pattern,
use24Hour: bool(df.use24Hour, d.dateFormat.use24Hour),
pretty: bool(df.pretty, d.dateFormat.pretty),
},
};
}
export function validateNotifications(raw: unknown): NotificationsValid {
const n = obj(raw);
const groups: Record<string, NotificationGroupValid> = {};
for (const [id, v] of Object.entries(obj(n.groups))) {
const g = obj(v);
groups[id] = { display: oneOf(g.display, DISPLAYS, "balloon"), log: bool(g.log, true) };
}
return {
balloons: bool(n.balloons, DEFAULT_NOTIFICATIONS.balloons),
sound: bool(n.sound, DEFAULT_NOTIFICATIONS.sound),
groups,
};
}