Custom filetype icon

I’ve managed to customize icon of my custom file extension, but I am unable to modify icon from navigation pane.

image

In my extension I am binding LabelProviderContribution my own implementation that overrides icon based on my file extension. What I am missing?

Thanks in advance.

@Delwing you’ll probably want to rebind the FileTreeLabelProvider to your custom LabelProviderContribution. This provider is used for all types of file trees (explorer, file open dialog, file save dialog).

Hey, I am yet completely green in the field. Not only in theia but in TypeScript.

When I do:

bind(FileTreeLabelProvider).to(MyDslWorkspaceLabelProviderContribution).inSingletonScope();

export class MyDslWorkspaceLabelProviderContribution extends FileTreeLabelProvider and overrides method canHandle and getIcon

I have a bunch of unknowns instead of file names and navigator is missing at all.

@Delwing here is an example of a CustomLabelProviderContribution which overrides the default implementation by using dependency injection (rebind mechanism).

In the changes I provided 2 examples:

  • Default icons for all tree nodes:

default

  • No icons for all tree nodes:

icon-none

It should give you an example on how to perform the necessary customizations for your own use-case and application, and since you mentioned you are new to the Theia framework and TypeScript, I think reviewing documentation / examples / source code will help you as well.

Thanks!

I am going through source and docs as well :slight_smile:

For better understanding what’s going on in this code you’ve linked.

rebind - we’re basically replacing component that will be injected as FileTreeLabelProvder, right?
bind(CustomLabelProviderContribution).toSelf() - we’re binding our label so it can be injected, right?

// This line creates a binding as:
// Name(CustomLabelProviderContribution) => Singleton(CustomLabelProviderContribution).
bind(CustomLabelProviderContribution).toSelf().inSingletonScope();

// This line REWIRES the old binding as:
// Name(FileTreeLabelProvider) => SameResolutionAsFor(CustomLabelProviderContribution)
rebind(FileTreeLabelProvider).toService(CustomLabelProviderContribution);

// The following line does almost the same as to the above two lines, but shorter:
rebind(FileTreeLabelProvider).to(CustomLabelProviderContribution).inSingletonScope();

Using rebind is important, else you will end up with the old binding + the new one and Inversify will complain later. I would recommend reading Inversify’s documentation to better understand the API.

Awesome, thanks.

Hi @Delwing!

I seem to have sort of the opposite problem of what you described in your first post. I got the icon to show in the Navigator view but when I click on the file and open its editor, I can’t get the icon to show in the editor’s tab (I just see the standard file icon). Also, it doesn’t show in the scm view. From your screenshot, it looks like you managed to get the icon to show in the tab. How did you manage to do that?

Btw, I based my implementation on the code from Theia generator’s label provider extension.

Any help would be welcome!

Well, it took me a couple of days to figure this one out, but I finally got everything to work. Below is a working example which will, hopefully, help anybody who has this problem in the future.
The code in the example will correctly show the icon in navigator and scm view, as well as the editor tab.
Any theme that has its own icon for this filetype will correctly show that icon instead of the custom icon.

Label provider contribution

import { LabelProviderContribution } from "@theia/core/lib/browser";
import { FileStatNode } from "@theia/filesystem/lib/browser/file-tree/file-tree";
import { FileTreeLabelProvider } from "@theia/filesystem/lib/browser/file-tree/file-tree-label-provider";
import { injectable } from "inversify";
import URI from '@theia/core/lib/common/uri';

@injectable()
export class CustomLabelProviderContribution implements LabelProviderContribution {

canHandle(element: object): number {
    if (element instanceof URI && element.path.ext === '.my') {
        return Number.MAX_SAFE_INTEGER;
    }

    return 0;
}

getIcon(): string {
    return 'fa fa-star-o';
}
}

@injectable()
export class CustomTreeLabelProviderContribution extends FileTreeLabelProvider {

canHandle(element: object): number {
    if (FileStatNode.is(element)) {
        let uri = element.uri;

        if (uri.path.ext === '.my') {
            return super.canHandle(element) + 1;
        }
    }

    return 0;
}

getIcon(): string {
    return 'fa fa-star-o';
}
}

Frontend module

import { LabelProviderContribution } from "@theia/core/lib/browser";
import { ContainerModule } from "inversify";
import { CustomLabelProviderContribution, CustomTreeLabelProviderContribution } from './label- 
provider-contribution';
import '../../src/browser/style/example.css';

export default new ContainerModule(bind => {

// label binding   
bind(CustomLabelProviderContribution).toSelf().inSingletonScope();
bind(CustomTreeLabelProviderContribution).toSelf().inSingletonScope();
bind(LabelProviderContribution).toService(CustomLabelProviderContribution);
bind(LabelProviderContribution).toService(CustomTreeLabelProviderContribution);
});

CSS

.fa-star-o:before{
    text-align: center;
    margin-right: 4px;
}
1 Like

Thank you for sharing the example :+1: