Skip to content

Commit ecdda15

Browse files
author
Anas Shahid
committed
[explorer]: Badge count for dirty editors
Fixes: #8296 Adds a badge count for dirty editors on the explorer tab icon. Signed-off-by: Anas Shahid <[email protected]>
1 parent fe24630 commit ecdda15

File tree

2 files changed

+70
-1
lines changed

2 files changed

+70
-1
lines changed

packages/navigator/src/browser/navigator-frontend-module.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { ContainerModule } from 'inversify';
2020
import {
2121
KeybindingContext, bindViewContribution,
2222
FrontendApplicationContribution, ViewContainer,
23-
ApplicationShellLayoutMigration
23+
ApplicationShellLayoutMigration,
2424
} from '@theia/core/lib/browser';
2525
import { FileNavigatorWidget, FILE_NAVIGATOR_ID, EXPLORER_VIEW_CONTAINER_ID, EXPLORER_VIEW_CONTAINER_TITLE_OPTIONS } from './navigator-widget';
2626
import { NavigatorActiveContext } from './navigator-keybinding-context';
@@ -33,6 +33,8 @@ import { NavigatorContextKeyService } from './navigator-context-key-service';
3333
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
3434
import { NavigatorDiff } from './navigator-diff';
3535
import { NavigatorLayoutVersion3Migration } from './navigator-layout-migrations';
36+
import { NavigatorTabBarDecorator } from './navigator-tab-bar-decorator';
37+
import { TabBarDecorator } from '@theia/core/lib/browser/shell/tab-bar-decorator';
3638

3739
export default new ContainerModule(bind => {
3840
bindFileNavigatorPreferences(bind);
@@ -72,4 +74,7 @@ export default new ContainerModule(bind => {
7274
bind(ApplicationShellLayoutMigration).to(NavigatorLayoutVersion3Migration).inSingletonScope();
7375

7476
bind(NavigatorDiff).toSelf().inSingletonScope();
77+
bind(NavigatorTabBarDecorator).toSelf().inSingletonScope();
78+
bind(FrontendApplicationContribution).toService(NavigatorTabBarDecorator);
79+
bind(TabBarDecorator).toService(NavigatorTabBarDecorator);
7580
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/********************************************************************************
2+
* Copyright (C) 2020 Ericsson and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the Eclipse
10+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
11+
* with the GNU Classpath Exception which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
********************************************************************************/
16+
17+
import { injectable } from 'inversify';
18+
import { Event, Emitter } from '@theia/core/lib/common/event';
19+
import { TabBarDecorator } from '@theia/core/lib/browser/shell/tab-bar-decorator';
20+
import { EXPLORER_VIEW_CONTAINER_ID } from './navigator-widget';
21+
import { Title, Widget, FrontendApplicationContribution, FrontendApplication, Navigatable, Saveable, ApplicationShell } from '@theia/core/lib/browser';
22+
import { WidgetDecoration } from '@theia/core/lib/browser/widget-decoration';
23+
import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable';
24+
25+
@injectable()
26+
export class NavigatorTabBarDecorator implements TabBarDecorator, FrontendApplicationContribution {
27+
readonly id = 'theia-navigator-tabbar-decorator';
28+
protected shell: ApplicationShell;
29+
30+
protected readonly emitter = new Emitter<void>();
31+
private readonly toDispose = new DisposableCollection();
32+
private readonly toDisposeOnDirtyChanged = new Map<Widget, Disposable>();
33+
34+
onStart(app: FrontendApplication): void {
35+
this.shell = app.shell;
36+
this.toDispose.push(this.shell.onDidAddWidget(w => {
37+
if (Navigatable.is(w) && Saveable.isSource(w)) {
38+
this.toDisposeOnDirtyChanged.set(w, w.saveable.onDirtyChanged(() => this.fireDidChangeDecorations()));
39+
}
40+
}));
41+
this.toDispose.push(this.shell.onDidRemoveWidget(w => this.toDisposeOnDirtyChanged.get(w)?.dispose));
42+
}
43+
44+
decorate(title: Title<Widget>): WidgetDecoration.Data[] {
45+
if (title.owner.id === EXPLORER_VIEW_CONTAINER_ID) {
46+
const changes = this.getDirtyEditorsCount();
47+
return changes > 0 ? [{ badge: changes }] : [];
48+
} else {
49+
return [];
50+
}
51+
}
52+
53+
getDirtyEditorsCount(): number {
54+
return this.shell.widgets.filter(widget => Navigatable.is(widget) && Saveable.isDirty(widget)).length;
55+
}
56+
57+
get onDidChangeDecorations(): Event<void> {
58+
return this.emitter.event;
59+
}
60+
61+
protected fireDidChangeDecorations(): void {
62+
this.emitter.fire(undefined);
63+
}
64+
}

0 commit comments

Comments
 (0)