@tuidom/elements 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/body/bodyElement.d.ts +33 -0
- package/dist/body/bodyElement.js +87 -0
- package/dist/button/buttonElement.d.ts +18 -0
- package/dist/button/buttonElement.js +68 -0
- package/dist/completionlist/completionDetailsElement.d.ts +49 -0
- package/dist/completionlist/completionDetailsElement.js +165 -0
- package/dist/completionlist/completionItemKindIcon.d.ts +2 -0
- package/dist/completionlist/completionItemKindIcon.js +43 -0
- package/dist/completionlist/completionListElement.d.ts +102 -0
- package/dist/completionlist/completionListElement.js +309 -0
- package/dist/completionlist/completionWidgetElement.d.ts +41 -0
- package/dist/completionlist/completionWidgetElement.js +102 -0
- package/dist/contextview/contextMenuController.d.ts +23 -0
- package/dist/contextview/contextMenuController.js +75 -0
- package/dist/contextview/overlayHostElement.d.ts +23 -0
- package/dist/contextview/overlayHostElement.js +47 -0
- package/dist/editorgroup/editorTabItemElement.d.ts +41 -0
- package/dist/editorgroup/editorTabItemElement.js +186 -0
- package/dist/editorgroup/editorTabStripElement.d.ts +33 -0
- package/dist/editorgroup/editorTabStripElement.js +118 -0
- package/dist/editorpart/editorPartElement.d.ts +95 -0
- package/dist/editorpart/editorPartElement.js +285 -0
- package/dist/inputbox/inputElement.d.ts +34 -0
- package/dist/inputbox/inputElement.js +207 -0
- package/dist/inputbox/inputState.d.ts +69 -0
- package/dist/inputbox/inputState.js +326 -0
- package/dist/layout/boxContainerElement.d.ts +27 -0
- package/dist/layout/boxContainerElement.js +121 -0
- package/dist/layout/boxElement.d.ts +4 -0
- package/dist/layout/boxElement.js +30 -0
- package/dist/layout/fillerElement.d.ts +12 -0
- package/dist/layout/fillerElement.js +16 -0
- package/dist/layout/fitContentElement.d.ts +18 -0
- package/dist/layout/fitContentElement.js +46 -0
- package/dist/layout/hFlexElement.d.ts +26 -0
- package/dist/layout/hFlexElement.js +134 -0
- package/dist/layout/paddingContainerElement.d.ts +30 -0
- package/dist/layout/paddingContainerElement.js +88 -0
- package/dist/layout/sizedBoxElement.d.ts +30 -0
- package/dist/layout/sizedBoxElement.js +74 -0
- package/dist/layout/vFlexElement.d.ts +32 -0
- package/dist/layout/vFlexElement.js +136 -0
- package/dist/layout/vStackElement.d.ts +18 -0
- package/dist/layout/vStackElement.js +74 -0
- package/dist/list/listViewElement.d.ts +232 -0
- package/dist/list/listViewElement.js +944 -0
- package/dist/menu/menuBarElement.d.ts +47 -0
- package/dist/menu/menuBarElement.js +285 -0
- package/dist/menu/menuBarItemElement.d.ts +20 -0
- package/dist/menu/menuBarItemElement.js +68 -0
- package/dist/menu/popupMenuElement.d.ts +81 -0
- package/dist/menu/popupMenuElement.js +318 -0
- package/dist/menu/popupMenuItemElement.d.ts +24 -0
- package/dist/menu/popupMenuItemElement.js +89 -0
- package/dist/panel/panelContainerElement.d.ts +64 -0
- package/dist/panel/panelContainerElement.js +219 -0
- package/dist/quickpick/quickPickElement.d.ts +161 -0
- package/dist/quickpick/quickPickElement.js +561 -0
- package/dist/sash/sashElement.d.ts +30 -0
- package/dist/sash/sashElement.js +78 -0
- package/dist/scrollbar/iScrollable.d.ts +8 -0
- package/dist/scrollbar/iScrollable.js +1 -0
- package/dist/scrollbar/scrollBarRenderer.d.ts +36 -0
- package/dist/scrollbar/scrollBarRenderer.js +76 -0
- package/dist/scrollbar/scrollContainerElement.d.ts +39 -0
- package/dist/scrollbar/scrollContainerElement.js +91 -0
- package/dist/scrollbar/scrollViewport.d.ts +28 -0
- package/dist/scrollbar/scrollViewport.js +65 -0
- package/dist/scrollbar/scrollableElement.d.ts +22 -0
- package/dist/scrollbar/scrollableElement.js +42 -0
- package/dist/selectbox/selectBoxElement.d.ts +48 -0
- package/dist/selectbox/selectBoxElement.js +161 -0
- package/dist/terminal/encodeKeyForPty.d.ts +3 -0
- package/dist/terminal/encodeKeyForPty.js +56 -0
- package/dist/terminal/terminalViewElement.d.ts +17 -0
- package/dist/terminal/terminalViewElement.js +172 -0
- package/dist/text/textBlockElement.d.ts +13 -0
- package/dist/text/textBlockElement.js +35 -0
- package/dist/text/textLabelElement.d.ts +28 -0
- package/dist/text/textLabelElement.js +77 -0
- package/dist/titledpanel/titledPanelElement.d.ts +14 -0
- package/dist/titledpanel/titledPanelElement.js +40 -0
- package/dist/tree/iTreeDataProvider.d.ts +19 -0
- package/dist/tree/iTreeDataProvider.js +1 -0
- package/dist/tree/treeViewElement.d.ts +116 -0
- package/dist/tree/treeViewElement.js +748 -0
- package/dist/workbenchlayout/workbenchLayoutElement.d.ts +60 -0
- package/dist/workbenchlayout/workbenchLayoutElement.js +203 -0
- package/package.json +33 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { BoxConstraints, Offset, Point, Rect, Size } from "@tuidom/core/common/geometryPromitives";
|
|
2
|
+
import { StyleFlags } from "@tuidom/core/common/styleFlags";
|
|
3
|
+
import { RenderContext, TUIElement } from "@tuidom/core/dom/tuiElement";
|
|
4
|
+
/** One space of padding on each side of a tab title. */
|
|
5
|
+
const TAB_PAD = 1;
|
|
6
|
+
/** Indent of the tab strip from the left edge. */
|
|
7
|
+
const TAB_INDENT = 1;
|
|
8
|
+
/** Row the tab header sits on (below the top border strip). */
|
|
9
|
+
const TAB_ROW = 1;
|
|
10
|
+
/** First content row (below the top border strip + tab header). */
|
|
11
|
+
const CONTENT_TOP = 2;
|
|
12
|
+
/** Left indent of the content area / placeholder (aligns under the tab label). */
|
|
13
|
+
const CONTENT_LEFT = 2;
|
|
14
|
+
/**
|
|
15
|
+
* The bottom **Panel** part (VS Code `ViewContainerLocation.Panel`): a top border
|
|
16
|
+
* strip + a header row of view tabs (PROBLEMS, OUTPUT, …), a left border that
|
|
17
|
+
* separates it from the sidebar, and the active view's content below. Views are
|
|
18
|
+
* registered via {@link addView}; the active one is shown. A view with no content
|
|
19
|
+
* element renders its {@link PanelView.placeholder} empty-state message.
|
|
20
|
+
*
|
|
21
|
+
* Tab labels are drawn dim (`panelTitle.inactiveForeground`); the active tab is
|
|
22
|
+
* marked with an underline. Colours are pushed by the controller (`panel.*` /
|
|
23
|
+
* `panelTitle.*`), mirroring how `EditorElement` receives its theme colours.
|
|
24
|
+
*/
|
|
25
|
+
export class PanelContainerElement extends TUIElement {
|
|
26
|
+
/** Fired when a tab is clicked (after the active view has switched). */
|
|
27
|
+
onActivateView;
|
|
28
|
+
views = [];
|
|
29
|
+
activeId = null;
|
|
30
|
+
constructor() {
|
|
31
|
+
super();
|
|
32
|
+
this.style = { bg: "panel.background" };
|
|
33
|
+
this.addEventListener("mousedown", (event) => {
|
|
34
|
+
if (event.button !== "left")
|
|
35
|
+
return;
|
|
36
|
+
// Событие, всплывшее из дочернего контрола (селектор в шапке), не наше:
|
|
37
|
+
// без этой проверки клик по нему ещё и переключал бы вкладку.
|
|
38
|
+
if (event.target !== this)
|
|
39
|
+
return;
|
|
40
|
+
const localY = event.screenY - this.globalPosition.y;
|
|
41
|
+
if (localY !== TAB_ROW)
|
|
42
|
+
return; // only the tab header row switches tabs
|
|
43
|
+
const localX = event.screenX - this.globalPosition.x;
|
|
44
|
+
const segment = this.tabSegments().find((s) => localX >= s.start && localX < s.end);
|
|
45
|
+
if (segment === undefined)
|
|
46
|
+
return;
|
|
47
|
+
this.setActiveView(segment.id);
|
|
48
|
+
this.onActivateView?.(segment.id);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
addView(view) {
|
|
52
|
+
this.views.push(view);
|
|
53
|
+
this.activeId ??= view.id;
|
|
54
|
+
this.syncChildren();
|
|
55
|
+
this.markDirty();
|
|
56
|
+
}
|
|
57
|
+
/** Replaces a registered view's title-row controls (null removes them). */
|
|
58
|
+
setViewActions(id, actions) {
|
|
59
|
+
const view = this.views.find((v) => v.id === id);
|
|
60
|
+
if (view === undefined)
|
|
61
|
+
return;
|
|
62
|
+
view.actions = actions;
|
|
63
|
+
this.syncChildren();
|
|
64
|
+
this.markDirty();
|
|
65
|
+
}
|
|
66
|
+
/** Replaces a registered view's content element (e.g. swapping a placeholder for the real view). */
|
|
67
|
+
setViewContent(id, content) {
|
|
68
|
+
const view = this.views.find((v) => v.id === id);
|
|
69
|
+
if (view === undefined)
|
|
70
|
+
return;
|
|
71
|
+
view.content = content;
|
|
72
|
+
this.syncChildren();
|
|
73
|
+
this.markDirty();
|
|
74
|
+
}
|
|
75
|
+
setActiveView(id) {
|
|
76
|
+
if (this.views.every((v) => v.id !== id) || this.activeId === id)
|
|
77
|
+
return;
|
|
78
|
+
this.activeId = id;
|
|
79
|
+
this.syncChildren();
|
|
80
|
+
this.markDirty();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Все вкладки живут в дереве постоянно; неактивные — hidden (root и стили
|
|
84
|
+
* доходят до них всегда). Раньше getChildren() отдавал только активную, и
|
|
85
|
+
* контент/actions, прицепленные до укоренения панели, оставались с
|
|
86
|
+
* протухшим root — модель бага #204 (селектор каналов Output молча не
|
|
87
|
+
* открывал выпадашку после restore сессии).
|
|
88
|
+
*/
|
|
89
|
+
syncChildren() {
|
|
90
|
+
const children = [];
|
|
91
|
+
for (const view of this.views) {
|
|
92
|
+
const isActive = view.id === this.activeId;
|
|
93
|
+
if (view.actions != null) {
|
|
94
|
+
view.actions.hidden = !isActive;
|
|
95
|
+
children.push(view.actions);
|
|
96
|
+
}
|
|
97
|
+
if (view.content !== null) {
|
|
98
|
+
view.content.hidden = !isActive;
|
|
99
|
+
children.push(view.content);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
this.setChildren(children);
|
|
103
|
+
}
|
|
104
|
+
getActiveViewId() {
|
|
105
|
+
return this.activeId;
|
|
106
|
+
}
|
|
107
|
+
getViewIds() {
|
|
108
|
+
return this.views.map((v) => v.id);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Observable panel state for the inspector: which tabs exist, which is
|
|
112
|
+
* active, and each tab's absolute hit geometry. Replaces the e2e helper that
|
|
113
|
+
* re-derived tab coordinates from TAB_INDENT/TAB_PAD by hand — a test clicks
|
|
114
|
+
* `tabs[i].centerX` on `tabRow` instead.
|
|
115
|
+
*/
|
|
116
|
+
inspectState() {
|
|
117
|
+
const originX = this.globalPosition.x;
|
|
118
|
+
let x = TAB_INDENT;
|
|
119
|
+
const tabs = this.views.map((view) => {
|
|
120
|
+
const width = view.title.length + TAB_PAD * 2;
|
|
121
|
+
const start = x;
|
|
122
|
+
x += width;
|
|
123
|
+
return {
|
|
124
|
+
id: view.id,
|
|
125
|
+
title: view.title,
|
|
126
|
+
active: view.id === this.activeId,
|
|
127
|
+
x: originX + start,
|
|
128
|
+
width,
|
|
129
|
+
centerX: originX + start + Math.floor(width / 2),
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
return { activeId: this.activeId, tabRow: this.globalPosition.y + TAB_ROW, tabs };
|
|
133
|
+
}
|
|
134
|
+
/** Правая граница таб-строки — за неё контролы вкладки заезжать не должны. */
|
|
135
|
+
tabsEnd() {
|
|
136
|
+
return this.views.reduce((x, view) => x + view.title.length + TAB_PAD * 2, TAB_INDENT);
|
|
137
|
+
}
|
|
138
|
+
activeView() {
|
|
139
|
+
return this.views.find((v) => v.id === this.activeId);
|
|
140
|
+
}
|
|
141
|
+
/** Header tab layout: ` Title ` segments after the indent, with hit ranges. */
|
|
142
|
+
tabSegments() {
|
|
143
|
+
const segments = [];
|
|
144
|
+
let x = TAB_INDENT;
|
|
145
|
+
for (const view of this.views) {
|
|
146
|
+
const width = view.title.length + TAB_PAD * 2;
|
|
147
|
+
segments.push({ id: view.id, start: x, end: x + width });
|
|
148
|
+
x += width;
|
|
149
|
+
}
|
|
150
|
+
return segments;
|
|
151
|
+
}
|
|
152
|
+
performLayout(constraints) {
|
|
153
|
+
const containerSize = super.performLayout(constraints);
|
|
154
|
+
// Контролы вкладки прижаты вправо на строке табов — как в шапке Panel у
|
|
155
|
+
// VS Code. Ширину берём интринсиковую и не даём заехать на сами табы.
|
|
156
|
+
const actions = this.activeView()?.actions;
|
|
157
|
+
if (actions != null) {
|
|
158
|
+
const actionsWidth = Math.min(actions.getMaxIntrinsicWidth(1), containerSize.width);
|
|
159
|
+
const x = Math.max(this.tabsEnd(), containerSize.width - actionsWidth - TAB_INDENT);
|
|
160
|
+
this.layoutChild(actions, x, TAB_ROW, BoxConstraints.tight(new Size(Math.max(0, containerSize.width - x), 1)));
|
|
161
|
+
}
|
|
162
|
+
const content = this.activeView()?.content;
|
|
163
|
+
if (content != null) {
|
|
164
|
+
const contentWidth = Math.max(0, containerSize.width - CONTENT_LEFT);
|
|
165
|
+
const contentHeight = Math.max(0, containerSize.height - CONTENT_TOP);
|
|
166
|
+
this.layoutChild(content, CONTENT_LEFT, CONTENT_TOP, BoxConstraints.tight(new Size(contentWidth, contentHeight)));
|
|
167
|
+
}
|
|
168
|
+
return containerSize;
|
|
169
|
+
}
|
|
170
|
+
render(context) {
|
|
171
|
+
const { width, height } = this.layoutSize;
|
|
172
|
+
// Fill the panel with its background first.
|
|
173
|
+
for (let y = 0; y < height; y++) {
|
|
174
|
+
for (let x = 0; x < width; x++) {
|
|
175
|
+
context.setCell(x, y, { char: " ", bg: this.resolvedStyle.bg });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Top border strip (row 0).
|
|
179
|
+
for (let x = 0; x < width; x++) {
|
|
180
|
+
context.setCell(x, 0, { char: "─", fg: this.styleVar("panel.border"), bg: this.resolvedStyle.bg });
|
|
181
|
+
}
|
|
182
|
+
// Tab header (dim). The active tab is underlined — but only under the title
|
|
183
|
+
// glyphs, leaving the surrounding padding un-underlined.
|
|
184
|
+
const segments = this.tabSegments();
|
|
185
|
+
for (let i = 0; i < this.views.length; i++) {
|
|
186
|
+
const view = this.views[i];
|
|
187
|
+
const segment = segments[i];
|
|
188
|
+
const isActive = view.id === this.activeId;
|
|
189
|
+
for (let x = segment.start; x < segment.end && x < width; x++) {
|
|
190
|
+
const textIndex = x - segment.start - TAB_PAD;
|
|
191
|
+
const isGlyph = textIndex >= 0 && textIndex < view.title.length;
|
|
192
|
+
const char = isGlyph ? view.title[textIndex] : " ";
|
|
193
|
+
const style = isActive && isGlyph ? StyleFlags.Underline : StyleFlags.None;
|
|
194
|
+
context.setCell(x, TAB_ROW, {
|
|
195
|
+
char,
|
|
196
|
+
fg: this.styleVar("panelTitle.inactiveForeground"),
|
|
197
|
+
bg: this.resolvedStyle.bg,
|
|
198
|
+
style,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// View-specific controls in the title row (drawn after the tabs so they win
|
|
203
|
+
// the shared row), then the active view's content below — renderChildren
|
|
204
|
+
// рисует только видимых детей, скрытые вкладки пропускаются базой.
|
|
205
|
+
this.renderChildren(context);
|
|
206
|
+
// Placeholder empty-state message, если у активной вкладки нет контента.
|
|
207
|
+
const active = this.activeView();
|
|
208
|
+
if (active?.content == null && active?.placeholder !== undefined && height > CONTENT_TOP) {
|
|
209
|
+
const message = active.placeholder;
|
|
210
|
+
for (let i = 0; i < message.length && i + CONTENT_LEFT < width; i++) {
|
|
211
|
+
context.setCell(i + CONTENT_LEFT, CONTENT_TOP, {
|
|
212
|
+
char: message[i],
|
|
213
|
+
fg: this.styleVar("panelTitle.inactiveForeground"),
|
|
214
|
+
bg: this.resolvedStyle.bg,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { BoxConstraints, Size } from "@tuidom/core/common/geometryPromitives";
|
|
2
|
+
import type { TUIEventBase } from "@tuidom/core/dom/events/tuiEventBase";
|
|
3
|
+
import { RenderContext, TUIElement } from "@tuidom/core/dom/tuiElement";
|
|
4
|
+
import { InputElement } from "../inputbox/inputElement.js";
|
|
5
|
+
export interface QuickPickItem {
|
|
6
|
+
/** Nerd font icon character (e.g. a file icon like "\uf15b"). */
|
|
7
|
+
icon?: string;
|
|
8
|
+
/** Main display text. */
|
|
9
|
+
label: string;
|
|
10
|
+
/** Right-side text: file path, category, etc. */
|
|
11
|
+
description?: string;
|
|
12
|
+
/** Keyboard shortcut shown right-aligned, e.g. "Ctrl+Shift+P". */
|
|
13
|
+
shortcut?: string;
|
|
14
|
+
/** Action hint shown after description, e.g. "Configure Binding". */
|
|
15
|
+
hint?: string;
|
|
16
|
+
/** Marker badge, e.g. "recently used". */
|
|
17
|
+
badge?: string;
|
|
18
|
+
/** Byte-offset ranges in `label` to highlight as fuzzy-match hits. */
|
|
19
|
+
labelMatchRanges?: readonly [number, number][];
|
|
20
|
+
/** Byte-offset ranges in `description` to highlight as fuzzy-match hits. */
|
|
21
|
+
descriptionMatchRanges?: readonly [number, number][];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Quick-open / command-palette style picker.
|
|
25
|
+
*
|
|
26
|
+
* Renders a bordered box with:
|
|
27
|
+
* - top row: text input (query)
|
|
28
|
+
* - optional separator + scrollable item list below
|
|
29
|
+
*
|
|
30
|
+
* The widget does NOT perform any filtering itself — pass pre-filtered
|
|
31
|
+
* `items` with `labelMatchRanges`/`descriptionMatchRanges` populated.
|
|
32
|
+
*
|
|
33
|
+
* Keyboard:
|
|
34
|
+
* ArrowDown/Up — move selection (no wrap)
|
|
35
|
+
* Enter — fire `onAccept(item, index)`
|
|
36
|
+
* Escape — fire `onCancel()`
|
|
37
|
+
* All other keys — forwarded to the InputElement (typing)
|
|
38
|
+
*
|
|
39
|
+
* Usage:
|
|
40
|
+
* const picker = new QuickPickElement();
|
|
41
|
+
* picker.placeholder = "Go to file…";
|
|
42
|
+
* picker.onQueryChange = query => { picker.items = search(query); };
|
|
43
|
+
* picker.onAccept = (item) => openFile(item.description!);
|
|
44
|
+
*/
|
|
45
|
+
export type QuickPickAcceptMode = "item" | "value";
|
|
46
|
+
export type ValidationSeverity = "error" | "warning" | "info";
|
|
47
|
+
export declare class QuickPickElement extends TUIElement {
|
|
48
|
+
placeholder: string;
|
|
49
|
+
/** Maximum rows of the list to show at once (scrolls when exceeded). */
|
|
50
|
+
maxVisibleItems: number;
|
|
51
|
+
/** Optional title drawn centered into the top border (e.g. "Save As"). */
|
|
52
|
+
title: string | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Optional subtitle drawn on a dedicated row under the input (dim).
|
|
55
|
+
* Used by the InputBox flavor. Overridden by `validationMessage` when set.
|
|
56
|
+
*/
|
|
57
|
+
prompt: string | undefined;
|
|
58
|
+
/** Validation/error text shown under the input; blocks Enter when severity is "error". */
|
|
59
|
+
validationMessage: string | null;
|
|
60
|
+
validationSeverity: ValidationSeverity;
|
|
61
|
+
/**
|
|
62
|
+
* How Enter is interpreted:
|
|
63
|
+
* "item" — fire onAccept(item, index), only when the list is non-empty (default; QuickOpen).
|
|
64
|
+
* "value" — always fire onAcceptValue(getQuery()) — the InputBox flavor.
|
|
65
|
+
*/
|
|
66
|
+
acceptMode: QuickPickAcceptMode;
|
|
67
|
+
/** Fired by Enter when acceptMode === "value". */
|
|
68
|
+
onAcceptValue: ((value: string) => void) | null;
|
|
69
|
+
/**
|
|
70
|
+
* Desired width of the picker in columns.
|
|
71
|
+
* When the element is laid out with loose constraints it will use this
|
|
72
|
+
* value (clamped to [minWidth, maxWidth]).
|
|
73
|
+
*/
|
|
74
|
+
preferredWidth: number;
|
|
75
|
+
onQueryChange: ((query: string) => void) | null;
|
|
76
|
+
onAccept: ((item: QuickPickItem, index: number) => void) | null;
|
|
77
|
+
onCancel: (() => void) | null;
|
|
78
|
+
/**
|
|
79
|
+
* Fired when the highlighted (active) item changes via keyboard navigation —
|
|
80
|
+
* used for live preview (e.g. the color-theme picker applies the theme as you
|
|
81
|
+
* arrow through the list). NOT fired by `items =` / `refreshItems` /
|
|
82
|
+
* `setActiveIndex` (those are programmatic repositioning, not user intent).
|
|
83
|
+
*/
|
|
84
|
+
onActiveItemChanged: ((item: QuickPickItem, index: number) => void) | null;
|
|
85
|
+
readonly inputElement: InputElement;
|
|
86
|
+
private itemsValue;
|
|
87
|
+
private selectedIndexValue;
|
|
88
|
+
private scrollOffset;
|
|
89
|
+
constructor();
|
|
90
|
+
protected performDefaultAction(event: TUIEventBase): void;
|
|
91
|
+
/** Follow the mouse: hovering a list row moves the selection onto it (VS Code behavior). */
|
|
92
|
+
private handleMouseMove;
|
|
93
|
+
/** Clicking a list row selects it and accepts it, mirroring Enter. */
|
|
94
|
+
private handleClick;
|
|
95
|
+
/**
|
|
96
|
+
* Maps a y offset local to this element onto an item index, or null when it
|
|
97
|
+
* falls outside the visible list rows (border, input, message, separator).
|
|
98
|
+
*/
|
|
99
|
+
private itemIndexFromLocalY;
|
|
100
|
+
get items(): readonly QuickPickItem[];
|
|
101
|
+
/** Observable state: current query, item labels and the active index. */
|
|
102
|
+
inspectState(): Record<string, unknown>;
|
|
103
|
+
set items(value: readonly QuickPickItem[]);
|
|
104
|
+
/**
|
|
105
|
+
* Replace the items WITHOUT jumping the cursor back to the top.
|
|
106
|
+
*
|
|
107
|
+
* Use this when the list is refreshed for the *same* query (e.g. the file
|
|
108
|
+
* index grew in the background and produced more results) — resetting the
|
|
109
|
+
* selection there would yank the cursor away from the user mid-navigation.
|
|
110
|
+
*
|
|
111
|
+
* The previously selected item is re-located by identity (label +
|
|
112
|
+
* description, which uniquely identifies a file row). If it is gone, the
|
|
113
|
+
* previous index is clamped into the new bounds. The selected row is kept
|
|
114
|
+
* on-screen.
|
|
115
|
+
*/
|
|
116
|
+
refreshItems(value: readonly QuickPickItem[]): void;
|
|
117
|
+
get selectedIndex(): number;
|
|
118
|
+
getQuery(): string;
|
|
119
|
+
setQuery(value: string): void;
|
|
120
|
+
/** Delegate focus to the inner InputElement. */
|
|
121
|
+
focus(): void;
|
|
122
|
+
private get visibleItemCount();
|
|
123
|
+
/**
|
|
124
|
+
* Сколько строк списка реально помещается в выделенную layout'ом высоту.
|
|
125
|
+
* Разрыв цикличности с {@link totalHeight}: natural-высота считается ДО
|
|
126
|
+
* layout (интринсики, visibleItemCount), фактическое окно — ПОСЛЕ, от
|
|
127
|
+
* layoutSize. На низком терминале (loose от overlay с маленьким остатком)
|
|
128
|
+
* окно меньше natural — рендер, хит-тест и скролл живут по нему.
|
|
129
|
+
*/
|
|
130
|
+
private get effectiveVisibleRows();
|
|
131
|
+
/**
|
|
132
|
+
* Строка сепаратора/нижней рамки сразу под «шапкой» (верхняя рамка + input +
|
|
133
|
+
* [message]) — она же счётчик занятых шапкой строк.
|
|
134
|
+
*/
|
|
135
|
+
private get bodyTop();
|
|
136
|
+
/**
|
|
137
|
+
* The message row shown under the input (InputBox flavor). A validation
|
|
138
|
+
* message wins over the plain prompt; returns null when neither is set.
|
|
139
|
+
*/
|
|
140
|
+
private get messageRow();
|
|
141
|
+
private get totalHeight();
|
|
142
|
+
getMinIntrinsicWidth(_height: number): number;
|
|
143
|
+
getMaxIntrinsicWidth(_height: number): number;
|
|
144
|
+
getMinIntrinsicHeight(_width: number): number;
|
|
145
|
+
getMaxIntrinsicHeight(_width: number): number;
|
|
146
|
+
protected performLayout(constraints: BoxConstraints): Size;
|
|
147
|
+
render(context: RenderContext): void;
|
|
148
|
+
/** Draws the title centered into the top border row as ┤ title ├. */
|
|
149
|
+
private renderTitle;
|
|
150
|
+
/** Draws the prompt / validation message row under the input. */
|
|
151
|
+
private renderMessageRow;
|
|
152
|
+
private renderItemRow;
|
|
153
|
+
private moveSelection;
|
|
154
|
+
/**
|
|
155
|
+
* Move the highlight to `index` programmatically (e.g. pre-select the current
|
|
156
|
+
* theme when the picker opens). Clamped into range; keeps the row on-screen.
|
|
157
|
+
* Does NOT fire {@link onActiveItemChanged} — this is not a user navigation.
|
|
158
|
+
*/
|
|
159
|
+
setActiveIndex(index: number): void;
|
|
160
|
+
private ensureVisible;
|
|
161
|
+
}
|