@theia/core 1.20.0 → 1.21.0-next.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/browser/common-frontend-contribution.d.ts +1 -13
- package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
- package/lib/browser/common-frontend-contribution.js +86 -127
- package/lib/browser/common-frontend-contribution.js.map +1 -1
- package/lib/browser/frontend-application-module.d.ts.map +1 -1
- package/lib/browser/frontend-application-module.js +6 -5
- package/lib/browser/frontend-application-module.js.map +1 -1
- package/lib/browser/preferences/preference-provider.d.ts +0 -2
- package/lib/browser/preferences/preference-provider.d.ts.map +1 -1
- package/lib/browser/preferences/preference-provider.js +1 -5
- package/lib/browser/preferences/preference-provider.js.map +1 -1
- package/lib/browser/preferences/preference-service.spec.js +0 -3
- package/lib/browser/preferences/preference-service.spec.js.map +1 -1
- package/lib/browser/shell/application-shell.d.ts +7 -7
- package/lib/browser/shell/application-shell.d.ts.map +1 -1
- package/lib/browser/shell/application-shell.js +10 -17
- package/lib/browser/shell/application-shell.js.map +1 -1
- package/lib/browser/shell/current-widget-command-adapter.d.ts +39 -0
- package/lib/browser/shell/current-widget-command-adapter.d.ts.map +1 -0
- package/lib/browser/shell/current-widget-command-adapter.js +42 -0
- package/lib/browser/shell/current-widget-command-adapter.js.map +1 -0
- package/lib/browser/shell/tab-bars.d.ts +8 -2
- package/lib/browser/shell/tab-bars.d.ts.map +1 -1
- package/lib/browser/shell/tab-bars.js +30 -3
- package/lib/browser/shell/tab-bars.js.map +1 -1
- package/lib/common/promise-util.d.ts +3 -0
- package/lib/common/promise-util.d.ts.map +1 -1
- package/lib/common/promise-util.js +16 -1
- package/lib/common/promise-util.js.map +1 -1
- package/lib/common/promise-util.spec.d.ts +2 -0
- package/lib/common/promise-util.spec.d.ts.map +1 -0
- package/lib/common/promise-util.spec.js +42 -0
- package/lib/common/promise-util.spec.js.map +1 -0
- package/lib/electron-main/electron-main-application.d.ts +10 -0
- package/lib/electron-main/electron-main-application.d.ts.map +1 -1
- package/lib/electron-main/electron-main-application.js +13 -3
- package/lib/electron-main/electron-main-application.js.map +1 -1
- package/package.json +3 -3
- package/src/browser/common-frontend-contribution.ts +75 -132
- package/src/browser/frontend-application-module.ts +6 -5
- package/src/browser/preferences/preference-provider.ts +1 -5
- package/src/browser/preferences/preference-service.spec.ts +0 -3
- package/src/browser/shell/application-shell.ts +17 -22
- package/src/browser/shell/current-widget-command-adapter.ts +57 -0
- package/src/browser/shell/tab-bars.ts +30 -5
- package/src/common/promise-util.spec.ts +41 -0
- package/src/common/promise-util.ts +20 -1
- package/src/electron-main/electron-main-application.ts +24 -5
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
|
|
19
19
|
import debounce = require('lodash.debounce');
|
|
20
20
|
import { injectable, inject, optional } from 'inversify';
|
|
21
|
-
import { TabBar, Widget } from '@phosphor/widgets';
|
|
22
21
|
import { MAIN_MENU_BAR, SETTINGS_MENU, MenuContribution, MenuModelRegistry, ACCOUNTS_MENU } from '../common/menu';
|
|
23
22
|
import { KeybindingContribution, KeybindingRegistry } from './keybinding';
|
|
24
23
|
import { FrontendApplication, FrontendApplicationContribution } from './frontend-application';
|
|
@@ -28,7 +27,7 @@ import { SelectionService } from '../common/selection-service';
|
|
|
28
27
|
import { MessageService } from '../common/message-service';
|
|
29
28
|
import { OpenerService, open } from '../browser/opener-service';
|
|
30
29
|
import { ApplicationShell } from './shell/application-shell';
|
|
31
|
-
import {
|
|
30
|
+
import { SHELL_TABBAR_CONTEXT_CLOSE, SHELL_TABBAR_CONTEXT_COPY, SHELL_TABBAR_CONTEXT_SPLIT } from './shell/tab-bars';
|
|
32
31
|
import { AboutDialog } from './about-dialog';
|
|
33
32
|
import * as browser from './browser';
|
|
34
33
|
import URI from '../common/uri';
|
|
@@ -51,10 +50,11 @@ import { EncodingRegistry } from './encoding-registry';
|
|
|
51
50
|
import { UTF8 } from '../common/encodings';
|
|
52
51
|
import { EnvVariablesServer } from '../common/env-variables';
|
|
53
52
|
import { AuthenticationService } from './authentication-service';
|
|
54
|
-
import { FormatType } from './saveable';
|
|
53
|
+
import { FormatType, Saveable } from './saveable';
|
|
55
54
|
import { QuickInputService, QuickPick, QuickPickItem } from './quick-input';
|
|
56
55
|
import { AsyncLocalizationProvider } from '../common/i18n/localization';
|
|
57
56
|
import { nls } from '../common/nls';
|
|
57
|
+
import { CurrentWidgetCommandAdapter } from './shell/current-widget-command-adapter';
|
|
58
58
|
|
|
59
59
|
export namespace CommonMenus {
|
|
60
60
|
|
|
@@ -181,6 +181,11 @@ export namespace CommonCommands {
|
|
|
181
181
|
category: VIEW_CATEGORY,
|
|
182
182
|
label: 'Close Other Tabs'
|
|
183
183
|
}, 'theia/core/common/closeOthers', VIEW_CATEGORY_KEY);
|
|
184
|
+
export const CLOSE_SAVED_TABS = Command.toDefaultLocalizedCommand({
|
|
185
|
+
id: 'workbench.action.closeUnmodifiedEditors',
|
|
186
|
+
category: VIEW_CATEGORY,
|
|
187
|
+
label: 'Close Saved Editors in Group',
|
|
188
|
+
});
|
|
184
189
|
export const CLOSE_RIGHT_TABS = Command.toLocalizedCommand({
|
|
185
190
|
id: 'core.close.right.tabs',
|
|
186
191
|
category: VIEW_CATEGORY,
|
|
@@ -546,35 +551,45 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
|
|
|
546
551
|
order: '2'
|
|
547
552
|
});
|
|
548
553
|
|
|
549
|
-
registry.registerMenuAction(
|
|
554
|
+
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_CLOSE, {
|
|
550
555
|
commandId: CommonCommands.CLOSE_TAB.id,
|
|
551
556
|
label: nls.localizeByDefault('Close'),
|
|
552
557
|
order: '0'
|
|
553
558
|
});
|
|
554
|
-
registry.registerMenuAction(
|
|
559
|
+
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_CLOSE, {
|
|
555
560
|
commandId: CommonCommands.CLOSE_OTHER_TABS.id,
|
|
556
561
|
label: nls.localizeByDefault('Close Others'),
|
|
557
562
|
order: '1'
|
|
558
563
|
});
|
|
559
|
-
registry.registerMenuAction(
|
|
564
|
+
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_CLOSE, {
|
|
560
565
|
commandId: CommonCommands.CLOSE_RIGHT_TABS.id,
|
|
561
566
|
label: nls.localizeByDefault('Close to the Right'),
|
|
562
567
|
order: '2'
|
|
563
568
|
});
|
|
564
|
-
registry.registerMenuAction(
|
|
569
|
+
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_CLOSE, {
|
|
570
|
+
commandId: CommonCommands.CLOSE_SAVED_TABS.id,
|
|
571
|
+
label: nls.localizeByDefault('Close Saved'),
|
|
572
|
+
order: '3',
|
|
573
|
+
});
|
|
574
|
+
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_CLOSE, {
|
|
565
575
|
commandId: CommonCommands.CLOSE_ALL_TABS.id,
|
|
566
576
|
label: nls.localizeByDefault('Close All'),
|
|
567
|
-
order: '
|
|
577
|
+
order: '4'
|
|
568
578
|
});
|
|
569
|
-
registry.registerMenuAction(
|
|
579
|
+
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_SPLIT, {
|
|
570
580
|
commandId: CommonCommands.COLLAPSE_PANEL.id,
|
|
571
581
|
label: CommonCommands.COLLAPSE_PANEL.label,
|
|
572
|
-
order: '
|
|
582
|
+
order: '5'
|
|
573
583
|
});
|
|
574
|
-
registry.registerMenuAction(
|
|
584
|
+
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_SPLIT, {
|
|
575
585
|
commandId: CommonCommands.TOGGLE_MAXIMIZED.id,
|
|
576
586
|
label: CommonCommands.TOGGLE_MAXIMIZED.label,
|
|
577
|
-
order: '
|
|
587
|
+
order: '6'
|
|
588
|
+
});
|
|
589
|
+
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_COPY, {
|
|
590
|
+
commandId: CommonCommands.COPY_PATH.id,
|
|
591
|
+
label: CommonCommands.COPY_PATH.label,
|
|
592
|
+
order: '1',
|
|
578
593
|
});
|
|
579
594
|
registry.registerMenuAction(CommonMenus.HELP, {
|
|
580
595
|
commandId: CommonCommands.ABOUT_COMMAND.id,
|
|
@@ -633,6 +648,8 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
|
|
|
633
648
|
}
|
|
634
649
|
});
|
|
635
650
|
commandRegistry.registerCommand(CommonCommands.COPY_PATH, UriAwareCommandHandler.MultiSelect(this.selectionService, {
|
|
651
|
+
isVisible: uris => Array.isArray(uris) && uris.some(uri => uri instanceof URI),
|
|
652
|
+
isEnabled: uris => Array.isArray(uris) && uris.some(uri => uri instanceof URI),
|
|
636
653
|
execute: async uris => {
|
|
637
654
|
if (uris.length) {
|
|
638
655
|
const lineDelimiter = isWindows ? '\r\n' : '\n';
|
|
@@ -685,62 +702,45 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
|
|
|
685
702
|
isEnabled: () => this.shell.previousTabBar() !== undefined,
|
|
686
703
|
execute: () => this.shell.activatePreviousTabBar()
|
|
687
704
|
});
|
|
688
|
-
commandRegistry.registerCommand(CommonCommands.CLOSE_TAB, {
|
|
689
|
-
isEnabled:
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
const
|
|
710
|
-
return tabBar.titles.some(title => title !== currentTitle && title.closable);
|
|
711
|
-
},
|
|
712
|
-
execute: (event?: Event) => {
|
|
713
|
-
const tabBar = this.shell.findTabBar(event)!;
|
|
714
|
-
const currentTitle = this.shell.findTitle(tabBar, event);
|
|
715
|
-
this.shell.closeTabs(tabBar, title => title !== currentTitle && title.closable);
|
|
716
|
-
}
|
|
717
|
-
});
|
|
718
|
-
commandRegistry.registerCommand(CommonCommands.CLOSE_RIGHT_TABS, {
|
|
719
|
-
isEnabled: (event?: Event) => {
|
|
720
|
-
const tabBar = this.shell.findTabBar(event);
|
|
721
|
-
if (!tabBar) {
|
|
722
|
-
return false;
|
|
723
|
-
}
|
|
724
|
-
const currentIndex = this.findTitleIndex(tabBar, event);
|
|
725
|
-
return tabBar.titles.some((title, index) => index > currentIndex && title.closable);
|
|
726
|
-
},
|
|
727
|
-
isVisible: (event?: Event) => {
|
|
728
|
-
const area = this.findTabArea(event);
|
|
705
|
+
commandRegistry.registerCommand(CommonCommands.CLOSE_TAB, new CurrentWidgetCommandAdapter(this.shell, {
|
|
706
|
+
isEnabled: title => Boolean(title?.closable),
|
|
707
|
+
execute: (title, tabBar) => tabBar && this.shell.closeTabs(tabBar, candidate => candidate === title),
|
|
708
|
+
}));
|
|
709
|
+
commandRegistry.registerCommand(CommonCommands.CLOSE_OTHER_TABS, new CurrentWidgetCommandAdapter(this.shell, {
|
|
710
|
+
isEnabled: (title, tabbar) => Boolean(tabbar?.titles.some(candidate => candidate !== title && candidate.closable)),
|
|
711
|
+
execute: (title, tabbar) => tabbar && this.shell.closeTabs(tabbar, candidate => candidate !== title && candidate.closable),
|
|
712
|
+
}));
|
|
713
|
+
commandRegistry.registerCommand(CommonCommands.CLOSE_SAVED_TABS, new CurrentWidgetCommandAdapter(this.shell, {
|
|
714
|
+
isEnabled: (_title, tabbar) => Boolean(tabbar?.titles.some(candidate => candidate.closable && !Saveable.isDirty(candidate.owner))),
|
|
715
|
+
execute: (_title, tabbar) => tabbar && this.shell.closeTabs(tabbar, candidate => candidate.closable && !Saveable.isDirty(candidate.owner)),
|
|
716
|
+
}));
|
|
717
|
+
commandRegistry.registerCommand(CommonCommands.CLOSE_RIGHT_TABS, new CurrentWidgetCommandAdapter(this.shell, {
|
|
718
|
+
isEnabled: (title, tabbar) => {
|
|
719
|
+
let targetSeen = false;
|
|
720
|
+
return Boolean(tabbar?.titles.some(candidate => {
|
|
721
|
+
if (targetSeen && candidate.closable) { return true; };
|
|
722
|
+
if (candidate === title) { targetSeen = true; };
|
|
723
|
+
}));
|
|
724
|
+
},
|
|
725
|
+
isVisible: (_title, tabbar) => {
|
|
726
|
+
const area = (tabbar && this.shell.getAreaFor(tabbar)) ?? this.shell.currentTabArea;
|
|
729
727
|
return area !== undefined && area !== 'left' && area !== 'right';
|
|
730
728
|
},
|
|
731
|
-
execute: (
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
729
|
+
execute: (title, tabbar) => {
|
|
730
|
+
if (tabbar) {
|
|
731
|
+
let targetSeen = false;
|
|
732
|
+
this.shell.closeTabs(tabbar, candidate => {
|
|
733
|
+
if (targetSeen && candidate.closable) { return true; };
|
|
734
|
+
if (candidate === title) { targetSeen = true; };
|
|
735
|
+
return false;
|
|
736
|
+
});
|
|
737
|
+
}
|
|
735
738
|
}
|
|
736
|
-
});
|
|
737
|
-
commandRegistry.registerCommand(CommonCommands.CLOSE_ALL_TABS, {
|
|
738
|
-
isEnabled: (
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
},
|
|
742
|
-
execute: (event?: Event) => this.shell.closeTabs(this.shell.findTabBar(event)!, title => title.closable)
|
|
743
|
-
});
|
|
739
|
+
}));
|
|
740
|
+
commandRegistry.registerCommand(CommonCommands.CLOSE_ALL_TABS, new CurrentWidgetCommandAdapter(this.shell, {
|
|
741
|
+
isEnabled: (_title, tabbar) => Boolean(tabbar?.titles.some(title => title.closable)),
|
|
742
|
+
execute: (_title, tabbar) => tabbar && this.shell.closeTabs(tabbar, candidate => candidate.closable),
|
|
743
|
+
}));
|
|
744
744
|
commandRegistry.registerCommand(CommonCommands.CLOSE_MAIN_TAB, {
|
|
745
745
|
isEnabled: () => {
|
|
746
746
|
const currentWidget = this.shell.getCurrentWidget('main');
|
|
@@ -763,11 +763,11 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
|
|
|
763
763
|
isEnabled: () => this.shell.mainAreaTabBars.some(tb => tb.titles.some(title => title.closable)),
|
|
764
764
|
execute: () => this.shell.closeTabs('main', title => title.closable)
|
|
765
765
|
});
|
|
766
|
-
commandRegistry.registerCommand(CommonCommands.COLLAPSE_PANEL, {
|
|
767
|
-
isEnabled: (
|
|
768
|
-
isVisible: (
|
|
769
|
-
execute: (
|
|
770
|
-
});
|
|
766
|
+
commandRegistry.registerCommand(CommonCommands.COLLAPSE_PANEL, new CurrentWidgetCommandAdapter(this.shell, {
|
|
767
|
+
isEnabled: (_title, tabbar) => Boolean(tabbar && ApplicationShell.isSideArea(this.shell.getAreaFor(tabbar))),
|
|
768
|
+
isVisible: (_title, tabbar) => Boolean(tabbar && ApplicationShell.isSideArea(this.shell.getAreaFor(tabbar))),
|
|
769
|
+
execute: (_title, tabbar) => tabbar && this.shell.collapsePanel(this.shell.getAreaFor(tabbar)!)
|
|
770
|
+
}));
|
|
771
771
|
commandRegistry.registerCommand(CommonCommands.COLLAPSE_ALL_PANELS, {
|
|
772
772
|
execute: () => {
|
|
773
773
|
this.shell.collapsePanel('left');
|
|
@@ -788,11 +788,11 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
|
|
|
788
788
|
commandRegistry.registerCommand(CommonCommands.TOGGLE_STATUS_BAR, {
|
|
789
789
|
execute: () => this.preferenceService.updateValue('workbench.statusBar.visible', !this.preferences['workbench.statusBar.visible'])
|
|
790
790
|
});
|
|
791
|
-
commandRegistry.registerCommand(CommonCommands.TOGGLE_MAXIMIZED, {
|
|
792
|
-
isEnabled: (
|
|
793
|
-
isVisible: (
|
|
794
|
-
execute:
|
|
795
|
-
});
|
|
791
|
+
commandRegistry.registerCommand(CommonCommands.TOGGLE_MAXIMIZED, new CurrentWidgetCommandAdapter(this.shell, {
|
|
792
|
+
isEnabled: title => Boolean(title?.owner && this.shell.canToggleMaximized(title?.owner)),
|
|
793
|
+
isVisible: title => Boolean(title?.owner && this.shell.canToggleMaximized(title?.owner)),
|
|
794
|
+
execute: title => title?.owner && this.shell.toggleMaximized(title?.owner),
|
|
795
|
+
}));
|
|
796
796
|
|
|
797
797
|
commandRegistry.registerCommand(CommonCommands.SAVE, {
|
|
798
798
|
execute: () => this.shell.save({ formatType: FormatType.ON })
|
|
@@ -823,63 +823,6 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
|
|
|
823
823
|
});
|
|
824
824
|
}
|
|
825
825
|
|
|
826
|
-
private findTabArea(event?: Event): ApplicationShell.Area | undefined {
|
|
827
|
-
const tabBar = this.shell.findTabBar(event);
|
|
828
|
-
if (tabBar) {
|
|
829
|
-
return this.shell.getAreaFor(tabBar);
|
|
830
|
-
}
|
|
831
|
-
return this.shell.currentTabArea;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
/**
|
|
835
|
-
* Finds the index of the selected title from the tab-bar.
|
|
836
|
-
* @param tabBar: used for providing an array of titles.
|
|
837
|
-
* @returns the index of the selected title if it is available in the tab-bar, else returns the index of currently-selected title.
|
|
838
|
-
*/
|
|
839
|
-
private findTitleIndex(tabBar: TabBar<Widget>, event?: Event): number {
|
|
840
|
-
if (event) {
|
|
841
|
-
const targetTitle = this.shell.findTitle(tabBar, event);
|
|
842
|
-
return targetTitle ? tabBar.titles.indexOf(targetTitle) : tabBar.currentIndex;
|
|
843
|
-
}
|
|
844
|
-
return tabBar.currentIndex;
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
private canToggleMaximized(event?: Event): boolean {
|
|
848
|
-
if (event?.target instanceof HTMLElement) {
|
|
849
|
-
const widget = this.shell.findWidgetForElement(event.target);
|
|
850
|
-
if (widget) {
|
|
851
|
-
return this.shell.mainPanel.contains(widget) || this.shell.bottomPanel.contains(widget);
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
return this.shell.canToggleMaximized();
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
/**
|
|
858
|
-
* Maximize the bottom or the main dockpanel based on the widget.
|
|
859
|
-
* @param event used to find the selected widget.
|
|
860
|
-
*/
|
|
861
|
-
private toggleMaximized(event?: Event): void {
|
|
862
|
-
if (event?.target instanceof HTMLElement) {
|
|
863
|
-
const widget = this.shell.findWidgetForElement(event.target);
|
|
864
|
-
if (widget) {
|
|
865
|
-
if (this.shell.mainPanel.contains(widget)) {
|
|
866
|
-
this.shell.mainPanel.toggleMaximized();
|
|
867
|
-
} else if (this.shell.bottomPanel.contains(widget)) {
|
|
868
|
-
this.shell.bottomPanel.toggleMaximized();
|
|
869
|
-
}
|
|
870
|
-
if (widget instanceof TabBar) {
|
|
871
|
-
// reveals the widget when maximized.
|
|
872
|
-
const title = this.shell.findTitle(widget, event);
|
|
873
|
-
if (title) {
|
|
874
|
-
this.shell.revealWidget(title.owner.id);
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
} else {
|
|
879
|
-
this.shell.toggleMaximized();
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
|
|
883
826
|
private isElectron(): boolean {
|
|
884
827
|
return environment.electron.is();
|
|
885
828
|
}
|
|
@@ -165,11 +165,12 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo
|
|
|
165
165
|
|
|
166
166
|
bind(DockPanelRendererFactory).toFactory(context => () => context.container.get(DockPanelRenderer));
|
|
167
167
|
bind(DockPanelRenderer).toSelf();
|
|
168
|
-
bind(TabBarRendererFactory).toFactory(
|
|
169
|
-
const contextMenuRenderer =
|
|
170
|
-
const
|
|
171
|
-
const iconThemeService =
|
|
172
|
-
|
|
168
|
+
bind(TabBarRendererFactory).toFactory(({ container }) => () => {
|
|
169
|
+
const contextMenuRenderer = container.get(ContextMenuRenderer);
|
|
170
|
+
const tabBarDecoratorService = container.get(TabBarDecoratorService);
|
|
171
|
+
const iconThemeService = container.get(IconThemeService);
|
|
172
|
+
const selectionService = container.get(SelectionService);
|
|
173
|
+
return new TabBarRenderer(contextMenuRenderer, tabBarDecoratorService, iconThemeService, selectionService);
|
|
173
174
|
});
|
|
174
175
|
|
|
175
176
|
bindContributionProvider(bind, TabBarDecorator);
|
|
@@ -75,10 +75,6 @@ export abstract class PreferenceProvider implements Disposable {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
protected deferredChanges: PreferenceProviderDataChanges | undefined;
|
|
78
|
-
protected _pendingChanges: Promise<boolean> = Promise.resolve(false);
|
|
79
|
-
get pendingChanges(): Promise<boolean> {
|
|
80
|
-
return this._pendingChanges;
|
|
81
|
-
}
|
|
82
78
|
|
|
83
79
|
/**
|
|
84
80
|
* Informs the listeners that one or more preferences of this provider are changed.
|
|
@@ -94,7 +90,7 @@ export abstract class PreferenceProvider implements Disposable {
|
|
|
94
90
|
this.mergePreferenceProviderDataChange(changes[preferenceName]);
|
|
95
91
|
}
|
|
96
92
|
}
|
|
97
|
-
return this.
|
|
93
|
+
return this.fireDidPreferencesChanged();
|
|
98
94
|
}
|
|
99
95
|
|
|
100
96
|
protected mergePreferenceProviderDataChange(change: PreferenceProviderDataChange): void {
|
|
@@ -193,7 +193,6 @@ describe('Preference Service', () => {
|
|
|
193
193
|
assert.strictEqual(prefService.get('editor.insertSpaces'), undefined, 'get after');
|
|
194
194
|
assert.strictEqual(prefService.get('[go].editor.insertSpaces'), undefined, 'get after overridden');
|
|
195
195
|
|
|
196
|
-
assert.strictEqual(await prefSchema.pendingChanges, false);
|
|
197
196
|
assert.deepStrictEqual([], events.map(e => ({
|
|
198
197
|
preferenceName: e.preferenceName,
|
|
199
198
|
newValue: e.newValue,
|
|
@@ -488,7 +487,6 @@ describe('Preference Service', () => {
|
|
|
488
487
|
|
|
489
488
|
it('onPreferenceChanged #0', async () => {
|
|
490
489
|
const { preferences, schema } = prepareServices();
|
|
491
|
-
await schema.pendingChanges;
|
|
492
490
|
|
|
493
491
|
const events: PreferenceChange[] = [];
|
|
494
492
|
preferences.onPreferenceChanged(event => events.push(event));
|
|
@@ -511,7 +509,6 @@ describe('Preference Service', () => {
|
|
|
511
509
|
|
|
512
510
|
it('onPreferenceChanged #1', async () => {
|
|
513
511
|
const { preferences, schema } = prepareServices();
|
|
514
|
-
await schema.pendingChanges;
|
|
515
512
|
|
|
516
513
|
const events: PreferenceChange[] = [];
|
|
517
514
|
preferences.onPreferenceChanged(event => events.push(event));
|
|
@@ -29,10 +29,10 @@ import { Saveable, SaveableWidget, SaveOptions } from '../saveable';
|
|
|
29
29
|
import { StatusBarImpl, StatusBarEntry, StatusBarAlignment } from '../status-bar/status-bar';
|
|
30
30
|
import { TheiaDockPanel, BOTTOM_AREA_ID, MAIN_AREA_ID } from './theia-dock-panel';
|
|
31
31
|
import { SidePanelHandler, SidePanel, SidePanelHandlerFactory } from './side-panel-handler';
|
|
32
|
-
import { TabBarRendererFactory,
|
|
32
|
+
import { TabBarRendererFactory, SHELL_TABBAR_CONTEXT_MENU, ScrollableTabBar, ToolbarAwareTabBar } from './tab-bars';
|
|
33
33
|
import { SplitPositionHandler, SplitPositionOptions } from './split-panels';
|
|
34
34
|
import { FrontendApplicationStateService } from '../frontend-application-state';
|
|
35
|
-
import { TabBarToolbarRegistry, TabBarToolbarFactory
|
|
35
|
+
import { TabBarToolbarRegistry, TabBarToolbarFactory } from './tab-bar-toolbar';
|
|
36
36
|
import { ContextKeyService } from '../context-key-service';
|
|
37
37
|
import { Emitter } from '../../common/event';
|
|
38
38
|
import { waitForRevealed, waitForClosed } from '../widgets';
|
|
@@ -82,9 +82,9 @@ export class DockPanelRenderer implements DockLayout.IRenderer {
|
|
|
82
82
|
readonly tabBarClasses: string[] = [];
|
|
83
83
|
|
|
84
84
|
constructor(
|
|
85
|
-
@inject(TabBarRendererFactory) protected readonly tabBarRendererFactory:
|
|
85
|
+
@inject(TabBarRendererFactory) protected readonly tabBarRendererFactory: TabBarRendererFactory,
|
|
86
86
|
@inject(TabBarToolbarRegistry) protected readonly tabBarToolbarRegistry: TabBarToolbarRegistry,
|
|
87
|
-
@inject(TabBarToolbarFactory) protected readonly tabBarToolbarFactory:
|
|
87
|
+
@inject(TabBarToolbarFactory) protected readonly tabBarToolbarFactory: TabBarToolbarFactory,
|
|
88
88
|
@inject(BreadcrumbsRendererFactory) protected readonly breadcrumbsRendererFactory: BreadcrumbsRendererFactory,
|
|
89
89
|
) { }
|
|
90
90
|
|
|
@@ -847,20 +847,15 @@ export class ApplicationShell extends Widget {
|
|
|
847
847
|
*/
|
|
848
848
|
findTitle(tabBar: TabBar<Widget>, event?: Event): Title<Widget> | undefined {
|
|
849
849
|
if (event?.target instanceof HTMLElement) {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
return title;
|
|
858
|
-
}
|
|
859
|
-
title = tabBar.titles.find(t => t.label === tabNode!.title);
|
|
860
|
-
if (title) {
|
|
861
|
-
return title;
|
|
862
|
-
}
|
|
850
|
+
const tabNode = event.target;
|
|
851
|
+
|
|
852
|
+
const titleIndex = Array.from(tabBar.contentNode.getElementsByClassName('p-TabBar-tab'))
|
|
853
|
+
.findIndex(node => node.contains(tabNode));
|
|
854
|
+
|
|
855
|
+
if (titleIndex !== -1) {
|
|
856
|
+
return tabBar.titles[titleIndex];
|
|
863
857
|
}
|
|
858
|
+
|
|
864
859
|
}
|
|
865
860
|
return tabBar.currentTitle || undefined;
|
|
866
861
|
}
|
|
@@ -1832,18 +1827,18 @@ export class ApplicationShell extends Widget {
|
|
|
1832
1827
|
return undefined;
|
|
1833
1828
|
}
|
|
1834
1829
|
|
|
1835
|
-
canToggleMaximized(): boolean {
|
|
1836
|
-
const area =
|
|
1830
|
+
canToggleMaximized(widget: Widget | undefined = this.currentWidget): boolean {
|
|
1831
|
+
const area = widget && this.getAreaFor(widget);
|
|
1837
1832
|
return area === 'main' || area === 'bottom';
|
|
1838
1833
|
}
|
|
1839
1834
|
|
|
1840
|
-
toggleMaximized(): void {
|
|
1841
|
-
const area =
|
|
1835
|
+
toggleMaximized(widget: Widget | undefined = this.currentWidget): void {
|
|
1836
|
+
const area = widget && this.getAreaPanelFor(widget);
|
|
1842
1837
|
if (area instanceof TheiaDockPanel && (area === this.mainPanel || area === this.bottomPanel)) {
|
|
1843
1838
|
area.toggleMaximized();
|
|
1839
|
+
this.revealWidget(widget!.id);
|
|
1844
1840
|
}
|
|
1845
1841
|
}
|
|
1846
|
-
|
|
1847
1842
|
}
|
|
1848
1843
|
|
|
1849
1844
|
/**
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2021 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 { CommandHandler } from '../../common';
|
|
18
|
+
import { TabBar, Title, Widget } from '../widgets';
|
|
19
|
+
import { ApplicationShell } from './application-shell';
|
|
20
|
+
|
|
21
|
+
type CurrentWidgetCommandAdapterBooleanCheck = (event: Event) => boolean;
|
|
22
|
+
type CurrentWidgetCommandHandlerBooleanCheck = (title: Title<Widget> | undefined, tabbar: TabBar<Widget> | undefined, event: Event) => boolean;
|
|
23
|
+
|
|
24
|
+
export interface TabBarContextMenuCommandHandler extends CommandHandler {
|
|
25
|
+
execute(title: Title<Widget> | undefined, tabbar: TabBar<Widget> | undefined, event: Event): unknown;
|
|
26
|
+
isEnabled?: CurrentWidgetCommandHandlerBooleanCheck;
|
|
27
|
+
isVisible?: CurrentWidgetCommandHandlerBooleanCheck;
|
|
28
|
+
isToggled?: CurrentWidgetCommandHandlerBooleanCheck;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Creates a command handler that acts on either the widget targeted by a DOM event or the current widget.
|
|
33
|
+
*/
|
|
34
|
+
export class CurrentWidgetCommandAdapter implements CommandHandler {
|
|
35
|
+
execute: (event: Event) => unknown;
|
|
36
|
+
isEnabled?: CurrentWidgetCommandAdapterBooleanCheck;
|
|
37
|
+
isVisible?: CurrentWidgetCommandAdapterBooleanCheck;
|
|
38
|
+
isToggled?: CurrentWidgetCommandAdapterBooleanCheck;
|
|
39
|
+
constructor(shell: ApplicationShell, handler: TabBarContextMenuCommandHandler) {
|
|
40
|
+
this.execute = (event: Event) => handler.execute(...this.transformArguments(shell, event));
|
|
41
|
+
if (handler.isEnabled) {
|
|
42
|
+
this.isEnabled = (event: Event) => !!handler.isEnabled?.(...this.transformArguments(shell, event));
|
|
43
|
+
}
|
|
44
|
+
if (handler.isVisible) {
|
|
45
|
+
this.isVisible = (event: Event) => !!handler.isVisible?.(...this.transformArguments(shell, event));
|
|
46
|
+
}
|
|
47
|
+
if (handler.isToggled) {
|
|
48
|
+
this.isToggled = (event: Event) => !!handler.isToggled?.(...this.transformArguments(shell, event));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected transformArguments(shell: ApplicationShell, event: Event): [Title<Widget> | undefined, TabBar<Widget> | undefined, Event] {
|
|
53
|
+
const tabBar = shell.findTabBar(event);
|
|
54
|
+
const title = tabBar && shell.findTitle(tabBar, event);
|
|
55
|
+
return [title, tabBar, event];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import PerfectScrollbar from 'perfect-scrollbar';
|
|
18
18
|
import { TabBar, Title, Widget } from '@phosphor/widgets';
|
|
19
19
|
import { VirtualElement, h, VirtualDOM, ElementInlineStyle } from '@phosphor/virtualdom';
|
|
20
|
-
import { Disposable, DisposableCollection, MenuPath, notEmpty } from '../../common';
|
|
20
|
+
import { Disposable, DisposableCollection, MenuPath, notEmpty, SelectionService } from '../../common';
|
|
21
21
|
import { ContextMenuRenderer } from '../context-menu-renderer';
|
|
22
22
|
import { Signal, Slot } from '@phosphor/signaling';
|
|
23
23
|
import { Message, MessageLoop } from '@phosphor/messaging';
|
|
@@ -37,8 +37,14 @@ const HIDDEN_CONTENT_CLASS = 'theia-TabBar-hidden-content';
|
|
|
37
37
|
|
|
38
38
|
/** Menu path for tab bars used throughout the application shell. */
|
|
39
39
|
export const SHELL_TABBAR_CONTEXT_MENU: MenuPath = ['shell-tabbar-context-menu'];
|
|
40
|
+
export const SHELL_TABBAR_CONTEXT_CLOSE: MenuPath = [...SHELL_TABBAR_CONTEXT_MENU, '0_close'];
|
|
41
|
+
export const SHELL_TABBAR_CONTEXT_COPY: MenuPath = [...SHELL_TABBAR_CONTEXT_MENU, '1_copy'];
|
|
42
|
+
// Kept here in anticipation of tab pinning behavior implemented in tab-bars.ts
|
|
43
|
+
export const SHELL_TABBAR_CONTEXT_PIN: MenuPath = [...SHELL_TABBAR_CONTEXT_MENU, '4_pin'];
|
|
44
|
+
export const SHELL_TABBAR_CONTEXT_SPLIT: MenuPath = [...SHELL_TABBAR_CONTEXT_MENU, '5_split'];
|
|
40
45
|
|
|
41
46
|
export const TabBarRendererFactory = Symbol('TabBarRendererFactory');
|
|
47
|
+
export type TabBarRendererFactory = () => TabBarRenderer;
|
|
42
48
|
|
|
43
49
|
/**
|
|
44
50
|
* Size information of DOM elements used for rendering tabs in side bars.
|
|
@@ -66,7 +72,6 @@ export interface SideBarRenderData extends TabBar.IRenderData<Widget> {
|
|
|
66
72
|
* automatically.
|
|
67
73
|
*/
|
|
68
74
|
export class TabBarRenderer extends TabBar.Renderer {
|
|
69
|
-
|
|
70
75
|
/**
|
|
71
76
|
* The menu path used to render the context menu.
|
|
72
77
|
*/
|
|
@@ -80,7 +85,8 @@ export class TabBarRenderer extends TabBar.Renderer {
|
|
|
80
85
|
constructor(
|
|
81
86
|
protected readonly contextMenuRenderer?: ContextMenuRenderer,
|
|
82
87
|
protected readonly decoratorService?: TabBarDecoratorService,
|
|
83
|
-
protected readonly iconThemeService?: IconThemeService
|
|
88
|
+
protected readonly iconThemeService?: IconThemeService,
|
|
89
|
+
protected readonly selectionService?: SelectionService,
|
|
84
90
|
) {
|
|
85
91
|
super();
|
|
86
92
|
if (this.decoratorService) {
|
|
@@ -437,7 +443,27 @@ export class TabBarRenderer extends TabBar.Renderer {
|
|
|
437
443
|
if (this.contextMenuRenderer && this.contextMenuPath && event.currentTarget instanceof HTMLElement) {
|
|
438
444
|
event.stopPropagation();
|
|
439
445
|
event.preventDefault();
|
|
440
|
-
|
|
446
|
+
let widget: Widget | undefined = undefined;
|
|
447
|
+
if (this.tabBar) {
|
|
448
|
+
const titleIndex = Array.from(this.tabBar.contentNode.getElementsByClassName('p-TabBar-tab'))
|
|
449
|
+
.findIndex(node => node.contains(event.currentTarget as HTMLElement));
|
|
450
|
+
if (titleIndex !== -1) {
|
|
451
|
+
widget = this.tabBar.titles[titleIndex].owner;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const oldSelection = this.selectionService?.selection;
|
|
456
|
+
if (widget && this.selectionService) {
|
|
457
|
+
this.selectionService.selection = NavigatableWidget.is(widget) ? { uri: widget.getResourceUri() } : widget;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
this.contextMenuRenderer.render({
|
|
461
|
+
menuPath: this.contextMenuPath!,
|
|
462
|
+
anchor: event,
|
|
463
|
+
args: [event],
|
|
464
|
+
// We'd like to wait until the command triggered by the context menu has been run, but this should let it get through the preamble, at least.
|
|
465
|
+
onHide: () => setTimeout(() => { if (this.selectionService) { this.selectionService.selection = oldSelection; } })
|
|
466
|
+
});
|
|
441
467
|
}
|
|
442
468
|
};
|
|
443
469
|
|
|
@@ -452,7 +478,6 @@ export class TabBarRenderer extends TabBar.Renderer {
|
|
|
452
478
|
}
|
|
453
479
|
}
|
|
454
480
|
};
|
|
455
|
-
|
|
456
481
|
}
|
|
457
482
|
|
|
458
483
|
/**
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2021 Red Hat 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
|
+
import * as assert from 'assert';
|
|
17
|
+
import { waitForEvent } from './promise-util';
|
|
18
|
+
import { Emitter } from './event';
|
|
19
|
+
|
|
20
|
+
describe('promise-util', () => {
|
|
21
|
+
it('should time out', async () => {
|
|
22
|
+
const emitter = new Emitter<string>();
|
|
23
|
+
try {
|
|
24
|
+
await waitForEvent(emitter.event, 1000);
|
|
25
|
+
assert.fail('did not time out');
|
|
26
|
+
} catch (e) {
|
|
27
|
+
// OK
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('promise-util', () => {
|
|
32
|
+
it('should get event', async () => {
|
|
33
|
+
const emitter = new Emitter<string>();
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
emitter.fire('abcd');
|
|
36
|
+
}, 500);
|
|
37
|
+
assert.strictEqual(await waitForEvent(emitter.event, 1000), 'abcd');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
});
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
********************************************************************************/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { Disposable } from './disposable';
|
|
18
|
+
import { Event } from './event';
|
|
19
|
+
import { CancellationToken, CancellationError, cancelled } from './cancellation';
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Simple implementation of the deferred pattern.
|
|
@@ -90,3 +92,20 @@ export function delay<T>(ms: number): (value: T) => Promise<T> {
|
|
|
90
92
|
export async function wait(ms: number): Promise<void> {
|
|
91
93
|
await delay(ms)(undefined);
|
|
92
94
|
}
|
|
95
|
+
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
97
|
+
export function waitForEvent<T>(event: Event<T>, ms: number, thisArg?: any, disposables?: Disposable[]): Promise<T> {
|
|
98
|
+
return new Promise<T>((resolve, reject) => {
|
|
99
|
+
const registration = setTimeout(() => {
|
|
100
|
+
listener.dispose();
|
|
101
|
+
reject(new CancellationError());
|
|
102
|
+
}, ms);
|
|
103
|
+
|
|
104
|
+
const listener = event((evt: T) => {
|
|
105
|
+
clearTimeout(registration);
|
|
106
|
+
listener.dispose();
|
|
107
|
+
resolve(evt);
|
|
108
|
+
}, thisArg, disposables);
|
|
109
|
+
|
|
110
|
+
});
|
|
111
|
+
}
|