trebleCode
trebleCode

Reputation: 2308

VS Code - Updating Icons in TreeView in Custom ViewsContainer

So I made some progress since this post: Duplicate TreeView Schema is VS Code Extension

I've since uploaded the code to this repo:

https://github.com/trebleCode/dxdevcheck.git

I'm able to put the contents of a static JSON file into the view that I want in my custom viewsContainers setting. My idea is on activating that view, putting items into the view based on the JSON content with undefined iconPath values, so that when I hit a refresh button, some functions run and check whether those items are considered "configured" and if so, update them with an icon indicating either configured or not configured.

My problem now is I am stuck trying to figure out why the refresh() method that exists on my TreeDataViewProvider will not update those icons when I try to return my view again with a new set of objects that do have values to .svg files in the project.

Just clone the repo, run npm i from within the cloned directory, hit F5 and it should run.

A lightning bolt icon will appear on the left side panel which once clicked, will load a new menu, labeled VALIDATE. Underneath it will be four expandable/collapsible items with two children each. At the top right of this menu will be a circular icon when hovered over will say 'Validate the things!'. When clicked, this will put a ShowInformationMessage modal in the bottom right.

This is the refresh() method in the ValidateMenuProvider class in validateMenu.ts. I have commented out code in that section that I have tried and cannot understand why it behaves the way it does.

I think I am trying to implement the icon updating in the wrong place in my extension, so any pointers appreciated.

My class:

import * as vscode from "vscode";
import * as validateMenuItems from "./validateMenu.json";
import * as path from "path";
import * as customUtils from "./customUtils";

export class ValidateMenuProvider implements vscode.TreeDataProvider<ValidateMenu> {
    private _onDidChangeTreeData: vscode.EventEmitter<ValidateMenu | undefined> = new vscode.EventEmitter<ValidateMenu | undefined>();
    readonly onDidChangeTreeData: vscode.Event<ValidateMenu | undefined> = this._onDidChangeTreeData.event;

    constructor() {}

    refresh() {
        // Why does this not work to update icons?
        vscode.window.showInformationMessage('Refresh button clicked!');
        return this.getValidateMenu(true);
        //
    }

    getTreeItem(element?: ValidateMenu): vscode.TreeItem {
        return element;
    }

    getChildren(element?: any, withIcons?: boolean): ValidateMenu[] {
        if(element) {
            return element.children;
        }
        else {
            return this.getValidateMenu();
        }
    }

    getValidateMenu(withIcons?: boolean): ValidateMenu[] {
        const toChild = (label: string, icon?: any): ValidateMenu => { return new ValidateMenu(label,vscode.TreeItemCollapsibleState.None);};

        function getMenusFromParent(target: any) {
            let childrenArray: any = [];

            for(let i in target) {
                let currentChild: string = target[i].name;
                let currentConvertedChild = toChild(currentChild);

                if(withIcons === true) {
                    let configCheck = currentConvertedChild.isConfigured(target[i].name);

                    if(configCheck === true) {
                        currentConvertedChild.setConfiguredIcon();
                    }
                    else if(configCheck === false) {
                        currentConvertedChild.setErrorIcon();
                    }
                }
                childrenArray.push(currentConvertedChild);
            }

            return childrenArray;
        }

        function createMenu(label: string, target: any) {
            let childData = getMenusFromParent(target);
            return new ValidateMenu(label, vscode.TreeItemCollapsibleState.Expanded, childData);
        }

        let headings = Object(validateMenuItems.children);
        let child1 = headings['child1'];
        let child2 = headings['child2'];
        let child3 = headings['child3'];
        let child4 = headings['child4'];

        let menus: any = [];

        menus.push(createMenu('child1', child1));
        menus.push(createMenu('child2', child2));
        menus.push(createMenu('child3', child3));
        menus.push(createMenu('child4', child4));

        return menus;
    }
}


export class ValidateMenu extends vscode.TreeItem {
    constructor(
        public readonly label: string,
        public readonly collapsibleState: vscode.TreeItemCollapsibleState,
        public children?: ValidateMenu[],
        public readonly command?: vscode.Command,
        public iconPath?: { light: string, dark: string }
    ) {
        super(label,collapsibleState);
    }

