Skip to content

Commit

Permalink
Add padding and handle root folders
Browse files Browse the repository at this point in the history
Signed-off-by: Colin Grant <colin.grant@ericsson.com>
  • Loading branch information
colin-grant-work committed Aug 30, 2021
1 parent 5b35428 commit ee4ac3d
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 22 deletions.
13 changes: 8 additions & 5 deletions packages/core/src/browser/breadcrumbs/breadcrumbs-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,22 @@ export namespace Styles {
export interface Breadcrumb {

/** An ID of this breadcrumb that should be unique in the breadcrumbs bar. */
readonly id: string
readonly id: string;

/** The breadcrumb type. Should be the same as the contribution type `BreadcrumbsContribution#type`. */
readonly type: symbol
readonly type: symbol;

/** The text that will be rendered as label. */
readonly label: string
readonly label: string;

/** A longer text that will be used as tooltip text. */
readonly longLabel: string
readonly longLabel: string;

/** A CSS class for the icon. */
readonly iconClass?: string
readonly iconClass?: string;

/** CSS classes for the container */
readonly containerClass?: string;
}

export const BreadcrumbsContribution = Symbol('BreadcrumbsContribution');
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/browser/style/breadcrumbs.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@
padding-left: 4px;
}

.theia-breadcrumbs .theia-breadcrumb-item::before {
width: 16px;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}

.theia-breadcrumbs .theia-breadcrumb-item:first-of-type::before {
content: " ";
}

