Skip to content

Commit

Permalink
fix #6586: tree indent guidelins
Browse files Browse the repository at this point in the history
Co-authored-By: Kaiyue Pan <kaiyue.pan@ericsson.com>
Signed-off-by: Anton Kosyakov <anton.kosyakov@typefox.io>
  • Loading branch information
akosyakov and Kaiyue Pan committed Aug 4, 2020
1 parent fe24630 commit cea71eb
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 2 deletions.
3 changes: 2 additions & 1 deletion packages/core/src/browser/common-frontend-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
// if not yet contributed by Monaco, check runtime css variables to learn.
// TODO: Following are not yet supported/no respective elements in theia:
// list.focusBackground, list.focusForeground, list.inactiveFocusBackground, list.filterMatchBorder,
// list.dropBackground, listFilterWidget.outline, listFilterWidget.noMatchesOutline, tree.indentGuidesStroke
// list.dropBackground, listFilterWidget.outline, listFilterWidget.noMatchesOutline
// list.invalidItemForeground,
// list.warningForeground, list.errorForeground => tree node needs an respective class
{ id: 'list.activeSelectionBackground', defaults: { dark: '#094771', light: '#0074E8' }, description: 'List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.' },
Expand All @@ -1100,6 +1100,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
{ id: 'list.hoverBackground', defaults: { dark: '#2A2D2E', light: '#F0F0F0' }, description: 'List/Tree background when hovering over items using the mouse.' },
{ id: 'list.hoverForeground', description: 'List/Tree foreground when hovering over items using the mouse.' },
{ id: 'list.filterMatchBackground', defaults: { dark: 'editor.findMatchHighlightBackground', light: 'editor.findMatchHighlightBackground' }, description: 'Background color of the filtered match.' },
{ id: 'tree.inactiveIndentGuidesStroke', defaults: { dark: Color.transparent('tree.indentGuidesStroke', 0.4), light: Color.transparent('tree.indentGuidesStroke', 0.4), hc: Color.transparent('tree.indentGuidesStroke', 0.4) }, description: 'Tree stroke color for the inactive indentation guides.' },

// Editor Group & Tabs colors should be aligned with https://code.visualstudio.com/api/references/theme-color#editor-groups-tabs
{
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/browser/core-preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ export const corePreferenceSchema: PreferenceSchema = {
'scope': 'language-overridable',
'enumDescriptions': Object.keys(SUPPORTED_ENCODINGS).map(key => SUPPORTED_ENCODINGS[key].labelLong),
'included': Object.keys(SUPPORTED_ENCODINGS).length > 1
},
'workbench.tree.renderIndentGuides': {
type: 'string',
enum: ['onHover', 'none', 'always'],
default: 'onHover',
description: 'Controls whether the tree should render indent guides.'
}
}
};
Expand All @@ -85,6 +91,7 @@ export interface CoreConfiguration {
'workbench.iconTheme'?: string | null;
'workbench.silentNotifications': boolean;
'files.encoding': string
'workbench.tree.renderIndentGuides'?: 'onHover' | 'none' | 'always';
}

export const CorePreferences = Symbol('CorePreferences');
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/browser/style/tree.css
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,16 @@
.theia-tree-element-node {
width: 100%
}

.theia-tree-node-indent {
position: absolute;
height: var(--theia-content-line-height);
border-right: var(--theia-border-width) solid transparent;
}
.theia-tree-node-indent.always,
.theia-TreeContainer:hover .theia-tree-node-indent.hover {
border-color: var(--theia-tree-inactiveIndentGuidesStroke);
}
.theia-tree-node-indent.active {
border-color: var(--theia-tree-indentGuidesStroke);
}
63 changes: 62 additions & 1 deletion packages/core/src/browser/tree/tree-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { ElementExt } from '@phosphor/domutils';
import { TreeWidgetSelection } from './tree-widget-selection';
import { MaybePromise } from '../../common/types';
import { LabelProvider } from '../label-provider';
import { CorePreferences } from '../core-preferences';

const debounce = require('lodash.debounce');

Expand All @@ -52,6 +53,7 @@ export const TREE_NODE_SEGMENT_GROW_CLASS = 'theia-TreeNodeSegmentGrow';
export const EXPANDABLE_TREE_NODE_CLASS = 'theia-ExpandableTreeNode';
export const COMPOSITE_TREE_NODE_CLASS = 'theia-CompositeTreeNode';
export const TREE_NODE_CAPTION_CLASS = 'theia-TreeNodeCaption';
export const TREE_NODE_INDENT_GUIDE_CLASS = 'theia-tree-node-indent';

export const TreeProps = Symbol('TreeProps');

Expand Down Expand Up @@ -162,6 +164,9 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
@inject(LabelProvider)
protected readonly labelProvider: LabelProvider;

@inject(CorePreferences)
protected readonly corePreferences: CorePreferences;

protected shouldScrollToRow = true;

constructor(
Expand Down Expand Up @@ -251,6 +256,11 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
})
]);
}
this.toDispose.push(this.corePreferences.onPreferenceChanged(preference => {
if (preference.preferenceName === 'workbench.tree.renderIndentGuides') {
this.update();
}
}));
}

/**
Expand Down Expand Up @@ -478,7 +488,10 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
* Actually render the node row.
*/
protected doRenderNodeRow({ index, node, depth }: TreeWidget.NodeRow): React.ReactNode {
return this.renderNode(node, { depth });
return <React.Fragment>
{this.renderIndent(node, { depth })}
{this.renderNode(node, { depth })}
</React.Fragment>;
}

/**
Expand Down Expand Up @@ -776,6 +789,54 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
return iconClass.concat(additionalClasses).join(' ');
}

/**
* Render indent for the file tree based on the depth
* @param node the tree node.
* @param depth the depth of the tree node.
*/
protected renderIndent(node: TreeNode, props: NodeProps): React.ReactNode {
const renderIndentGuides = this.corePreferences['workbench.tree.renderIndentGuides'];

const indentDivs: React.ReactNode[] = [];
let current: TreeNode | undefined = node;
let depth = props.depth;
while (current && depth) {
const classNames: string[] = [TREE_NODE_INDENT_GUIDE_CLASS];
if (renderIndentGuides !== 'none') {
if (this.needsActiveIndentGuideline(current)) {
classNames.push('active');
} else {
classNames.push(renderIndentGuides === 'onHover' ? 'hover' : 'always');
}
}
const paddingLeft = this.props.leftPadding * depth;
indentDivs.unshift(<div key={depth} className={classNames.join(' ')} style={{
paddingLeft: `${paddingLeft}px`
}} />);
current = current.parent;
depth--;
}
return indentDivs;
}

protected needsActiveIndentGuideline(node: TreeNode): boolean {
const parent = node.parent;
if (!parent || !this.isExpandable(parent)) {
return false;
}
if (SelectableTreeNode.isSelected(parent)) {
return true;
}
if (parent.expanded) {
for (const sibling of parent.children) {
if (SelectableTreeNode.isSelected(sibling) && !(this.isExpandable(sibling) && sibling.expanded)) {
return true;
}
}
}
return false;
}

/**
* Render the node given the tree node and node properties.
* @param node the tree node.
Expand Down

0 comments on commit cea71eb

Please sign in to comment.