    setConfiguredIcon(): void {
        let newLightIcon: any;
        let newDarkIcon: any;

        newLightIcon = path.join(__filename, '..', '..', 'resources', 'light', 'checkmark.svg');
        newDarkIcon = path.join(__filename, '..', '..', 'resources', 'dark', 'checkmark2.svg');

        if(this.iconPath === undefined) {
            this.iconPath = {light: newLightIcon, dark: newDarkIcon};
        }
        else {
            this.iconPath = {light: newLightIcon, dark: newDarkIcon};
        }
    }

    setErrorIcon(): void {
        let newLightIcon: any;
        let newDarkIcon: any;

        newLightIcon = path.join(__filename, '..', '..', 'resources', 'light', 'confused2.svg');
        newDarkIcon = path.join(__filename, '..', '..', 'resources', 'dark', 'confused.svg');

        if(this.iconPath === undefined) {
            this.iconPath = {light: newLightIcon, dark: newDarkIcon};
        }
        else {
            this.iconPath = {light: newLightIcon, dark: newDarkIcon};
        }
    }

    isConfigured(name: string): boolean {
        if(customUtils.checkIsConfigured(name) === true) {
            return true;
        }
        else {
            return false;
        }
    }
}

My static JSON file:

{
    "name": "root",
    "children": {
        "child1": [
            {"id": "id1", "name": "child1name1"},
            {"id": "id2", "name": "child2name2"}
        ],
        "child2": [
            {"id": "id1", "name": "child2name1"},
            {"id": "id2", "name": "child2name2"}
        ],
        "child3": [
            {"id": "id1", "name": "child3name1"},
            {"id": "id2", "name": "child3name2"}
        ],
        "child4": [
            {"id": "id1", "name": "child4name1"},
            {"id": "id2", "name": "child5name2"}
        ]
    }
}

And some helper utilities I call from some imported functions (customUtils.ts):

export function checkIsConfigured(name: string): boolean {
     let isConfigured: boolean;
    switch (name) {
        case 'child1':
                isConfigured = checkChild1();
            break;
        case 'child2':
             isConfigured = checkChild2();
            break;
        case 'child1':
             isConfigured = checkChild3();
            break;
        case 'child1':
             isConfigured = checkChild4();
            break;
    }
    return isConfigured;
}

export function checkChild1() {
    return true;
}

export function checkChild2() {
    return false;
}

export function checkChild3() {
    return true;
}

export function checkChild4() {
    return false;
}

Lastly, the package.json:

{
    "name": "myview",
    "displayName": "MYVIEW",
    "description": "checks dev things",
    "version": "0.0.2",
    "publisher": "someperson",
    "engines": {
        "vscode": "^1.28.0"
    },
    "categories": [
        "Other"
    ],
    "activationEvents": [
        "onView:validateMenu"
    ],
    "main": "./out/extension.js",
    "contributes": {
        "viewsContainers": {
            "activitybar": [
                {
                    "id": "myview",
                    "title": "My Custom View Container",
                    "icon": "resources/bolt.svg"
                }
            ]
        },
        "views": {
            "myview": [
                {
                    "id": "validateMenu",
                    "name": "Validate"
                }
            ]
        },
        "commands": [
            {
                "command": "validateMenu.refreshList",
                "title": "Validate the things!",
                "icon": "resources/spinner.svg"
            }
        ],
        "menus": {
            "view/title": [
                {
                    "command": "validateMenu.refreshList",
                    "when": "view == validateMenu",
                    "group": "navigation"
                }
            ]
        }
    },
    "scripts": {
        "vscode:prepublish": "npm run compile",
        "compile": "tsc -p ./",
        "watch": "tsc -watch -p ./",
        "postinstall": "node ./node_modules/vscode/bin/install",
        "test": "npm run compile && node ./node_modules/vscode/bin/test"
    },
    "devDependencies": {
        "@types/mocha": "^2.2.42",
        "@types/node": "^10.12.21",
        "tslint": "^5.12.1",
        "typescript": "^3.3.1",
        "vscode": "^1.1.28"
    },
    "dependencies": {}
}

Upvotes: 0

Views: 2945

Answers (1)

Robert Brisita
Robert Brisita

Reputation: 5844

To get icons to change, it needs to be a combination of setting the icon paths either in package.json and then setting the context value that will be evaluated when the refresh method is called or creating a TreeItem with iconPath and then calling the refresh method.

Example of the former:

  "menus": {
    "view/title": [{
      "command": "nodeDependencies.refreshEntry",
      "when": "view == nodeDependencies && shouldRefresh",
      "group": "navigation"
    }]
  }
vscode.commands.executeCommand('setContext', 'shouldRefresh', true);

Upvotes: 3

Related Questions