mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
Custom tree view looses focussed checkbox on toggle (#186906)
Fixes #186306
This commit is contained in:
@@ -630,68 +630,6 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
|
||||
this._register(focusTracker.onDidBlur(() => this.focused = false));
|
||||
}
|
||||
|
||||
private updateCheckboxes(items: ITreeItem[]) {
|
||||
const additionalItems: ITreeItem[] = [];
|
||||
|
||||
if (!this.manuallyManageCheckboxes) {
|
||||
for (const item of items) {
|
||||
if (item.checkbox !== undefined) {
|
||||
|
||||
function checkChildren(currentItem: ITreeItem) {
|
||||
for (const child of (currentItem.children ?? [])) {
|
||||
if ((child.checkbox !== undefined) && (currentItem.checkbox !== undefined) && (child.checkbox.isChecked !== currentItem.checkbox.isChecked)) {
|
||||
child.checkbox.isChecked = currentItem.checkbox.isChecked;
|
||||
additionalItems.push(child);
|
||||
checkChildren(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
checkChildren(item);
|
||||
|
||||
const visitedParents: Set<ITreeItem> = new Set();
|
||||
function checkParents(currentItem: ITreeItem) {
|
||||
if (currentItem.parent && (currentItem.parent.checkbox !== undefined) && currentItem.parent.children) {
|
||||
if (visitedParents.has(currentItem.parent)) {
|
||||
return;
|
||||
} else {
|
||||
visitedParents.add(currentItem.parent);
|
||||
}
|
||||
|
||||
let someUnchecked = false;
|
||||
let someChecked = false;
|
||||
for (const child of currentItem.parent.children) {
|
||||
if (someUnchecked && someChecked) {
|
||||
break;
|
||||
}
|
||||
if (child.checkbox !== undefined) {
|
||||
if (child.checkbox.isChecked) {
|
||||
someChecked = true;
|
||||
} else {
|
||||
someUnchecked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (someChecked && !someUnchecked && (currentItem.parent.checkbox.isChecked !== true)) {
|
||||
currentItem.parent.checkbox.isChecked = true;
|
||||
additionalItems.push(currentItem.parent);
|
||||
checkParents(currentItem.parent);
|
||||
} else if (someUnchecked && (currentItem.parent.checkbox.isChecked !== false)) {
|
||||
currentItem.parent.checkbox.isChecked = false;
|
||||
additionalItems.push(currentItem.parent);
|
||||
checkParents(currentItem.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
checkParents(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
items = items.concat(additionalItems);
|
||||
items.forEach(item => this.tree?.rerender(item));
|
||||
this._onDidChangeCheckboxState.fire(items);
|
||||
}
|
||||
|
||||
|
||||
protected createTree() {
|
||||
const actionViewItemProvider = createActionViewItem.bind(undefined, this.instantiationService);
|
||||
const treeMenus = this._register(this.instantiationService.createInstance(TreeMenus, this.id));
|
||||
@@ -699,10 +637,9 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
|
||||
const dataSource = this.instantiationService.createInstance(TreeDataSource, this, <T>(task: Promise<T>) => this.progressService.withProgress({ location: this.id }, () => task));
|
||||
const aligner = new Aligner(this.themeService);
|
||||
const checkboxStateHandler = this._register(new CheckboxStateHandler());
|
||||
this._register(checkboxStateHandler.onDidChangeCheckboxState(items => {
|
||||
this.updateCheckboxes(items);
|
||||
}));
|
||||
const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner, checkboxStateHandler);
|
||||
const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner, checkboxStateHandler, this.manuallyManageCheckboxes);
|
||||
this._register(renderer.onDidChangeCheckboxState(e => this._onDidChangeCheckboxState.fire(e)));
|
||||
|
||||
const widgetAriaLabel = this._title;
|
||||
|
||||
this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer!, new TreeViewDelegate(), [renderer],
|
||||
@@ -1125,10 +1062,13 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
static readonly ITEM_HEIGHT = 22;
|
||||
static readonly TREE_TEMPLATE_ID = 'treeExplorer';
|
||||
|
||||
private readonly _onDidChangeCheckboxState: Emitter<readonly ITreeItem[]> = this._register(new Emitter<readonly ITreeItem[]>());
|
||||
readonly onDidChangeCheckboxState: Event<readonly ITreeItem[]> = this._onDidChangeCheckboxState.event;
|
||||
|
||||
private _actionRunner: MultipleSelectionActionRunner | undefined;
|
||||
private _hoverDelegate: IHoverDelegate;
|
||||
private _hasCheckbox: boolean = false;
|
||||
private _renderedElements = new Map<ITreeNode<ITreeItem, FuzzyScore>, ITreeExplorerTemplateData>();
|
||||
private _renderedElements = new Map<string, { original: ITreeNode<ITreeItem, FuzzyScore>; rendered: ITreeExplorerTemplateData }>(); // tree item handle to template data
|
||||
|
||||
constructor(
|
||||
private treeViewId: string,
|
||||
@@ -1137,6 +1077,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
private actionViewItemProvider: IActionViewItemProvider,
|
||||
private aligner: Aligner,
|
||||
private checkboxStateHandler: CheckboxStateHandler,
|
||||
private readonly manuallyManageCheckboxes: boolean,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@@ -1151,6 +1092,9 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
};
|
||||
this._register(this.themeService.onDidFileIconThemeChange(() => this.rerender()));
|
||||
this._register(this.themeService.onDidColorThemeChange(() => this.rerender()));
|
||||
this._register(checkboxStateHandler.onDidChangeCheckboxState(items => {
|
||||
this.updateCheckboxes(items);
|
||||
}));
|
||||
}
|
||||
|
||||
get templateId(): string {
|
||||
@@ -1302,7 +1246,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
this.treeViewsService.addRenderedTreeItemElement(node, templateData.container);
|
||||
|
||||
// remember rendered element
|
||||
this._renderedElements.set(element, templateData);
|
||||
this._renderedElements.set(element.element.handle, { original: element, rendered: templateData });
|
||||
}
|
||||
|
||||
private rerender() {
|
||||
@@ -1312,8 +1256,8 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
for (const key of keys) {
|
||||
const value = this._renderedElements.get(key);
|
||||
if (value) {
|
||||
this.disposeElement(key, 0, value);
|
||||
this.renderElement(key, 0, value);
|
||||
this.disposeElement(value.original, 0, value.rendered);
|
||||
this.renderElement(value.original, 0, value.rendered);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1381,10 +1325,76 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded ? FileKind.FOLDER : FileKind.FILE;
|
||||
}
|
||||
|
||||
private updateCheckboxes(items: ITreeItem[]) {
|
||||
const additionalItems: ITreeItem[] = [];
|
||||
|
||||
if (!this.manuallyManageCheckboxes) {
|
||||
for (const item of items) {
|
||||
if (item.checkbox !== undefined) {
|
||||
|
||||
function checkChildren(currentItem: ITreeItem) {
|
||||
for (const child of (currentItem.children ?? [])) {
|
||||
if ((child.checkbox !== undefined) && (currentItem.checkbox !== undefined) && (child.checkbox.isChecked !== currentItem.checkbox.isChecked)) {
|
||||
child.checkbox.isChecked = currentItem.checkbox.isChecked;
|
||||
additionalItems.push(child);
|
||||
checkChildren(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
checkChildren(item);
|
||||
|
||||
const visitedParents: Set<ITreeItem> = new Set();
|
||||
function checkParents(currentItem: ITreeItem) {
|
||||
if (currentItem.parent && (currentItem.parent.checkbox !== undefined) && currentItem.parent.children) {
|
||||
if (visitedParents.has(currentItem.parent)) {
|
||||
return;
|
||||
} else {
|
||||
visitedParents.add(currentItem.parent);
|
||||
}
|
||||
|
||||
let someUnchecked = false;
|
||||
let someChecked = false;
|
||||
for (const child of currentItem.parent.children) {
|
||||
if (someUnchecked && someChecked) {
|
||||
break;
|
||||
}
|
||||
if (child.checkbox !== undefined) {
|
||||
if (child.checkbox.isChecked) {
|
||||
someChecked = true;
|
||||
} else {
|
||||
someUnchecked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (someChecked && !someUnchecked && (currentItem.parent.checkbox.isChecked !== true)) {
|
||||
currentItem.parent.checkbox.isChecked = true;
|
||||
additionalItems.push(currentItem.parent);
|
||||
checkParents(currentItem.parent);
|
||||
} else if (someUnchecked && (currentItem.parent.checkbox.isChecked !== false)) {
|
||||
currentItem.parent.checkbox.isChecked = false;
|
||||
additionalItems.push(currentItem.parent);
|
||||
checkParents(currentItem.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
checkParents(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
items = items.concat(additionalItems);
|
||||
items.forEach(item => {
|
||||
const renderedItem = this._renderedElements.get(item.handle);
|
||||
if (renderedItem) {
|
||||
renderedItem.rendered.checkbox?.render(item);
|
||||
}
|
||||
});
|
||||
this._onDidChangeCheckboxState.fire(items);
|
||||
}
|
||||
|
||||
disposeElement(resource: ITreeNode<ITreeItem, FuzzyScore>, index: number, templateData: ITreeExplorerTemplateData): void {
|
||||
templateData.elementDisposable.clear();
|
||||
|
||||
this._renderedElements.delete(resource);
|
||||
this._renderedElements.delete(resource.element.handle);
|
||||
this.treeViewsService.removeRenderedTreeItemElement(resource.element);
|
||||
|
||||
templateData.checkbox?.dispose();
|
||||
|
||||
Reference in New Issue
Block a user