From ebac061a1f3003c257cdbe175722236a04b8cfd3 Mon Sep 17 00:00:00 2001 From: Vadim Lazuk Date: Fri, 6 Nov 2026 21:42:55 +0300 Subject: [PATCH] feat(settings): add an option for smaller indents in trees (#1795) Signed-off-by: Inf1mum --- crates/ide-core/src/settings.rs | 3 ++ src/bindings.ts | 2 ++ .../settings/__tests__/behavior.test.ts | 16 ++++++++- src/features/settings/appearanceRuntime.ts | 6 ++++ src/features/settings/model/behaviorSchema.ts | 4 +++ src/features/settings/pages/behavior.tsx | 7 ++++ src/platform/locales/text/zh.json | 2 ++ src/ui/Tree.test.tsx | 34 +++++++++++++++++++ src/ui/Tree.tsx | 9 +++-- 9 files changed, 79 insertions(+), 4 deletions(-) diff --git a/crates/ide-core/src/settings.rs b/crates/ide-core/src/settings.rs index 52a207572..4057c3b86 100644 --- a/crates/ide-core/src/settings.rs +++ b/crates/ide-core/src/settings.rs @@ -367,6 +367,8 @@ pub struct AppearanceSettings { pub red_green_vision: bool, pub different_tool_window_background: bool, pub compact_mode: bool, + /// IDEA "Use smaller indents in trees": tool window trees indent less per level. + pub small_tree_indents: bool, pub full_path_in_title: bool, pub project_colors_in_toolbar: bool, pub dnd_with_alt_only: bool, @@ -437,6 +439,7 @@ impl Default for AppearanceSettings { red_green_vision: false, different_tool_window_background: false, compact_mode: false, + small_tree_indents: false, full_path_in_title: false, project_colors_in_toolbar: true, dnd_with_alt_only: false, diff --git a/src/bindings.ts b/src/bindings.ts index 07fce559e..6c85852eb 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -1760,6 +1760,8 @@ export type AppearanceSettings = { 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, diff --git a/src/features/settings/__tests__/behavior.test.ts b/src/features/settings/__tests__/behavior.test.ts index 8d1ab1e82..f91a51b60 100644 --- a/src/features/settings/__tests__/behavior.test.ts +++ b/src/features/settings/__tests__/behavior.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { formatPattern, formatTimestamp } from "@/platform/dateFormat"; import { resolveThemeId } from "@/workbench/theme"; -import { applyAppearanceOptions, nextZoom } from "../appearanceRuntime"; +import { applyAppearanceOptions, nextZoom, SMALL_TREE_INDENT } from "../appearanceRuntime"; import { validateSettings } from "../model/schema"; import { validateAppearanceExtras, validateNotifications } from "../model/behaviorSchema"; @@ -69,6 +69,20 @@ describe("appearance & behavior settings", () => { expect(validateAppearanceExtras({ showToolWindowBadges: 1 }).showToolWindowBadges).toBe(true); }); + it("defaults Use smaller indents in trees to off and applies the indent variable", () => { + expect(validateAppearanceExtras({}).smallTreeIndents).toBe(false); + expect(validateAppearanceExtras({ smallTreeIndents: true }).smallTreeIndents).toBe(true); + expect(validateAppearanceExtras({ smallTreeIndents: "on" }).smallTreeIndents).toBe(false); + const root = document.createElement("div"); + applyAppearanceOptions(validateSettings({ appearance: { smallTreeIndents: true } }), root); + expect(root.style.getPropertyValue("--tree-indent")).toBe(SMALL_TREE_INDENT); + // Off (or a stored non-boolean) restores the default indents. + applyAppearanceOptions(validateSettings({}), root); + expect(root.style.getPropertyValue("--tree-indent")).toBe(""); + applyAppearanceOptions(validateSettings({ appearance: { smallTreeIndents: false } }), root); + expect(root.style.getPropertyValue("--tree-indent")).toBe(""); + }); + it("keeps the UI fallback font empty by default and trims a stored one", () => { expect(validateAppearanceExtras({}).uiFontFallback).toBe(""); expect(validateAppearanceExtras({ uiFontFallback: " 思源黑体 " }).uiFontFallback).toBe("思源黑体"); diff --git a/src/features/settings/appearanceRuntime.ts b/src/features/settings/appearanceRuntime.ts index c60c5744b..311afd2b2 100644 --- a/src/features/settings/appearanceRuntime.ts +++ b/src/features/settings/appearanceRuntime.ts @@ -24,6 +24,9 @@ function flag(root: HTMLElement, name: string, on: boolean, value = "") { else delete root.dataset[name]; } +/** The per-level indent `ui/Tree` reads when "Use smaller indents in trees" is on (IDEA parity). */ +export const SMALL_TREE_INDENT = "12px"; + /** Root attributes and CSS variables for the Appearance options. */ export function applyAppearanceOptions(s: ValidSettings, root: HTMLElement = document.documentElement): void { const a = s.appearance; @@ -42,6 +45,9 @@ export function applyAppearanceOptions(s: ValidSettings, root: HTMLElement = doc flag(root, "toolWindowBg", a.differentToolWindowBackground, "different"); flag(root, "projectColors", !a.projectColorsInToolbar, "off"); flag(root, "menuIcons", !a.menuIcons, "off"); + // "Use smaller indents in trees": `ui/Tree` reads the variable as its per-level indent. + if (a.smallTreeIndents) root.style.setProperty("--tree-indent", SMALL_TREE_INDENT); + else root.style.removeProperty("--tree-indent"); root.dataset.aaIde = a.ideAntialiasing; root.dataset.aaEditor = a.editorAntialiasing; applyZoom(a.zoom, root); diff --git a/src/features/settings/model/behaviorSchema.ts b/src/features/settings/model/behaviorSchema.ts index bd21bfd78..e72fa31c7 100644 --- a/src/features/settings/model/behaviorSchema.ts +++ b/src/features/settings/model/behaviorSchema.ts @@ -30,6 +30,8 @@ export interface AppearanceExtras { 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; @@ -88,6 +90,7 @@ export const DEFAULT_APPEARANCE_EXTRAS: AppearanceExtras = { redGreenVision: false, differentToolWindowBackground: false, compactMode: false, + smallTreeIndents: false, fullPathInTitle: false, projectColorsInToolbar: true, dndWithAltOnly: false, @@ -144,6 +147,7 @@ export function validateAppearanceExtras(raw: unknown): AppearanceExtras { redGreenVision: b("redGreenVision"), differentToolWindowBackground: b("differentToolWindowBackground"), compactMode: b("compactMode"), + smallTreeIndents: b("smallTreeIndents"), fullPathInTitle: b("fullPathInTitle"), projectColorsInToolbar: b("projectColorsInToolbar"), dndWithAltOnly: b("dndWithAltOnly"), diff --git a/src/features/settings/pages/behavior.tsx b/src/features/settings/pages/behavior.tsx index 92d6b7400..ca58b2d44 100644 --- a/src/features/settings/pages/behavior.tsx +++ b/src/features/settings/pages/behavior.tsx @@ -338,6 +338,11 @@ function UiOptionsGrid() { label="Drag-and-drop with Alt pressed only" hint="Applies to editor tabs and tool window buttons" /> + } @@ -646,6 +651,8 @@ export const BEHAVIOR_PAGES: PageSpec[] = [ "Use project colors in main toolbar", "Drag-and-drop with Alt pressed only", "Display icons in menu items", + "Use smaller indents in trees", + "tree indent", ], component: UiOptionsGrid, }, diff --git a/src/platform/locales/text/zh.json b/src/platform/locales/text/zh.json index 4fd67605a..dd4819269 100644 --- a/src/platform/locales/text/zh.json +++ b/src/platform/locales/text/zh.json @@ -1646,6 +1646,7 @@ "Project Errors": "项目错误", "Project Files": "项目文件", "Project GOPATH": "项目 GOPATH", + "Project, Structure, Commit and other tool window trees indent less per level": "项目、结构、提交等工具窗口树的每层缩进更小", "Project JDK": "项目 JDK", "Project Production Files": "项目生产文件", "Project Settings": "项目设置", @@ -2478,6 +2479,7 @@ "Use original line's indent for wrapped parts": "换行部分使用原始行的缩进", "Use pretty formatting": "使用友好格式", "Use project colors in main toolbar": "在主工具栏中使用项目颜色", + "Use smaller indents in trees": "使用较小的树缩进", "Use smooth caret blinking": "使用平滑光标闪烁", "Use smooth caret movement": "使用平滑光标移动", "Use soft wraps in console": "在控制台中使用软换行", diff --git a/src/ui/Tree.test.tsx b/src/ui/Tree.test.tsx index b75891616..94a3edae1 100644 --- a/src/ui/Tree.test.tsx +++ b/src/ui/Tree.test.tsx @@ -466,3 +466,37 @@ describe("Tree scroll following", () => { expect(scrollTo).toHaveBeenCalledTimes(1); }); }); + +describe("Tree indents", () => { + // Settings › Appearance › "Use smaller indents in trees" sets `--tree-indent` + // on the root; the row padding reads it so the setting reaches every tree at + // once, without a restart, and chevrons keep aligning with it. + const pads = (root: HTMLElement) => + within(root) + .queryAllByRole("treeitem") + .map((el) => el.style.paddingLeft); + + it("pads each level through --tree-indent, falling back to the indent prop", () => { + const { tree } = setup({ defaultExpanded: ["src"] }); + expect(pads(tree)).toEqual([ + "calc(4px + 0 * var(--tree-indent, 20px))", // src + "calc(4px + 1 * var(--tree-indent, 20px))", // main + "calc(4px + 1 * var(--tree-indent, 20px))", // util.ts + "calc(4px + 0 * var(--tree-indent, 20px))", // README.md + ]); + }); + + it("honours a custom indent prop as the fallback value", () => { + const { tree } = setup({ defaultExpanded: ["src"], indent: 16 }); + expect(pads(tree)[1]).toBe("calc(4px + 1 * var(--tree-indent, 16px))"); + }); + + it("indents lazy-loading placeholder rows past their parent level", () => { + const { tree, key } = setup({ getChildren: () => undefined, loadChildren: () => undefined }); + key("ArrowDown"); + key("ArrowRight"); + // Loading placeholders are `role="presentation"`, not tree items. + const placeholder = tree.querySelector('[role="presentation"]'); + expect(placeholder?.style.paddingLeft).toBe("calc(24px + 1 * var(--tree-indent, 20px))"); + }); +}); diff --git a/src/ui/Tree.tsx b/src/ui/Tree.tsx index 84e5f0ceb..5d29a7c8d 100644 --- a/src/ui/Tree.tsx +++ b/src/ui/Tree.tsx @@ -611,14 +611,17 @@ export function Tree(props: TreeProps) { )} {virtualizer.getVirtualItems().map((v) => { const row = rows[v.index]!; - const pad = 4 + row.depth * indent; - const style = { transform: `translateY(${v.start}px)`, height: v.size, paddingLeft: pad }; + // Settings › Appearance › "Use smaller indents in trees" sets `--tree-indent` + // on the root; reading it here applies the setting to every tree at once, + // instantly, and keeps chevrons and level alignment consistent with it. + const pad = (extra = 0) => `calc(${4 + extra}px + ${row.depth} * var(--tree-indent, ${indent}px))`; + const style = { transform: `translateY(${v.start}px)`, height: v.size, paddingLeft: pad() }; if (row.loading) { return (
Loading…