.theia-breadcrumb-item-haspopup:hover {
background: var(--theia-accent-color3);
cursor: pointer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export class FilepathBreadcrumb implements Breadcrumb {
readonly uri: URI,
readonly label: string,
readonly longLabel: string,
readonly iconClass: string
readonly iconClass: string,
readonly containerClass: string,
) { }

get id(): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import { FileStat } from '../../common/files';

export const FilepathBreadcrumbType = Symbol('FilepathBreadcrumb');

export interface FilepathBreadccrumbClassNameFactory {
(location: URI, index: number): string;
}

@injectable()
export class FilepathBreadcrumbsContribution implements BreadcrumbsContribution {

Expand All @@ -50,17 +54,32 @@ export class FilepathBreadcrumbsContribution implements BreadcrumbsContribution
if (uri.scheme !== 'file') {
return [];
}
const getContainerClass = this.getContainerClassCreator(uri);
const getIconClass = this.getIconClassCreator(uri);
return uri.allLocations
.map((location, index) => new FilepathBreadcrumb(
location,
this.labelProvider.getName(location),
this.labelProvider.getLongName(location),
index === 0 ? this.labelProvider.getIcon(location) + ' file-icon' : ''
))
.map((location, index) => {
const icon = getIconClass(location, index);
const containerClass = getContainerClass(location, index);
return new FilepathBreadcrumb(
location,
this.labelProvider.getName(location),
this.labelProvider.getLongName(location),
icon,
containerClass,
);
})
.filter(b => this.filterBreadcrumbs(uri, b))
.reverse();
}

protected getContainerClassCreator(fileURI: URI): FilepathBreadccrumbClassNameFactory {
return (location, index) => location.isEqual(fileURI) ? 'file' : 'folder';
}

protected getIconClassCreator(fileURI: URI): FilepathBreadccrumbClassNameFactory {
return (location, index) => location.isEqual(fileURI) ? this.labelProvider.getIcon(location) + ' file-icon' : '';
}

protected filterBreadcrumbs(_: URI, breadcrumb: FilepathBreadcrumb): boolean {
return !breadcrumb.uri.path.isRoot;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { UriSelection } from '@theia/core/lib/common';

export const OutlineBreadcrumbType = Symbol('OutlineBreadcrumb');
export const BreadcrumbPopupOutlineViewFactory = Symbol('BreadcrumbPopupOutlineViewFactory');
export const OUTLINE_BREADCRUMB_CONTAINER_CLASS = 'outline-element';
export interface BreadcrumbPopupOutlineViewFactory {
(): BreadcrumbPopupOutlineView;
}
Expand Down Expand Up @@ -103,16 +104,36 @@ export class OutlineBreadcrumbsContribution implements BreadcrumbsContribution {
const outlinePath = this.toOutlinePath(selectedNode);
if (outlinePath && selectedNode) {
this.currentBreadcrumbs = outlinePath.map((node, index) =>
new OutlineBreadcrumb(node, uri, index.toString(), this.labelProvider.getName(node), 'symbol-icon symbol-icon-center ' + node.iconClass)
new OutlineBreadcrumb(
node,
uri,
index.toString(),
this.labelProvider.getName(node),
'symbol-icon symbol-icon-center ' + node.iconClass,
OUTLINE_BREADCRUMB_CONTAINER_CLASS,
)
);
if (selectedNode.children && selectedNode.children.length > 0) {
this.currentBreadcrumbs.push(new OutlineBreadcrumb(selectedNode.children as OutlineSymbolInformationNode[],
uri, this.currentBreadcrumbs.length.toString(), '…', ''));
this.currentBreadcrumbs.push(new OutlineBreadcrumb(
selectedNode.children as OutlineSymbolInformationNode[],
uri,
this.currentBreadcrumbs.length.toString(),
'…',
'',
OUTLINE_BREADCRUMB_CONTAINER_CLASS,
));
}
} else {
this.currentBreadcrumbs = [];
if (this.roots) {
this.currentBreadcrumbs.push(new OutlineBreadcrumb(this.roots, uri, this.currentBreadcrumbs.length.toString(), '…', ''));
this.currentBreadcrumbs.push(new OutlineBreadcrumb(
this.roots,
uri,
this.currentBreadcrumbs.length.toString(),
'…',
'',
OUTLINE_BREADCRUMB_CONTAINER_CLASS
));
}
}
this.onDidChangeBreadcrumbsEmitter.fire(uri);
Expand Down Expand Up @@ -187,7 +208,8 @@ export class OutlineBreadcrumb implements Breadcrumb {
readonly uri: URI,
readonly index: string,
readonly label: string,
readonly iconClass: string
readonly iconClass: string,
readonly containerClass: string,
) { }

get id(): string {
Expand Down
14 changes: 12 additions & 2 deletions packages/plugin-ext/src/main/browser/plugin-icon-theme-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { WorkspaceRootNode } from '@theia/navigator/lib/browser/navigator-tree';
import { Endpoint } from '@theia/core/lib/browser/endpoint';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { FileStat, FileChangeType } from '@theia/filesystem/lib/common/files';
import { WorkspaceService } from '@theia/workspace/lib/browser';

export interface PluginIconDefinition {
iconPath: string;
Expand Down Expand Up @@ -107,6 +108,9 @@ export class PluginIconTheme extends PluginIconThemeDefinition implements IconTh
@inject(PluginIconThemeDefinition)
protected readonly definition: PluginIconThemeDefinition;

@inject(WorkspaceService)
protected readonly workspaceService: WorkspaceService;

protected readonly onDidChangeEmitter = new Emitter<DidChangeLabelEvent>();
readonly onDidChange = this.onDidChangeEmitter.event;

Expand Down Expand Up @@ -508,9 +512,15 @@ export class PluginIconTheme extends PluginIconThemeDefinition implements IconTh
const name = this.labelProvider.getName(element);
const classNames = this.fileNameIcon(name);
if (uri) {
const language = monaco.services.StaticServices.modeService.get().createByFilepathOrFirstLine(monaco.Uri.parse(uri));
const parsedURI = new URI(uri);
const isRoot = this.workspaceService.getWorkspaceRootUri(new URI(uri))?.isEqual(parsedURI);
if (isRoot) {
classNames.unshift(this.rootFolderIcon);
} else {
classNames.unshift(this.fileIcon);
}
const language = monaco.services.StaticServices.modeService.get().createByFilepathOrFirstLine(parsedURI['codeUri']);
classNames.push(this.languageIcon(language.languageIdentifier.language));
classNames.unshift(this.fileIcon);
}
return classNames;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
********************************************************************************/

import { FilepathBreadcrumb } from '@theia/filesystem/lib/browser/breadcrumbs/filepath-breadcrumb';
import { FilepathBreadcrumbsContribution } from '@theia/filesystem/lib/browser/breadcrumbs/filepath-breadcrumbs-contribution';
import { FilepathBreadccrumbClassNameFactory, FilepathBreadcrumbsContribution } from '@theia/filesystem/lib/browser/breadcrumbs/filepath-breadcrumbs-contribution';
import { inject, injectable } from '@theia/core/shared/inversify';
import { WorkspaceService } from './workspace-service';
import URI from '@theia/core/lib/common/uri';
Expand All @@ -26,6 +26,28 @@ export class WorkspaceBreadcrumbsContribution extends FilepathBreadcrumbsContrib
@inject(WorkspaceService)
protected readonly workspaceService: WorkspaceService;

getContainerClassCreator(fileURI: URI): FilepathBreadccrumbClassNameFactory {
const workspaceRoot = this.workspaceService.getWorkspaceRootUri(fileURI);
return (location, index) => {
if (location.isEqual(fileURI)) {
return 'file';
} else if (workspaceRoot?.isEqual(location)) {
return 'root_folder';
}
return 'folder';
};
}

getIconClassCreator(fileURI: URI): FilepathBreadccrumbClassNameFactory {
const workspaceRoot = this.workspaceService.getWorkspaceRootUri(fileURI);
return (location, index) => {
if (location.isEqual(fileURI) || workspaceRoot?.isEqual(location)) {
return this.labelProvider.getIcon(location) + ' file-icon';
}
return '';
};
}

protected filterBreadcrumbs(uri: URI, breadcrumb: FilepathBreadcrumb): boolean {
const workspaceRootUri = this.workspaceService.getWorkspaceRootUri(uri);
const firstCrumbToHide = this.workspaceService.isMultiRootWorkspaceOpened ? workspaceRootUri?.parent : workspaceRootUri;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,13 @@ describe('WorkspaceUriLabelProviderContribution class', () => {
expect(labelProvider.getIcon(FileStat.file('file:///home/test'))).eq(labelProvider.defaultFileIcon);
});

it('should return folder icon from a folder URI', async () => {
it('should return folder icon from a folder FileStat', async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
stubs.push(sinon.stub(DefaultUriLabelProviderContribution.prototype, <any>'getFileIcon').returns(undefined));
expect(labelProvider.getIcon(FileStat.dir('file:///home/test'))).eq(labelProvider.defaultFolderIcon);
});

it('should return file icon from a file URI', async () => {
it('should return file icon from a file FileStat', async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
stubs.push(sinon.stub(DefaultUriLabelProviderContribution.prototype, <any>'getFileIcon').returns(undefined));
expect(labelProvider.getIcon(FileStat.file('file:///home/test'))).eq(labelProvider.defaultFileIcon);
Expand All @@ -119,6 +119,11 @@ describe('WorkspaceUriLabelProviderContribution class', () => {
expect(labelProvider.getIcon(new URI('file:///home/test'))).eq(ret);
expect(labelProvider.getIcon(FileStat.file('file:///home/test'))).eq(ret);
});

it('should return rootfolder-icon for a URI or file stat that corresponds to a workspace root', () => {
expect(labelProvider.getIcon(new URI('file:///workspace'))).eq('rootfolder-icon');
expect(labelProvider.getIcon(FileStat.dir('file:///workspace'))).eq('rootfolder-icon');
});
});

describe('getName()', () => {
Expand Down
4 changes: 4 additions & 0 deletions packages/workspace/src/browser/workspace-uri-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export class WorkspaceUriLabelProviderContribution extends DefaultUriLabelProvid
}

getIcon(element: URI | URIIconReference | FileStat): string {
const uri = this.getUri(element);
if (uri && this.workspaceVariable.getWorkspaceRootUri(uri)?.isEqual(uri)) {
return 'rootfolder-icon';
}
return super.getIcon(this.asURIIconReference(element));
}

Expand Down

0 comments on commit ee4ac3d

Please sign in to comment.