Merge VS Code 1.26.1 (#2394)
* Squash merge commits for 1.26 (#1) (#2323) * Polish tag search as per feedback (#55269) * Polish tag search as per feedback * Updated regex * Allow users to opt-out of features that send online requests in the background (#55097) * settings sweep #54690 * Minor css tweaks to enable eoverflow elipsis in more places (#55277) * fix an issue with titlebarheight when not scaling with zoom * Settings descriptions update #54690 * fixes #55209 * Settings editor - many padding fixes * More space above level 2 label * Fixing Cannot debug npm script using Yarn #55103 * Settings editor - show ellipsis when description overflows * Settings editor - ... fix measuring around links, relayout * Setting descriptions * Settings editor - fix ... for some short lines, fix select container width * Settings editor - overlay trees so scrollable shadow is full width * Fix #54133 - missing extension settings after reload * Settings color token description tweak * Settings editor - disable overflow indicator temporarily, needs to be faster * Added command to Run the selected npm script * fixes #54452 * fixes #54929 * fixes #55248 * prefix command with extension name * Contribute run selected to the context menu * node-debug@1.26.6 * Allow terminal rendererType to be swapped out at runtime Part of #53274 Fixes #55344 * Settings editor - fix not focusing search when restoring editor setInput must be actually async. Will be fixed naturally when we aren't using winJS promises... * Settings editor - TOC should only expand the section with a selected item * Bump node-debug2 * Settings editor - Tree focus outlines * Settings editor - don't blink the scrollbar when toc selection changes And hide TOC correctly when the editor is narrow * Settings editor - header rows should not be selectable * fixes #54877 * change debug assignee to isi * Settings sweep (#54690) * workaround for #55051 * Settings sweep (#54690) * settings sweep #54690 * Don't try closing tags when you type > after another > * Describe what implementation code lens does Fixes #55370 * fix javadoc formatter setting description * fixes #55325 * update to officical TS version * Settings editor - Even more padding, use semibold instead of bold * Fix #55357 - fix TOC twistie * fixes #55288 * explorer: refresh on di change file system provider registration fixes #53256 * Disable push to Linux repo to test standalone publisher * New env var to notify log level to extensions #54001 * Disable snippets in extension search (when not in suggest dropdown) (#55281) * Disable snippits in extension search (when not in suggest dropdown) * Add monaco input contributions * Fix bug preventing snippetSuggestions from taking effect in sub-editors * Latest emmet helper to fix #52366 * Fix comment updates for threads within same file * Allow extensions to log telemetry to log files #54001 * Pull latest css grammar * files.exclude control - use same style for "add" vs "edit" * files.exclude control - focus/keyboard behavior * don't show menubar too early * files.exclude - better styling * Place cursor at end of extensions search box on autofill (#55254) * Place cursor at end of extensions search box on autofill * Use position instead of selection * fix linux build issue (empty if block) * Settings editor - fix extension category prefixes * Settings editor - add simple ellipsis for first line that overflows, doesn't cover case when first line does not overflow but there is more text, TODO * File/Text search provider docs * Fixes #52655 * Include epoch (#55008) * Fixes #53385 * Fixes #49480 * VS Code Insiders (Users) not opening Fixes #55353 * Better handling of the case when the extension host fails to start * Fixes #53966 * Remove confusing Start from wordPartLeft commands ID * vscode-xterm@3.6.0-beta12 Fixes #55488 * Initial size is set to infinity!! Fixes #55461 * Polish embeddedEditorBackground * configuration service misses event * Fix #55224 - fix duplicate results in multiroot workspace from splitting the diskseach query * Select all not working in issue reporter on mac, fixes #55424 * Disable fuzzy matching for extensions autosuggest (#55498) * Fix clipping of extensions search border in some third party themes (#55504) * fixes #55538 * Fix bug causing an aria alert to not be shown the third time (and odd numbers thereafter) * Settings editor - work around rendering glitch with webkit-line-clamp * Settings editor - revert earlier '...' changes * Settings editor - move enumDescription to its own div, because it disturbs -webkit-line-clamp for some reason * Settings editor - better overflow indicator * Don't show existing filters in autocomplete (#55495) * Dont show existing filters in autocomplete * Simplify * Settings Editor: Add aria labels for input elements Fixes: #54836 (#55543) * fixes #55223 * Update vscode-css-languageservice to 3.0.10-next.1 * Fix #55509 - settings navigation * Fix #55519 * Fix #55520 * FIx #55524 * Fix #55556 - include wordSeparators in all search queries, so findTextInFiles can respect isWordMatch correctly * oss updates for endgame * Fix unit tests * fixes #55522 * Avoid missing manifest error from bubbling up #54757 * Settings format crawl * Search provider - Fix FileSearchProvider to return array, not progress * Fix #55598 * Settings editor - fix NPE rendering settings with no description * dont render inden guides in search box (#55600) * fixes #55454 * More settings crawl * Another change for #55598 - maxResults applies to FileSearch and TextSearch but not FileIndex * Fix FileSearchProvider unit tests for progress change * fixes #55561 * Settings description update for #54690 * Update setting descriptions for online services * Minor edits * fixes #55513 * fixes #55451 * Fix #55612 - fix findTextInFiles cancellation * fixes #55539 * More setting description tweaks * Setting to disable online experiments #54354 * fixes #55507 * fixes #55515 * Show online services action only in Insiders for now * Settings editor - change toc behavior default to 'filter' * Settings editor - nicer filter count style during search * Fix #55617 - search viewlet icons * Settings editor - better styling for element count indicator * SearchProvider - fix NPE when searching extraFileResources * Allow extends to work without json suffix Fixes #16905 * Remove accessability options logic entirely Follow up on #55451 * use latest version of DAP * fixes #55490 * fixes #55122 * fixes #52332 * Avoid assumptions about git: URIs (fixes #36236) * relative path for descriptions * resourece: get rid of isFile context key fixes #48275 * Register previous ids for compatibility (#53497) * more tuning for #48275 * no need to always re-read "files explorer" fixes #52003 * read out active composites properly fixes #51967 * Update link colors for hc theme to meet color contrast ratio, fixes #55651 Also updated link color for `textLinkActiveForeground` to be the same as `textLinkForeground` as it wasn't properly updated * detect 'winpty-agent.exe'; fixes #55672 * node-debug@1.26.7 * reset counter on new label * Settings editor - fix multiple setting links in one description * Settings editor - color code blocks in setting descriptions, fix #55532 * Settings editor - hover color in TOC * Settings editor - fix navigation NPE * Settings editor - fix text control width * Settings editor - maybe fix #55684 * Fix bug causing cursor to not move on paste * fixes #53582 * Use ctrlCmd instead of ctrl for go down from search box * fixes #55264 * fixes #55456 * filter for spcaes before triggering search (#55611) * Fix #55698 - don't lose filtered TOC counts when refreshing TOC * fixes #55421 * fixes #28979 * fixes #55576 * only add check for updates to windows/linux help * readonly files: append decoration to label fixes #53022 * debug: do not show toolbar while initialising fixes #55026 * Opening launch.json should not activate debug extensions fixes #55029 * fixes #55435 * fixes #55434 * fixes #55439 * trigger menu only on altkey up * Fix #50555 - fix settings editor memory leak * Fix #55712 - no need to focus 'a' anymore when restoring control focus after tree render * fixes #55335 * proper fix for readonly model fixes #53022 * improve FoldingRangeKind spec (for #55686) * Use class with static fields (fixes #55494) * Fixes #53671 * fixes #54630 * [html] should disable ionic suggestions by default. Currently forces deprecated Ionic v1 suggestions in .html files while typing. Fixes #53324 * cleanup deps * debug issues back to andre * update electron for smoketest * Fix #55757 - prevent settings tabs from overflowing * Fix #53897 - revert setting menu defaults to old editor * Add enum descriptions to `typescript.preferences.importModuleSpecifier` * Fix #55767 - leaking style elements from settings editor * Fix #55521 - prevent flashing when clicking in exclude control * Update Git modified color for contrast ratio, fixes #53140 * Revert "Merge branch 'master' of github.com:Microsoft/vscode" This reverts commit bf46b6bfbae0cab99c2863e1244a916181fa9fbc, reversing changes made to e275a424483dfb4ed33b428c97d5e2c441d6b917. * Revert "Revert "Merge branch 'master' of github.com:Microsoft/vscode"" This reverts commit 53949d963f39e40757557c6526332354a31d9154. * don't ask to install an incomplete menu * Fix NPE in terminal AccessibilityManager Fixes #55744 * don't display fallback menu unless we've closed the last window * fixes #55547 * Fix smoke tests for extension search box * Update OSSREADME.json for Electron 2.0.5 * Update distro Includes Chromium license changes * fix #55455 * fix #55865 * fixes #55893 * Fix bug causing workspace recommendations to go away upon ignoring a recommendation (#55805) * Fix bug causing workspace recommendations to go away upon ignoring a recommendation * ONly show on @recommended or @recommended:workspace * Make more consistant * Fix #55911 * Understand json activity (#55926) * Understand json file activity * Refactoring * adding composer.json * Distro update for experiments * use terminal.processId for auto-attach; fixes #55918 * Reject invalid URI with vscode.openFolder (for #55891) * improve win32 setup system vs user detection fixes #55840 fixes #55840 delay winreg import related to #55840 show notification earlier related to #55840 fix #55840 update inno setup message related to #55840 * Fix #55593 - this code only operates on local paths, so use fsPath and Uri.file instead * Bring back the old menu due to electron 2.0 issues (#55913) * add the old menu back for native menus * make menu labels match * `vscode.openFolder`: treat missing URI schema gracefully (for #55891) * delay EH reattach; fixes #55955 * Mark all json files under appSettingsHome as settings * Use localized strings for telemetry opt-out * Exception when saving file editor opened from remote file provider (fixes #55051) * Remove terminal menu from stable Fixes 56003 * VSCode Insiders crashes on open with TypeError: Cannot read property 'lastIndexOf' of undefined. Fixes #54933 * improve fix for #55891 * fix #55916 * Improve #55891 * increase EH debugging restart delay; fixes #55955 * Revert "Don't include non-resource entries in history quick pick" This reverts commit 37209a838e9f7e9abe6dc53ed73cdf1e03b72060. * Diff editor: horizontal scrollbar height is smaller (fixes #56062) * improve openFolder uri fix (correctly treat backslashes) * fixes #56116 repair ipc for native menubar keybindings * Fix #56240 - Open the JSON settings editor instead of the UI editor * Fix #55536 * uriDisplay: if no formatter is registered fall back to getPathlabel fixes #56104 * VSCode hangs when opening python file. Fixes #56377 * VS Code Hangs When Opening Specific PowerShell File. Fixes #56430 * Fix #56433 - search extraFileResources even when no folders open * Workaround #55649 * Fix in master #56371 * Fix tests #56371 * Fix in master #56317 * increase version to 1.26.1 * Fixes #56387: Handle SIGPIPE in extension host * fixes #56185 * Fix merge issues (part 1) * Fix build breaks (part 1) * Build breaks (part 2) * Build breaks (part 3) * More build breaks (part 4) * Fix build breaks (part 5) * WIP * Fix menus * Render query result and message panels (#2363) * Put back query editor hot exit changes * Fix grid changes that broke profiler (#2365) * Update APIs for saving query editor state * Fix restore view state for profiler and edit data * Updating custom default themes to support 4.5:1 contrast ratio * Test updates * Fix Extension Manager and Windows Setup * Update license headers * Add appveyor and travis files back * Fix hidden modal dropdown issue
@@ -21,8 +21,9 @@ import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colo
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { ActivityAction, ActivityActionItem, ICompositeBarColors } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
|
||||
import { ActivityAction, ActivityActionItem, ICompositeBarColors, ToggleCompositePinnedAction, ICompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import URI from 'vs/base/common/uri';
|
||||
|
||||
export class ViewletActivityAction extends ActivityAction {
|
||||
|
||||
@@ -39,7 +40,7 @@ export class ViewletActivityAction extends ActivityAction {
|
||||
super(activity);
|
||||
}
|
||||
|
||||
public run(event: any): TPromise<any> {
|
||||
run(event: any): TPromise<any> {
|
||||
if (event instanceof MouseEvent && event.button === 2) {
|
||||
return TPromise.as(false); // do not run on right click
|
||||
}
|
||||
@@ -85,7 +86,7 @@ export class ToggleViewletAction extends Action {
|
||||
super(_viewlet.id, _viewlet.name);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
run(): TPromise<any> {
|
||||
const sideBarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
|
||||
@@ -116,7 +117,7 @@ export class GlobalActivityActionItem extends ActivityActionItem {
|
||||
super(action, { draggable: false, colors, icon: true }, themeService);
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
render(container: HTMLElement): void {
|
||||
super.render(container);
|
||||
|
||||
// Context menus are triggered on mouse down so that an item can be picked
|
||||
@@ -158,6 +159,41 @@ export class GlobalActivityActionItem extends ActivityActionItem {
|
||||
}
|
||||
}
|
||||
|
||||
export class PlaceHolderViewletActivityAction extends ViewletActivityAction {
|
||||
|
||||
constructor(
|
||||
id: string, iconUrl: URI,
|
||||
@IViewletService viewletService: IViewletService,
|
||||
@IPartService partService: IPartService,
|
||||
@ITelemetryService telemetryService: ITelemetryService
|
||||
) {
|
||||
super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, partService, telemetryService);
|
||||
|
||||
const iconClass = `.monaco-workbench > .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar
|
||||
DOM.createCSSRule(iconClass, `-webkit-mask: url('${iconUrl || ''}') no-repeat 50% 50%`);
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
setActivity(activity: IActivity): void {
|
||||
this.activity = activity;
|
||||
this.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
export class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinnedAction {
|
||||
|
||||
constructor(id: string, compositeBar: ICompositeBar) {
|
||||
super({ id, name: id, cssClass: void 0 }, compositeBar);
|
||||
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
setActivity(activity: IActivity): void {
|
||||
this.label = activity.name;
|
||||
this.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
|
||||
// Styling with Outline color (e.g. high contrast theme)
|
||||
|
||||
@@ -7,85 +7,105 @@
|
||||
|
||||
import 'vs/css!./media/activitybarpart';
|
||||
import * as nls from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { illegalArgument } from 'vs/base/common/errors';
|
||||
import { $ } from 'vs/base/browser/builder';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { ActionsOrientation, ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/common/activity';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Part } from 'vs/workbench/browser/part';
|
||||
import { GlobalActivityActionItem, GlobalActivityAction, ViewletActivityAction, ToggleViewletAction } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
|
||||
import { GlobalActivityActionItem, GlobalActivityAction, ViewletActivityAction, ToggleViewletAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewletActivityAction } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/toggleActivityBarVisibility';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar';
|
||||
import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { scheduleAtNextAnimationFrame, Dimension } from 'vs/base/browser/dom';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ViewLocation, ViewsRegistry } from 'vs/workbench/common/views';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
|
||||
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
|
||||
interface IPlaceholderComposite {
|
||||
id: string;
|
||||
iconUrl: URI;
|
||||
}
|
||||
|
||||
export class ActivitybarPart extends Part {
|
||||
|
||||
private static readonly ACTION_HEIGHT = 50;
|
||||
private static readonly PINNED_VIEWLETS = 'workbench.activity.pinnedViewlets';
|
||||
private static readonly PLACEHOLDER_VIEWLETS = 'workbench.activity.placeholderViewlets';
|
||||
private static readonly COLORS = {
|
||||
backgroundColor: ACTIVITY_BAR_FOREGROUND,
|
||||
badgeBackground: ACTIVITY_BAR_BADGE_BACKGROUND,
|
||||
badgeForeground: ACTIVITY_BAR_BADGE_FOREGROUND,
|
||||
dragAndDropBackground: ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND
|
||||
};
|
||||
private static readonly ACTION_HEIGHT = 50;
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
private dimension: Dimension;
|
||||
|
||||
private globalActionBar: ActionBar;
|
||||
private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; };
|
||||
private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; } = Object.create(null);
|
||||
|
||||
private placeholderComposites: IPlaceholderComposite[] = [];
|
||||
private compositeBar: CompositeBar;
|
||||
private compositeActions: { [compositeId: string]: { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null);
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@IViewletService private viewletService: IViewletService,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IPartService private partService: IPartService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
// {{SQL CARBON EDIT}}
|
||||
@IStorageService private storageService: IStorageService
|
||||
@ILifecycleService private lifecycleService: ILifecycleService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@IExtensionService private extensionService: IExtensionService
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService);
|
||||
|
||||
this.globalActivityIdToActions = Object.create(null);
|
||||
|
||||
this.compositeBar = this.instantiationService.createInstance(CompositeBar, {
|
||||
this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, {
|
||||
icon: true,
|
||||
storageId: ActivitybarPart.PINNED_VIEWLETS,
|
||||
orientation: ActionsOrientation.VERTICAL,
|
||||
composites: this.viewletService.getViewlets(),
|
||||
openComposite: (compositeId: string) => this.viewletService.openViewlet(compositeId, true),
|
||||
getActivityAction: (compositeId: string) => this.instantiationService.createInstance(ViewletActivityAction, this.viewletService.getViewlet(compositeId)),
|
||||
getCompositePinnedAction: (compositeId: string) => new ToggleCompositePinnedAction(this.viewletService.getViewlet(compositeId), this.compositeBar),
|
||||
getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction,
|
||||
getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction,
|
||||
getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(ToggleViewletAction, this.viewletService.getViewlet(compositeId)),
|
||||
getContextMenuActions: () => [this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar"))],
|
||||
getDefaultCompositeId: () => this.viewletService.getDefaultViewletId(),
|
||||
hidePart: () => this.partService.setSideBarHidden(true),
|
||||
compositeSize: 50,
|
||||
colors: ActivitybarPart.COLORS,
|
||||
overflowActionSize: ActivitybarPart.ACTION_HEIGHT
|
||||
});
|
||||
}));
|
||||
|
||||
const previousState = this.storageService.get(ActivitybarPart.PLACEHOLDER_VIEWLETS, StorageScope.GLOBAL, void 0);
|
||||
if (previousState) {
|
||||
let parsedPreviousState = <IPlaceholderComposite[]>JSON.parse(previousState);
|
||||
parsedPreviousState.forEach((s) => {
|
||||
if (typeof s.iconUrl === 'object') {
|
||||
s.iconUrl = URI.revive(s.iconUrl);
|
||||
} else {
|
||||
s.iconUrl = void 0;
|
||||
}
|
||||
});
|
||||
this.placeholderComposites = parsedPreviousState;
|
||||
} else {
|
||||
this.placeholderComposites = this.compositeBar.getCompositesFromStorage().map(id => (<IPlaceholderComposite>{ id, iconUrl: void 0 }));
|
||||
}
|
||||
|
||||
this.registerListeners();
|
||||
this.updateCompositebar();
|
||||
this.updatePlaceholderComposites();
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -101,27 +121,30 @@ export class ActivitybarPart extends Part {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
this.toUnbind.push(this.viewletService.onDidViewletRegister(() => this.updateCompositebar()));
|
||||
this.toUnbind.push(ViewsRegistry.onViewsRegistered(() => this.updateCompositebar()));
|
||||
this.toUnbind.push(ViewsRegistry.onViewsDeregistered(() => this.updateCompositebar()));
|
||||
this._register(this.viewletService.onDidViewletRegister(() => this.updateCompositebar()));
|
||||
|
||||
// Activate viewlet action on opening of a viewlet
|
||||
this.toUnbind.push(this.viewletService.onDidViewletOpen(viewlet => this.compositeBar.activateComposite(viewlet.getId())));
|
||||
this._register(this.viewletService.onDidViewletOpen(viewlet => this.compositeBar.activateComposite(viewlet.getId())));
|
||||
|
||||
// Deactivate viewlet action on close
|
||||
this.toUnbind.push(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId())));
|
||||
this.toUnbind.push(this.compositeBar.onDidContextMenu(e => this.showContextMenu(e)));
|
||||
this.toUnbind.push(this.viewletService.onDidViewletEnablementChange(({ id, enabled }) => {
|
||||
this._register(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId())));
|
||||
this._register(this.viewletService.onDidViewletEnablementChange(({ id, enabled }) => {
|
||||
if (enabled) {
|
||||
this.compositeBar.addComposite(this.viewletService.getViewlet(id), true);
|
||||
this.compositeBar.addComposite(this.viewletService.getViewlet(id));
|
||||
} else {
|
||||
this.compositeBar.removeComposite(id);
|
||||
this.removeComposite(id);
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.extensionService.onDidRegisterExtensions(() => this.onDidRegisterExtensions()));
|
||||
}
|
||||
|
||||
public showActivity(viewletOrActionId: string, badge: IBadge, clazz?: string, priority?: number): IDisposable {
|
||||
private onDidRegisterExtensions(): void {
|
||||
this.removeNotExistingPlaceholderComposites();
|
||||
this.updateCompositebar();
|
||||
}
|
||||
|
||||
showActivity(viewletOrActionId: string, badge: IBadge, clazz?: string, priority?: number): IDisposable {
|
||||
if (this.viewletService.getViewlet(viewletOrActionId)) {
|
||||
return this.compositeBar.showActivity(viewletOrActionId, badge, clazz, priority);
|
||||
}
|
||||
@@ -144,7 +167,7 @@ export class ActivitybarPart extends Part {
|
||||
return toDisposable(() => action.setBadge(undefined));
|
||||
}
|
||||
|
||||
public createContentArea(parent: HTMLElement): HTMLElement {
|
||||
createContentArea(parent: HTMLElement): HTMLElement {
|
||||
const $el = $(parent);
|
||||
const $result = $('.content').appendTo($el);
|
||||
|
||||
@@ -154,10 +177,31 @@ export class ActivitybarPart extends Part {
|
||||
// Top Actionbar with action items for each viewlet action
|
||||
this.createGlobalActivityActionBar($('.global-activity').appendTo($result).getHTMLElement());
|
||||
|
||||
// TODO@Ben: workaround for https://github.com/Microsoft/vscode/issues/45700
|
||||
// It looks like there are rendering glitches on macOS with Chrome 61 when
|
||||
// using --webkit-mask with a background color that is different from the image
|
||||
// The workaround is to promote the element onto its own drawing layer. We do
|
||||
// this only after the workbench has loaded because otherwise there is ugly flicker.
|
||||
if (isMacintosh) {
|
||||
this.lifecycleService.when(LifecyclePhase.Running).then(() => {
|
||||
scheduleAtNextAnimationFrame(() => { // another delay...
|
||||
scheduleAtNextAnimationFrame(() => { // ...to prevent more flickering on startup
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const activityBarForeground = theme.getColor(ACTIVITY_BAR_FOREGROUND);
|
||||
if (activityBarForeground && !activityBarForeground.equals(Color.white)) {
|
||||
// only apply this workaround if the color is different from the image one (white)
|
||||
collector.addRule('.monaco-workbench .activitybar > .content .monaco-action-bar .action-label { will-change: transform; }');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return $result.getHTMLElement();
|
||||
}
|
||||
|
||||
public updateStyles(): void {
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
// Part container
|
||||
@@ -176,22 +220,6 @@ export class ActivitybarPart extends Part {
|
||||
container.style('border-left-color', !isPositionLeft ? borderColor : null);
|
||||
}
|
||||
|
||||
private showContextMenu(e: MouseEvent): void {
|
||||
const event = new StandardMouseEvent(e);
|
||||
|
||||
const actions: Action[] = this.viewletService.getViewlets()
|
||||
.filter(viewlet => this.canShow(viewlet))
|
||||
.map(viewlet => this.instantiationService.createInstance(ToggleCompositePinnedAction, viewlet, this.compositeBar));
|
||||
actions.push(new Separator());
|
||||
actions.push(this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar")));
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => { return { x: event.posx, y: event.posy }; },
|
||||
getActions: () => TPromise.as(actions),
|
||||
onHide: () => dispose(actions)
|
||||
});
|
||||
}
|
||||
|
||||
private createGlobalActivityActionBar(container: HTMLElement): void {
|
||||
const activityRegistry = Registry.as<IGlobalActivityRegistry>(GlobalActivityExtensions);
|
||||
const descriptors = activityRegistry.getActivities();
|
||||
@@ -199,13 +227,12 @@ export class ActivitybarPart extends Part {
|
||||
.map(d => this.instantiationService.createInstance(d))
|
||||
.map(a => new GlobalActivityAction(a));
|
||||
|
||||
this.globalActionBar = new ActionBar(container, {
|
||||
this.globalActionBar = this._register(new ActionBar(container, {
|
||||
actionItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionItem, a, ActivitybarPart.COLORS),
|
||||
orientation: ActionsOrientation.VERTICAL,
|
||||
ariaLabel: nls.localize('globalActions', "Global Actions"),
|
||||
animated: false
|
||||
});
|
||||
this.toUnbind.push(this.globalActionBar);
|
||||
}));
|
||||
|
||||
actions.forEach(a => {
|
||||
this.globalActivityIdToActions[a.id] = a;
|
||||
@@ -213,34 +240,91 @@ export class ActivitybarPart extends Part {
|
||||
});
|
||||
}
|
||||
|
||||
private getCompositeActions(compositeId: string): { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction } {
|
||||
let compositeActions = this.compositeActions[compositeId];
|
||||
if (!compositeActions) {
|
||||
const viewlet = this.viewletService.getViewlet(compositeId);
|
||||
if (viewlet) {
|
||||
compositeActions = {
|
||||
activityAction: this.instantiationService.createInstance(ViewletActivityAction, viewlet),
|
||||
pinnedAction: new ToggleCompositePinnedAction(viewlet, this.compositeBar)
|
||||
};
|
||||
} else {
|
||||
const placeHolderComposite = this.placeholderComposites.filter(c => c.id === compositeId)[0];
|
||||
compositeActions = {
|
||||
activityAction: this.instantiationService.createInstance(PlaceHolderViewletActivityAction, compositeId, placeHolderComposite.iconUrl),
|
||||
pinnedAction: new PlaceHolderToggleCompositePinnedAction(compositeId, this.compositeBar)
|
||||
};
|
||||
}
|
||||
|
||||
this.compositeActions[compositeId] = compositeActions;
|
||||
}
|
||||
|
||||
return compositeActions;
|
||||
}
|
||||
|
||||
private updateCompositebar(): void {
|
||||
const viewlets = this.viewletService.getViewlets();
|
||||
for (const viewlet of viewlets) {
|
||||
const canShow = this.canShow(viewlet);
|
||||
if (canShow) {
|
||||
this.compositeBar.addComposite(viewlet, false);
|
||||
} else {
|
||||
this.compositeBar.removeComposite(viewlet.id);
|
||||
this.compositeBar.addComposite(viewlet);
|
||||
|
||||
// Pin it by default if it is new => it does not has a placeholder
|
||||
if (this.placeholderComposites.every(c => c.id !== viewlet.id)) {
|
||||
this.compositeBar.pin(viewlet.id);
|
||||
}
|
||||
|
||||
this.enableCompositeActions(viewlet);
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
if (activeViewlet && activeViewlet.getId() === viewlet.id) {
|
||||
this.compositeBar.pin(viewlet.id);
|
||||
this.compositeBar.activateComposite(viewlet.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private canShow(viewlet: ViewletDescriptor): boolean {
|
||||
const viewLocation = ViewLocation.get(viewlet.id);
|
||||
if (viewLocation) {
|
||||
return ViewsRegistry.getViews(viewLocation).length > 0;
|
||||
private updatePlaceholderComposites(): void {
|
||||
const viewlets = this.viewletService.getViewlets();
|
||||
for (const { id } of this.placeholderComposites) {
|
||||
if (viewlets.every(viewlet => viewlet.id !== id)) {
|
||||
this.compositeBar.addComposite({ id, name: id, order: void 0 });
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public getPinned(): string[] {
|
||||
private removeNotExistingPlaceholderComposites(): void {
|
||||
const viewlets = this.viewletService.getViewlets();
|
||||
for (const { id } of this.placeholderComposites) {
|
||||
if (viewlets.every(viewlet => viewlet.id !== id)) {
|
||||
this.removeComposite(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private removeComposite(compositeId: string): void {
|
||||
this.compositeBar.removeComposite(compositeId);
|
||||
const compositeActions = this.compositeActions[compositeId];
|
||||
if (compositeActions) {
|
||||
compositeActions.activityAction.dispose();
|
||||
compositeActions.pinnedAction.dispose();
|
||||
delete this.compositeActions[compositeId];
|
||||
}
|
||||
}
|
||||
|
||||
private enableCompositeActions(viewlet: ViewletDescriptor): void {
|
||||
const { activityAction, pinnedAction } = this.getCompositeActions(viewlet.id);
|
||||
if (activityAction instanceof PlaceHolderViewletActivityAction) {
|
||||
activityAction.setActivity(viewlet);
|
||||
}
|
||||
if (pinnedAction instanceof PlaceHolderToggleCompositePinnedAction) {
|
||||
pinnedAction.setActivity(viewlet);
|
||||
}
|
||||
}
|
||||
|
||||
getPinned(): string[] {
|
||||
return this.viewletService.getViewlets().map(v => v.id).filter(id => this.compositeBar.isPinned(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout title, content and status area in the given dimension.
|
||||
*/
|
||||
public layout(dimension: Dimension): Dimension[] {
|
||||
layout(dimension: Dimension): Dimension[] {
|
||||
if (!this.partService.isVisible(Parts.ACTIVITYBAR_PART)) {
|
||||
return [dimension];
|
||||
}
|
||||
@@ -260,22 +344,10 @@ export class ActivitybarPart extends Part {
|
||||
return sizes;
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.compositeBar.shutdown();
|
||||
shutdown(): void {
|
||||
const state = this.viewletService.getViewlets().map(viewlet => ({ id: viewlet.id, iconUrl: viewlet.iconUrl }));
|
||||
this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEWLETS, JSON.stringify(state), StorageScope.GLOBAL);
|
||||
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.compositeBar) {
|
||||
this.compositeBar.dispose();
|
||||
this.compositeBar = null;
|
||||
}
|
||||
|
||||
if (this.globalActionBar) {
|
||||
this.globalActionBar.dispose();
|
||||
this.globalActionBar = null;
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { Part, IPartOptions } from 'vs/workbench/browser/part';
|
||||
import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite';
|
||||
import { IComposite } from 'vs/workbench/common/composite';
|
||||
import { WorkbenchProgressService } from 'vs/workbench/services/progress/browser/progressService';
|
||||
import { ScopedProgressService } from 'vs/workbench/services/progress/browser/progressService';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
@@ -51,6 +51,12 @@ export interface ICompositeTitleLabel {
|
||||
}
|
||||
|
||||
export abstract class CompositePart<T extends Composite> extends Part {
|
||||
|
||||
protected _onDidCompositeOpen = this._register(new Emitter<IComposite>());
|
||||
protected _onDidCompositeClose = this._register(new Emitter<IComposite>());
|
||||
|
||||
protected toolBar: ToolBar;
|
||||
|
||||
private instantiatedCompositeListeners: IDisposable[];
|
||||
private mapCompositeToCompositeContainer: { [compositeId: string]: Builder; };
|
||||
private mapActionsBindingToComposite: { [compositeId: string]: () => void; };
|
||||
@@ -59,13 +65,10 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
private lastActiveCompositeId: string;
|
||||
private instantiatedComposites: Composite[];
|
||||
private titleLabel: ICompositeTitleLabel;
|
||||
protected toolBar: ToolBar;
|
||||
private progressBar: ProgressBar;
|
||||
private contentAreaSize: Dimension;
|
||||
private telemetryActionsListener: IDisposable;
|
||||
private currentCompositeOpenToken: string;
|
||||
protected _onDidCompositeOpen = new Emitter<IComposite>();
|
||||
protected _onDidCompositeClose = new Emitter<IComposite>();
|
||||
|
||||
constructor(
|
||||
private notificationService: INotificationService,
|
||||
@@ -178,7 +181,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
// Instantiate composite from registry otherwise
|
||||
const compositeDescriptor = this.registry.getComposite(id);
|
||||
if (compositeDescriptor) {
|
||||
const progressService = this.instantiationService.createInstance(WorkbenchProgressService, this.progressBar, compositeDescriptor.id, isActive);
|
||||
const progressService = this.instantiationService.createInstance(ScopedProgressService, this.progressBar, compositeDescriptor.id, isActive);
|
||||
const compositeInstantiationService = this.instantiationService.createChild(new ServiceCollection([IProgressService, progressService]));
|
||||
|
||||
const composite = compositeDescriptor.instantiate(compositeInstantiationService);
|
||||
@@ -400,7 +403,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
});
|
||||
}
|
||||
|
||||
public createTitleArea(parent: HTMLElement): HTMLElement {
|
||||
createTitleArea(parent: HTMLElement): HTMLElement {
|
||||
|
||||
// Title Area Container
|
||||
const titleArea = $(parent).div({
|
||||
@@ -416,11 +419,11 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
}, div => {
|
||||
|
||||
// Toolbar
|
||||
this.toolBar = new ToolBar(div.getHTMLElement(), this.contextMenuService, {
|
||||
this.toolBar = this._register(new ToolBar(div.getHTMLElement(), this.contextMenuService, {
|
||||
actionItemProvider: action => this.actionItemProvider(action as Action),
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id)
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
return titleArea.getHTMLElement();
|
||||
@@ -431,7 +434,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
$(parent).div({
|
||||
'class': 'title-label'
|
||||
}, div => {
|
||||
titleLabel = div.span();
|
||||
titleLabel = div.element('h2');
|
||||
});
|
||||
|
||||
const $this = this;
|
||||
@@ -463,12 +466,12 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public createContentArea(parent: HTMLElement): HTMLElement {
|
||||
createContentArea(parent: HTMLElement): HTMLElement {
|
||||
return $(parent).div({
|
||||
'class': 'content'
|
||||
}, div => {
|
||||
this.progressBar = new ProgressBar(div.getHTMLElement());
|
||||
this.toUnbind.push(attachProgressBarStyler(this.progressBar, this.themeService));
|
||||
this.progressBar = this._register(new ProgressBar(div.getHTMLElement()));
|
||||
this._register(attachProgressBarStyler(this.progressBar, this.themeService));
|
||||
this.progressBar.hide();
|
||||
}).getHTMLElement();
|
||||
}
|
||||
@@ -477,7 +480,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
this.notificationService.error(types.isString(error) ? new Error(error) : error);
|
||||
}
|
||||
|
||||
public getProgressIndicator(id: string): IProgressService {
|
||||
getProgressIndicator(id: string): IProgressService {
|
||||
return this.mapProgressServiceToComposite[id];
|
||||
}
|
||||
|
||||
@@ -489,7 +492,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
return [];
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): Dimension[] {
|
||||
layout(dimension: Dimension): Dimension[] {
|
||||
|
||||
// Pass to super
|
||||
const sizes = super.layout(dimension);
|
||||
@@ -503,13 +506,13 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
return sizes;
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
shutdown(): void {
|
||||
this.instantiatedComposites.forEach(i => i.shutdown());
|
||||
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
this.mapCompositeToCompositeContainer = null;
|
||||
this.mapProgressServiceToComposite = null;
|
||||
this.mapActionsBindingToComposite = null;
|
||||
@@ -519,13 +522,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
}
|
||||
|
||||
this.instantiatedComposites = [];
|
||||
|
||||
this.instantiatedCompositeListeners = dispose(this.instantiatedCompositeListeners);
|
||||
|
||||
this.progressBar.dispose();
|
||||
this.toolBar.dispose();
|
||||
|
||||
// Super Dispose
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,146 +6,182 @@
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { illegalArgument } from 'vs/base/common/errors';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ActionBar, IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ActionBar, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { CompositeActionItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionItem, ActivityAction, ICompositeBar, ICompositeBarColors } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Dimension, $, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
|
||||
export interface ICompositeBarOptions {
|
||||
icon: boolean;
|
||||
storageId: string;
|
||||
orientation: ActionsOrientation;
|
||||
composites: { id: string, name: string, order: number }[];
|
||||
colors: ICompositeBarColors;
|
||||
compositeSize: number;
|
||||
overflowActionSize: number;
|
||||
getActivityAction: (compositeId: string) => ActivityAction;
|
||||
getCompositePinnedAction: (compositeId: string) => Action;
|
||||
getOnCompositeClickAction: (compositeId: string) => Action;
|
||||
getContextMenuActions: () => Action[];
|
||||
openComposite: (compositeId: string) => TPromise<any>;
|
||||
getDefaultCompositeId: () => string;
|
||||
hidePart: () => TPromise<any>;
|
||||
}
|
||||
|
||||
interface CompositeState {
|
||||
id: string;
|
||||
pinned: boolean;
|
||||
}
|
||||
|
||||
export class CompositeBar implements ICompositeBar {
|
||||
|
||||
private readonly _onDidContextMenu: Emitter<MouseEvent>;
|
||||
export class CompositeBar extends Widget implements ICompositeBar {
|
||||
|
||||
private dimension: Dimension;
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
private compositeSwitcherBar: ActionBar;
|
||||
private compositeOverflowAction: CompositeOverflowActivityAction;
|
||||
private compositeOverflowActionItem: CompositeOverflowActivityActionItem;
|
||||
|
||||
private compositeIdToActions: { [compositeId: string]: ActivityAction; };
|
||||
private compositeIdToActionItems: { [compositeId: string]: IActionItem; };
|
||||
private compositeIdToActivityStack: { [compositeId: string]: ICompositeActivity[]; };
|
||||
private model: CompositeBarModel;
|
||||
private storedState: ISerializedCompositeBarItem[];
|
||||
private visibleComposites: string[];
|
||||
private compositeSizeInBar: Map<string, number>;
|
||||
|
||||
private initialCompositesStates: CompositeState[];
|
||||
private pinnedComposites: string[];
|
||||
private activeCompositeId: string;
|
||||
private activeUnpinnedCompositeId: string;
|
||||
|
||||
constructor(
|
||||
private options: ICompositeBarOptions,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService
|
||||
) {
|
||||
this.toDispose = [];
|
||||
this.compositeIdToActionItems = Object.create(null);
|
||||
this.compositeIdToActions = Object.create(null);
|
||||
this.compositeIdToActivityStack = Object.create(null);
|
||||
super();
|
||||
|
||||
this.model = new CompositeBarModel(options);
|
||||
this.storedState = this.loadCompositeItemsFromStorage();
|
||||
this.visibleComposites = [];
|
||||
this.compositeSizeInBar = new Map<string, number>();
|
||||
|
||||
this._onDidContextMenu = new Emitter<MouseEvent>();
|
||||
this.initialCompositesStates = this.loadCompositesStates();
|
||||
this.pinnedComposites = this.initialCompositesStates
|
||||
.filter(c => c.pinned)
|
||||
.map(c => c.id)
|
||||
.filter(id => this.options.composites.some(c => c.id === id));
|
||||
}
|
||||
|
||||
public get onDidContextMenu(): Event<MouseEvent> {
|
||||
return this._onDidContextMenu.event;
|
||||
getCompositesFromStorage(): string[] {
|
||||
return this.storedState.map(s => s.id);
|
||||
}
|
||||
|
||||
public addComposite(compositeData: { id: string; name: string, order: number }, activate: boolean): void {
|
||||
if (this.options.composites.filter(c => c.id === compositeData.id).length) {
|
||||
return;
|
||||
}
|
||||
this.options.composites.push(compositeData);
|
||||
create(parent: HTMLElement): HTMLElement {
|
||||
const actionBarDiv = parent.appendChild($('.composite-bar'));
|
||||
this.compositeSwitcherBar = this._register(new ActionBar(actionBarDiv, {
|
||||
actionItemProvider: (action: Action) => {
|
||||
if (action instanceof CompositeOverflowActivityAction) {
|
||||
return this.compositeOverflowActionItem;
|
||||
}
|
||||
const item = this.model.findItem(action.id);
|
||||
return item && this.instantiationService.createInstance(CompositeActionItem, action, item.pinnedAction, this.options.colors, this.options.icon, this);
|
||||
},
|
||||
orientation: this.options.orientation,
|
||||
ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"),
|
||||
animated: false,
|
||||
}));
|
||||
|
||||
const compositeState = this.initialCompositesStates.filter(c => c.id === compositeData.id)[0];
|
||||
if (!compositeState /* new composites are pinned by default */ || compositeState.pinned) {
|
||||
let index;
|
||||
if (compositeState) {
|
||||
index = this.initialCompositesStates.indexOf(compositeState);
|
||||
} else {
|
||||
index = 0;
|
||||
while (index < this.options.composites.length && this.options.composites[index].order < compositeData.order) {
|
||||
index++;
|
||||
// Contextmenu for composites
|
||||
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(e)));
|
||||
|
||||
// Allow to drop at the end to move composites to the end
|
||||
this._register(addDisposableListener(parent, EventType.DROP, (e: DragEvent) => {
|
||||
const draggedCompositeId = CompositeActionItem.getDraggedCompositeId();
|
||||
if (draggedCompositeId) {
|
||||
EventHelper.stop(e, true);
|
||||
CompositeActionItem.clearDraggedComposite();
|
||||
|
||||
const targetItem = this.model.items[this.model.items.length - 1];
|
||||
if (targetItem && targetItem.id !== draggedCompositeId) {
|
||||
this.move(draggedCompositeId, targetItem.id);
|
||||
}
|
||||
}
|
||||
this.pin(compositeData.id, true, index, activate);
|
||||
}
|
||||
}));
|
||||
|
||||
return actionBarDiv;
|
||||
}
|
||||
|
||||
public removeComposite(id: string): void {
|
||||
if (this.options.composites.filter(c => c.id === id).length === 0) {
|
||||
layout(dimension: Dimension): void {
|
||||
this.dimension = dimension;
|
||||
if (dimension.height === 0 || dimension.width === 0) {
|
||||
// Do not layout if not visible. Otherwise the size measurment would be computed wrongly
|
||||
return;
|
||||
}
|
||||
|
||||
this.options.composites = this.options.composites.filter(c => c.id !== id);
|
||||
this.unpin(id);
|
||||
this.pullComposite(id);
|
||||
// Only at the end deactivate composite so the unpin and pull properly finish
|
||||
this.deactivateComposite(id);
|
||||
if (this.compositeSizeInBar.size === 0) {
|
||||
// Compute size of each composite by getting the size from the css renderer
|
||||
// Size is later used for overflow computation
|
||||
this.computeSizes(this.model.items);
|
||||
}
|
||||
|
||||
this.updateCompositeSwitcher();
|
||||
}
|
||||
|
||||
public activateComposite(id: string): void {
|
||||
if (this.compositeIdToActions[id]) {
|
||||
if (this.compositeIdToActions[this.activeCompositeId]) {
|
||||
this.compositeIdToActions[this.activeCompositeId].deactivate();
|
||||
addComposite({ id, name, order }: { id: string; name: string, order: number }): void {
|
||||
const state = this.storedState.filter(s => s.id === id)[0];
|
||||
const pinned = state ? state.pinned : true;
|
||||
let index = order >= 0 ? order : this.model.items.length;
|
||||
|
||||
if (state) {
|
||||
// Find the index by looking its previous item
|
||||
index = 0;
|
||||
for (let i = this.storedState.indexOf(state) - 1; i >= 0; i--) {
|
||||
const previousItemId = this.storedState[i].id;
|
||||
const previousItemIndex = this.model.findIndex(previousItemId);
|
||||
if (previousItemIndex !== -1) {
|
||||
index = previousItemIndex + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.compositeIdToActions[id].activate();
|
||||
}
|
||||
this.activeCompositeId = id;
|
||||
|
||||
const activeUnpinnedCompositeShouldClose = this.activeUnpinnedCompositeId && this.activeUnpinnedCompositeId !== id;
|
||||
const activeUnpinnedCompositeShouldShow = !this.pinnedComposites.some(pid => pid === id);
|
||||
if (activeUnpinnedCompositeShouldShow || activeUnpinnedCompositeShouldClose) {
|
||||
// Add to the model
|
||||
if (this.model.add(id, name, order, index)) {
|
||||
this.computeSizes([this.model.findItem(id)]);
|
||||
if (pinned) {
|
||||
this.pin(id);
|
||||
} else {
|
||||
this.updateCompositeSwitcher();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeComposite(id: string): void {
|
||||
|
||||
// If it pinned, unpin it first
|
||||
if (this.isPinned(id)) {
|
||||
this.unpin(id);
|
||||
}
|
||||
|
||||
// Remove from the model
|
||||
if (this.model.remove(id)) {
|
||||
this.updateCompositeSwitcher();
|
||||
}
|
||||
}
|
||||
|
||||
public deactivateComposite(id: string): void {
|
||||
if (this.compositeIdToActions[id]) {
|
||||
this.compositeIdToActions[id].deactivate();
|
||||
}
|
||||
if (this.activeCompositeId === id) {
|
||||
this.activeCompositeId = undefined;
|
||||
}
|
||||
if (this.activeUnpinnedCompositeId === id) {
|
||||
this.updateCompositeSwitcher();
|
||||
this.activeUnpinnedCompositeId = undefined;
|
||||
activateComposite(id: string): void {
|
||||
const previousActiveItem = this.model.activeItem;
|
||||
if (this.model.activate(id)) {
|
||||
// Update if current composite is neither visible nor pinned
|
||||
// or previous active composite is not pinned
|
||||
if (this.visibleComposites.indexOf(id) === - 1 || !this.model.activeItem.pinned || (previousActiveItem && !previousActiveItem.pinned)) {
|
||||
this.updateCompositeSwitcher();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public showActivity(compositeId: string, badge: IBadge, clazz?: string, priority?: number): IDisposable {
|
||||
deactivateComposite(id: string): void {
|
||||
const previousActiveItem = this.model.activeItem;
|
||||
if (this.model.deactivate()) {
|
||||
if (previousActiveItem && !previousActiveItem.pinned) {
|
||||
this.updateCompositeSwitcher();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showActivity(compositeId: string, badge: IBadge, clazz?: string, priority?: number): IDisposable {
|
||||
if (!badge) {
|
||||
throw illegalArgument('badge');
|
||||
}
|
||||
@@ -155,100 +191,90 @@ export class CompositeBar implements ICompositeBar {
|
||||
}
|
||||
|
||||
const activity: ICompositeActivity = { badge, clazz, priority };
|
||||
const stack = this.compositeIdToActivityStack[compositeId] || (this.compositeIdToActivityStack[compositeId] = []);
|
||||
|
||||
for (let i = 0; i <= stack.length; i++) {
|
||||
if (i === stack.length) {
|
||||
stack.push(activity);
|
||||
break;
|
||||
} else if (stack[i].priority <= priority) {
|
||||
stack.splice(i, 0, activity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateActivity(compositeId);
|
||||
|
||||
return {
|
||||
dispose: () => {
|
||||
const stack = this.compositeIdToActivityStack[compositeId];
|
||||
if (!stack) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idx = stack.indexOf(activity);
|
||||
if (idx < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
stack.splice(idx, 1);
|
||||
if (stack.length === 0) {
|
||||
delete this.compositeIdToActivityStack[compositeId];
|
||||
}
|
||||
|
||||
this.updateActivity(compositeId);
|
||||
}
|
||||
};
|
||||
this.model.addActivity(compositeId, activity);
|
||||
return toDisposable(() => this.model.removeActivity(compositeId, activity));
|
||||
}
|
||||
|
||||
private updateActivity(compositeId: string) {
|
||||
const action = this.compositeIdToActions[compositeId];
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
pin(compositeId: string, open?: boolean): void {
|
||||
if (this.model.setPinned(compositeId, true)) {
|
||||
this.updateCompositeSwitcher();
|
||||
|
||||
const stack = this.compositeIdToActivityStack[compositeId];
|
||||
|
||||
// reset
|
||||
if (!stack || !stack.length) {
|
||||
action.setBadge(undefined);
|
||||
}
|
||||
|
||||
// update
|
||||
else {
|
||||
const [{ badge, clazz }] = stack;
|
||||
action.setBadge(badge);
|
||||
if (clazz) {
|
||||
action.class = clazz;
|
||||
if (open) {
|
||||
this.options.openComposite(compositeId)
|
||||
.done(() => this.activateComposite(compositeId)); // Activate after opening
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public create(parent: HTMLElement): HTMLElement {
|
||||
const actionBarDiv = parent.appendChild($('.composite-bar'));
|
||||
this.compositeSwitcherBar = new ActionBar(actionBarDiv, {
|
||||
actionItemProvider: (action: Action) => action instanceof CompositeOverflowActivityAction ? this.compositeOverflowActionItem : this.compositeIdToActionItems[action.id],
|
||||
orientation: this.options.orientation,
|
||||
ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"),
|
||||
animated: false,
|
||||
});
|
||||
this.toDispose.push(this.compositeSwitcherBar);
|
||||
unpin(compositeId: string): void {
|
||||
if (this.model.setPinned(compositeId, false)) {
|
||||
|
||||
// Contextmenu for composites
|
||||
this.toDispose.push(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => {
|
||||
EventHelper.stop(e, true);
|
||||
this._onDidContextMenu.fire(e);
|
||||
}));
|
||||
this.updateCompositeSwitcher();
|
||||
|
||||
// Allow to drop at the end to move composites to the end
|
||||
this.toDispose.push(addDisposableListener(parent, EventType.DROP, (e: DragEvent) => {
|
||||
const draggedCompositeId = CompositeActionItem.getDraggedCompositeId();
|
||||
if (draggedCompositeId) {
|
||||
EventHelper.stop(e, true);
|
||||
CompositeActionItem.clearDraggedComposite();
|
||||
const defaultCompositeId = this.options.getDefaultCompositeId();
|
||||
|
||||
const targetId = this.pinnedComposites[this.pinnedComposites.length - 1];
|
||||
if (targetId !== draggedCompositeId) {
|
||||
this.move(draggedCompositeId, this.pinnedComposites[this.pinnedComposites.length - 1]);
|
||||
}
|
||||
// Case: composite is not the active one or the active one is a different one
|
||||
// Solv: we do nothing
|
||||
if (!this.model.activeItem || this.model.activeItem.id !== compositeId) {
|
||||
return;
|
||||
}
|
||||
}));
|
||||
|
||||
return actionBarDiv;
|
||||
// Deactivate itself
|
||||
this.deactivateComposite(compositeId);
|
||||
|
||||
// Case: composite is not the default composite and default composite is still showing
|
||||
// Solv: we open the default composite
|
||||
if (defaultCompositeId !== compositeId && this.isPinned(defaultCompositeId)) {
|
||||
this.options.openComposite(defaultCompositeId);
|
||||
}
|
||||
|
||||
// Case: we closed the last visible composite
|
||||
// Solv: we hide the part
|
||||
else if (this.visibleComposites.length === 1) {
|
||||
this.options.hidePart();
|
||||
}
|
||||
|
||||
// Case: we closed the default composite
|
||||
// Solv: we open the next visible composite from top
|
||||
else {
|
||||
this.options.openComposite(this.visibleComposites.filter(cid => cid !== compositeId)[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getAction(compositeId): ActivityAction {
|
||||
return this.compositeIdToActions[compositeId];
|
||||
isPinned(compositeId: string): boolean {
|
||||
const item = this.model.findItem(compositeId);
|
||||
return item && item.pinned;
|
||||
}
|
||||
|
||||
move(compositeId: string, toCompositeId: string): void {
|
||||
if (this.model.move(compositeId, toCompositeId)) {
|
||||
// timeout helps to prevent artifacts from showing up
|
||||
setTimeout(() => this.updateCompositeSwitcher(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
getAction(compositeId): ActivityAction {
|
||||
const item = this.model.findItem(compositeId);
|
||||
return item && item.activityAction;
|
||||
}
|
||||
|
||||
private computeSizes(items: ICompositeBarItem[]): void {
|
||||
const size = this.options.compositeSize;
|
||||
if (size) {
|
||||
items.forEach(composite => this.compositeSizeInBar.set(composite.id, size));
|
||||
} else {
|
||||
if (this.dimension && this.dimension.height !== 0 && this.dimension.width !== 0) {
|
||||
// Compute sizes only if visible. Otherwise the size measurment would be computed wrongly.
|
||||
const currentItemsLength = this.compositeSwitcherBar.items.length;
|
||||
this.compositeSwitcherBar.push(items.map(composite => composite.activityAction));
|
||||
items.map((composite, index) => this.compositeSizeInBar.set(composite.id, this.options.orientation === ActionsOrientation.VERTICAL
|
||||
? this.compositeSwitcherBar.getHeight(currentItemsLength + index)
|
||||
: this.compositeSwitcherBar.getWidth(currentItemsLength + index)
|
||||
));
|
||||
items.forEach(() => this.compositeSwitcherBar.pull(this.compositeSwitcherBar.items.length - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateCompositeSwitcher(): void {
|
||||
@@ -256,15 +282,10 @@ export class CompositeBar implements ICompositeBar {
|
||||
return; // We have not been rendered yet so there is nothing to update.
|
||||
}
|
||||
|
||||
let compositesToShow = this.pinnedComposites.slice(0); // never modify original array
|
||||
|
||||
// Always show the active composite even if it is marked to be hidden
|
||||
if (this.activeCompositeId && !compositesToShow.some(id => id === this.activeCompositeId)) {
|
||||
this.activeUnpinnedCompositeId = this.activeCompositeId;
|
||||
compositesToShow = compositesToShow.concat(this.activeUnpinnedCompositeId);
|
||||
} else {
|
||||
this.activeUnpinnedCompositeId = void 0;
|
||||
}
|
||||
let compositesToShow = this.model.items.filter(item =>
|
||||
item.pinned
|
||||
|| (this.model.activeItem && this.model.activeItem.id === item.id) /* Show the active composite even if it is not pinned */
|
||||
).map(item => item.id);
|
||||
|
||||
// Ensure we are not showing more composites than we have height for
|
||||
let overflows = false;
|
||||
@@ -288,19 +309,20 @@ export class CompositeBar implements ICompositeBar {
|
||||
if (size > limit) {
|
||||
size -= this.compositeSizeInBar.get(compositesToShow.pop());
|
||||
}
|
||||
|
||||
// We always try show the active composite
|
||||
if (this.activeCompositeId && compositesToShow.length && compositesToShow.indexOf(this.activeCompositeId) === -1) {
|
||||
if (this.model.activeItem && compositesToShow.every(compositeId => compositeId !== this.model.activeItem.id)) {
|
||||
const removedComposite = compositesToShow.pop();
|
||||
size = size - this.compositeSizeInBar.get(removedComposite) + this.compositeSizeInBar.get(this.activeCompositeId);
|
||||
compositesToShow.push(this.activeCompositeId);
|
||||
size = size - this.compositeSizeInBar.get(removedComposite) + this.compositeSizeInBar.get(this.model.activeItem.id);
|
||||
compositesToShow.push(this.model.activeItem.id);
|
||||
}
|
||||
|
||||
// The active composite might have bigger size than the removed composite, check for overflow again
|
||||
if (size > limit) {
|
||||
compositesToShow.length ? compositesToShow.splice(compositesToShow.length - 2, 1) : compositesToShow.pop();
|
||||
}
|
||||
|
||||
const visibleComposites = Object.keys(this.compositeIdToActions);
|
||||
const visibleCompositesChange = !arrays.equals(compositesToShow, visibleComposites);
|
||||
const visibleCompositesChange = !arrays.equals(compositesToShow, this.visibleComposites);
|
||||
|
||||
// Pull out overflow action if there is a composite change so that we can add it to the end later
|
||||
if (this.compositeOverflowAction && visibleCompositesChange) {
|
||||
@@ -313,37 +335,35 @@ export class CompositeBar implements ICompositeBar {
|
||||
this.compositeOverflowActionItem = null;
|
||||
}
|
||||
|
||||
// Pull out composites that overflow, got hidden or changed position
|
||||
visibleComposites.forEach((compositeId, index) => {
|
||||
if (compositesToShow.indexOf(compositeId) !== index) {
|
||||
this.pullComposite(compositeId);
|
||||
// Pull out composites that overflow or got hidden
|
||||
const compositesToRemove: number[] = [];
|
||||
this.visibleComposites.forEach((compositeId, index) => {
|
||||
if (compositesToShow.indexOf(compositeId) === -1) {
|
||||
compositesToRemove.push(index);
|
||||
}
|
||||
});
|
||||
compositesToRemove.reverse().forEach(index => {
|
||||
const actionItem = this.compositeSwitcherBar.items[index];
|
||||
this.compositeSwitcherBar.pull(index);
|
||||
actionItem.dispose();
|
||||
this.visibleComposites.splice(index, 1);
|
||||
});
|
||||
|
||||
// Built actions for composites to show
|
||||
const newCompositesToShow = compositesToShow
|
||||
.filter(compositeId => !this.compositeIdToActions[compositeId])
|
||||
.map(compositeId => this.toAction(compositeId));
|
||||
|
||||
// Update when we have new composites to show
|
||||
if (newCompositesToShow.length) {
|
||||
|
||||
// Add to composite switcher
|
||||
this.compositeSwitcherBar.push(newCompositesToShow, { label: true, icon: this.options.icon });
|
||||
|
||||
// Make sure to activate the active one
|
||||
if (this.activeCompositeId) {
|
||||
const activeCompositeEntry = this.compositeIdToActions[this.activeCompositeId];
|
||||
if (activeCompositeEntry) {
|
||||
activeCompositeEntry.activate();
|
||||
// Update the positions of the composites
|
||||
compositesToShow.forEach((compositeId, newIndex) => {
|
||||
const currentIndex = this.visibleComposites.indexOf(compositeId);
|
||||
if (newIndex !== currentIndex) {
|
||||
if (currentIndex !== -1) {
|
||||
const actionItem = this.compositeSwitcherBar.items[currentIndex];
|
||||
this.compositeSwitcherBar.pull(currentIndex);
|
||||
actionItem.dispose();
|
||||
this.visibleComposites.splice(currentIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure to restore activity
|
||||
Object.keys(this.compositeIdToActions).forEach(compositeId => {
|
||||
this.updateActivity(compositeId);
|
||||
});
|
||||
}
|
||||
this.compositeSwitcherBar.push(this.model.findItem(compositeId).activityAction, { label: true, icon: this.options.icon, index: newIndex });
|
||||
this.visibleComposites.splice(newIndex, 0, compositeId);
|
||||
}
|
||||
});
|
||||
|
||||
// Add overflow action as needed
|
||||
if ((visibleCompositesChange && overflows) || this.compositeSwitcherBar.length() === 0) {
|
||||
@@ -352,210 +372,254 @@ export class CompositeBar implements ICompositeBar {
|
||||
CompositeOverflowActivityActionItem,
|
||||
this.compositeOverflowAction,
|
||||
() => this.getOverflowingComposites(),
|
||||
() => this.activeCompositeId,
|
||||
(compositeId: string) => this.compositeIdToActivityStack[compositeId] && this.compositeIdToActivityStack[compositeId][0].badge,
|
||||
() => this.model.activeItem ? this.model.activeItem.id : void 0,
|
||||
(compositeId: string) => {
|
||||
const item = this.model.findItem(compositeId);
|
||||
return item && item.activity[0] && item.activity[0].badge;
|
||||
},
|
||||
this.options.getOnCompositeClickAction,
|
||||
this.options.colors
|
||||
);
|
||||
|
||||
this.compositeSwitcherBar.push(this.compositeOverflowAction, { label: false, icon: true });
|
||||
}
|
||||
|
||||
// Persist
|
||||
this.saveCompositeItems();
|
||||
}
|
||||
|
||||
private getOverflowingComposites(): { id: string, name: string }[] {
|
||||
let overflowingIds = this.pinnedComposites;
|
||||
if (this.activeUnpinnedCompositeId) {
|
||||
overflowingIds = overflowingIds.concat(this.activeUnpinnedCompositeId);
|
||||
}
|
||||
const visibleComposites = Object.keys(this.compositeIdToActions);
|
||||
let overflowingIds = this.model.items.filter(item => item.pinned).map(item => item.id);
|
||||
|
||||
overflowingIds = overflowingIds.filter(compositeId => visibleComposites.indexOf(compositeId) === -1);
|
||||
return this.options.composites.filter(c => overflowingIds.indexOf(c.id) !== -1);
|
||||
// Show the active composite even if it is not pinned
|
||||
if (this.model.activeItem && !this.model.activeItem.pinned) {
|
||||
overflowingIds.push(this.model.activeItem.id);
|
||||
}
|
||||
|
||||
overflowingIds = overflowingIds.filter(compositeId => this.visibleComposites.indexOf(compositeId) === -1);
|
||||
return this.model.items.filter(c => overflowingIds.indexOf(c.id) !== -1);
|
||||
}
|
||||
|
||||
private getVisibleComposites(): string[] {
|
||||
return Object.keys(this.compositeIdToActions);
|
||||
}
|
||||
|
||||
private pullComposite(compositeId: string): void {
|
||||
const index = Object.keys(this.compositeIdToActions).indexOf(compositeId);
|
||||
if (index >= 0) {
|
||||
this.compositeSwitcherBar.pull(index);
|
||||
|
||||
const action = this.compositeIdToActions[compositeId];
|
||||
action.dispose();
|
||||
delete this.compositeIdToActions[compositeId];
|
||||
|
||||
const actionItem = this.compositeIdToActionItems[action.id];
|
||||
actionItem.dispose();
|
||||
delete this.compositeIdToActionItems[action.id];
|
||||
private showContextMenu(e: MouseEvent): void {
|
||||
EventHelper.stop(e, true);
|
||||
const event = new StandardMouseEvent(e);
|
||||
const actions: IAction[] = this.model.items
|
||||
.map(({ id, name, activityAction }) => (<IAction>{
|
||||
id,
|
||||
label: name,
|
||||
checked: this.isPinned(id),
|
||||
enabled: activityAction.enabled,
|
||||
run: () => {
|
||||
if (this.isPinned(id)) {
|
||||
this.unpin(id);
|
||||
} else {
|
||||
this.pin(id, true);
|
||||
}
|
||||
}
|
||||
}));
|
||||
const otherActions = this.options.getContextMenuActions();
|
||||
if (otherActions.length) {
|
||||
actions.push(new Separator());
|
||||
actions.push(...otherActions);
|
||||
}
|
||||
}
|
||||
|
||||
private toAction(compositeId: string): ActivityAction {
|
||||
if (this.compositeIdToActions[compositeId]) {
|
||||
return this.compositeIdToActions[compositeId];
|
||||
}
|
||||
|
||||
const compositeActivityAction = this.options.getActivityAction(compositeId);
|
||||
const pinnedAction = this.options.getCompositePinnedAction(compositeId);
|
||||
this.compositeIdToActionItems[compositeId] = this.instantiationService.createInstance(CompositeActionItem, compositeActivityAction, pinnedAction, this.options.colors, this.options.icon, this);
|
||||
this.compositeIdToActions[compositeId] = compositeActivityAction;
|
||||
|
||||
return compositeActivityAction;
|
||||
}
|
||||
|
||||
public unpin(compositeId: string): void {
|
||||
if (!this.isPinned(compositeId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultCompositeId = this.options.getDefaultCompositeId();
|
||||
const visibleComposites = this.getVisibleComposites();
|
||||
|
||||
let unpinPromise: TPromise<any>;
|
||||
|
||||
// remove from pinned
|
||||
const index = this.pinnedComposites.indexOf(compositeId);
|
||||
this.pinnedComposites.splice(index, 1);
|
||||
|
||||
// Case: composite is not the active one or the active one is a different one
|
||||
// Solv: we do nothing
|
||||
if (!this.activeCompositeId || this.activeCompositeId !== compositeId) {
|
||||
unpinPromise = TPromise.as(null);
|
||||
}
|
||||
|
||||
// Case: composite is not the default composite and default composite is still showing
|
||||
// Solv: we open the default composite
|
||||
else if (defaultCompositeId !== compositeId && this.isPinned(defaultCompositeId)) {
|
||||
unpinPromise = this.options.openComposite(defaultCompositeId);
|
||||
}
|
||||
|
||||
// Case: we closed the last visible composite
|
||||
// Solv: we hide the part
|
||||
else if (visibleComposites.length === 1) {
|
||||
unpinPromise = this.options.hidePart();
|
||||
}
|
||||
|
||||
// Case: we closed the default composite
|
||||
// Solv: we open the next visible composite from top
|
||||
else {
|
||||
unpinPromise = this.options.openComposite(visibleComposites.filter(cid => cid !== compositeId)[0]);
|
||||
}
|
||||
|
||||
unpinPromise.then(() => {
|
||||
this.updateCompositeSwitcher();
|
||||
});
|
||||
|
||||
// Persist
|
||||
this.saveCompositesStates();
|
||||
}
|
||||
|
||||
public isPinned(compositeId: string): boolean {
|
||||
return this.pinnedComposites.indexOf(compositeId) >= 0;
|
||||
}
|
||||
|
||||
public pin(compositeId: string, update = true, index = this.pinnedComposites.length, activate: boolean = true): void {
|
||||
if (this.isPinned(compositeId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activatePromise = activate ? this.options.openComposite(compositeId) : TPromise.as(null);
|
||||
activatePromise.then(() => {
|
||||
this.pinnedComposites.splice(index, 0, compositeId);
|
||||
this.pinnedComposites = arrays.distinct(this.pinnedComposites);
|
||||
|
||||
if (update) {
|
||||
this.updateCompositeSwitcher();
|
||||
}
|
||||
|
||||
// Persist
|
||||
this.saveCompositesStates();
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => { return { x: event.posx, y: event.posy }; },
|
||||
getActions: () => TPromise.as(actions),
|
||||
});
|
||||
}
|
||||
|
||||
public move(compositeId: string, toCompositeId: string): void {
|
||||
// Make sure both composites are known to this composite bar
|
||||
if (this.options.composites.filter(c => c.id === compositeId || c.id === toCompositeId).length !== 2) {
|
||||
return;
|
||||
}
|
||||
// Make sure a moved composite gets pinned
|
||||
if (!this.isPinned(compositeId)) {
|
||||
this.pin(compositeId, false /* defer update, we take care of it */);
|
||||
}
|
||||
|
||||
const fromIndex = this.pinnedComposites.indexOf(compositeId);
|
||||
const toIndex = this.pinnedComposites.indexOf(toCompositeId);
|
||||
|
||||
this.pinnedComposites.splice(fromIndex, 1);
|
||||
this.pinnedComposites.splice(toIndex, 0, compositeId);
|
||||
|
||||
// Clear composites that are impacted by the move
|
||||
const visibleComposites = Object.keys(this.compositeIdToActions);
|
||||
for (let i = Math.min(fromIndex, toIndex); i < visibleComposites.length; i++) {
|
||||
this.pullComposite(visibleComposites[i]);
|
||||
}
|
||||
|
||||
// timeout helps to prevent artifacts from showing up
|
||||
setTimeout(() => {
|
||||
this.updateCompositeSwitcher();
|
||||
}, 0);
|
||||
|
||||
// Persist
|
||||
this.saveCompositesStates();
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
this.dimension = dimension;
|
||||
if (dimension.height === 0 || dimension.width === 0) {
|
||||
// Do not layout if not visible. Otherwise the size measurment would be computed wrongly
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.compositeSizeInBar.size === 0) {
|
||||
// Compute size of each composite by getting the size from the css renderer
|
||||
// Size is later used for overflow computation
|
||||
this.compositeSwitcherBar.clear();
|
||||
this.compositeSwitcherBar.push(this.options.composites.map(c => this.options.getActivityAction(c.id)));
|
||||
this.options.composites.map((c, index) => this.compositeSizeInBar.set(c.id, this.options.orientation === ActionsOrientation.VERTICAL
|
||||
? this.compositeSwitcherBar.getHeight(index)
|
||||
: this.compositeSwitcherBar.getWidth(index)
|
||||
));
|
||||
this.compositeSwitcherBar.clear();
|
||||
}
|
||||
this.updateCompositeSwitcher();
|
||||
}
|
||||
|
||||
private loadCompositesStates(): CompositeState[] {
|
||||
const storedStates = <Array<string | CompositeState>>JSON.parse(this.storageService.get(this.options.storageId, StorageScope.GLOBAL, '[]'));
|
||||
const isOldData = storedStates && storedStates.length && typeof storedStates[0] === 'string';
|
||||
const compositeStates = <CompositeState[]>storedStates.map(c =>
|
||||
private loadCompositeItemsFromStorage(): ISerializedCompositeBarItem[] {
|
||||
const storedStates = <Array<string | ISerializedCompositeBarItem>>JSON.parse(this.storageService.get(this.options.storageId, StorageScope.GLOBAL, '[]'));
|
||||
const compositeStates = <ISerializedCompositeBarItem[]>storedStates.map(c =>
|
||||
typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true } : c);
|
||||
|
||||
if (!isOldData) { /* Add new composites only if it is new data */
|
||||
const newComposites = this.options.composites.filter(c => compositeStates.every(s => s.id !== c.id));
|
||||
newComposites.sort((c1, c2) => c1.order < c2.order ? -1 : 1);
|
||||
newComposites.forEach(c => compositeStates.push({ id: c.id, pinned: true /* new composites are pinned by default */ }));
|
||||
}
|
||||
|
||||
return compositeStates;
|
||||
}
|
||||
|
||||
private saveCompositesStates(): void {
|
||||
const toSave = this.pinnedComposites.map(id => (<CompositeState>{ id, pinned: true }));
|
||||
for (const composite of this.options.composites) {
|
||||
if (this.pinnedComposites.indexOf(composite.id) === -1) { // Unpinned composites
|
||||
toSave.push({ id: composite.id, pinned: false });
|
||||
}
|
||||
}
|
||||
this.storageService.store(this.options.storageId, JSON.stringify(toSave), StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.saveCompositesStates();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
private saveCompositeItems(): void {
|
||||
this.storedState = this.model.toJSON();
|
||||
this.storageService.store(this.options.storageId, JSON.stringify(this.storedState), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
interface ISerializedCompositeBarItem {
|
||||
id: string;
|
||||
pinned: boolean;
|
||||
order: number;
|
||||
}
|
||||
|
||||
interface ICompositeBarItem extends ISerializedCompositeBarItem {
|
||||
name: string;
|
||||
activityAction: ActivityAction;
|
||||
pinnedAction: Action;
|
||||
activity: ICompositeActivity[];
|
||||
}
|
||||
|
||||
class CompositeBarModel {
|
||||
|
||||
readonly items: ICompositeBarItem[] = [];
|
||||
activeItem: ICompositeBarItem;
|
||||
|
||||
constructor(private options: ICompositeBarOptions) { }
|
||||
|
||||
private createCompositeBarItem(id: string, name: string, order: number, pinned: boolean): ICompositeBarItem {
|
||||
const options = this.options;
|
||||
return {
|
||||
id, name, pinned, order, activity: [],
|
||||
get activityAction() {
|
||||
return options.getActivityAction(id);
|
||||
},
|
||||
get pinnedAction() {
|
||||
return options.getCompositePinnedAction(id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
add(id: string, name: string, order: number, index: number): boolean {
|
||||
const item = this.findItem(id);
|
||||
if (item) {
|
||||
item.order = order;
|
||||
item.name = name;
|
||||
return false;
|
||||
} else {
|
||||
if (index === void 0) {
|
||||
index = 0;
|
||||
while (index < this.items.length && this.items[index].order < order) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
this.items.splice(index, 0, this.createCompositeBarItem(id, name, order, false));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
remove(id: string): boolean {
|
||||
for (let index = 0; index < this.items.length; index++) {
|
||||
if (this.items[index].id === id) {
|
||||
this.items.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
move(compositeId: string, toCompositeId: string): boolean {
|
||||
|
||||
const fromIndex = this.findIndex(compositeId);
|
||||
const toIndex = this.findIndex(toCompositeId);
|
||||
|
||||
// Make sure both items are known to the model
|
||||
if (fromIndex === -1 || toIndex === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sourceItem = this.items.splice(fromIndex, 1)[0];
|
||||
this.items.splice(toIndex, 0, sourceItem);
|
||||
|
||||
// Make sure a moved composite gets pinned
|
||||
sourceItem.pinned = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
setPinned(id: string, pinned: boolean): boolean {
|
||||
for (let index = 0; index < this.items.length; index++) {
|
||||
const item = this.items[index];
|
||||
if (item.id === id) {
|
||||
if (item.pinned !== pinned) {
|
||||
item.pinned = pinned;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
addActivity(id: string, activity: ICompositeActivity): boolean {
|
||||
const item = this.findItem(id);
|
||||
if (item) {
|
||||
const stack = item.activity;
|
||||
for (let i = 0; i <= stack.length; i++) {
|
||||
if (i === stack.length) {
|
||||
stack.push(activity);
|
||||
break;
|
||||
} else if (stack[i].priority <= activity.priority) {
|
||||
stack.splice(i, 0, activity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.updateActivity(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
removeActivity(id: string, activity: ICompositeActivity): boolean {
|
||||
const item = this.findItem(id);
|
||||
if (item) {
|
||||
const index = item.activity.indexOf(activity);
|
||||
if (index !== -1) {
|
||||
item.activity.splice(index, 1);
|
||||
this.updateActivity(id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
updateActivity(id: string): void {
|
||||
const item = this.findItem(id);
|
||||
if (item) {
|
||||
if (item.activity.length) {
|
||||
const [{ badge, clazz }] = item.activity;
|
||||
item.activityAction.setBadge(badge, clazz);
|
||||
}
|
||||
else {
|
||||
item.activityAction.setBadge(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activate(id: string): boolean {
|
||||
if (!this.activeItem || this.activeItem.id !== id) {
|
||||
if (this.activeItem) {
|
||||
this.deactivate();
|
||||
}
|
||||
for (let index = 0; index < this.items.length; index++) {
|
||||
const item = this.items[index];
|
||||
if (item.id === id) {
|
||||
this.activeItem = item;
|
||||
this.activeItem.activityAction.activate();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
deactivate(): boolean {
|
||||
if (this.activeItem) {
|
||||
this.activeItem.activityAction.deactivate();
|
||||
this.activeItem = void 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
findItem(id: string): ICompositeBarItem {
|
||||
return this.items.filter(item => item.id === id)[0];
|
||||
}
|
||||
|
||||
findIndex(id: string): number {
|
||||
for (let index = 0; index < this.items.length; index++) {
|
||||
if (this.items[index].id === id) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
toJSON(): ISerializedCompositeBarItem[] {
|
||||
return this.items.map(({ id, pinned, order }) => ({ id, pinned, order }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as dom from 'vs/base/browser/dom';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { BaseActionItem, IBaseActionItemOptions, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { dispose, IDisposable, empty, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { dispose, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { TextBadge, NumberBadge, IBadge, IconBadge, ProgressBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
@@ -52,9 +52,15 @@ export interface ICompositeBar {
|
||||
}
|
||||
|
||||
export class ActivityAction extends Action {
|
||||
|
||||
private _onDidChangeActivity = new Emitter<this>();
|
||||
get onDidChangeActivity(): Event<this> { return this._onDidChangeActivity.event; }
|
||||
|
||||
private _onDidChangeBadge = new Emitter<this>();
|
||||
get onDidChangeBadge(): Event<this> { return this._onDidChangeBadge.event; }
|
||||
|
||||
private badge: IBadge;
|
||||
private clazz: string | undefined;
|
||||
private _onDidChangeBadge = new Emitter<this>();
|
||||
|
||||
constructor(private _activity: IActivity) {
|
||||
super(_activity.id, _activity.name, _activity.cssClass);
|
||||
@@ -62,39 +68,47 @@ export class ActivityAction extends Action {
|
||||
this.badge = null;
|
||||
}
|
||||
|
||||
public get activity(): IActivity {
|
||||
get activity(): IActivity {
|
||||
return this._activity;
|
||||
}
|
||||
|
||||
public get onDidChangeBadge(): Event<this> {
|
||||
return this._onDidChangeBadge.event;
|
||||
set activity(activity: IActivity) {
|
||||
this._activity = activity;
|
||||
this._onDidChangeActivity.fire(this);
|
||||
}
|
||||
|
||||
public activate(): void {
|
||||
activate(): void {
|
||||
if (!this.checked) {
|
||||
this._setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
public deactivate(): void {
|
||||
deactivate(): void {
|
||||
if (this.checked) {
|
||||
this._setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
public getBadge(): IBadge {
|
||||
getBadge(): IBadge {
|
||||
return this.badge;
|
||||
}
|
||||
|
||||
public getClass(): string | undefined {
|
||||
getClass(): string | undefined {
|
||||
return this.clazz;
|
||||
}
|
||||
|
||||
public setBadge(badge: IBadge, clazz?: string): void {
|
||||
setBadge(badge: IBadge, clazz?: string): void {
|
||||
this.badge = badge;
|
||||
this.clazz = clazz;
|
||||
this._onDidChangeBadge.fire(this);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._onDidChangeActivity.dispose();
|
||||
this._onDidChangeBadge.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export interface ICompositeBarColors {
|
||||
@@ -116,7 +130,7 @@ export class ActivityActionItem extends BaseActionItem {
|
||||
protected options: IActivityActionItemOptions;
|
||||
|
||||
private $badgeContent: Builder;
|
||||
private badgeDisposable: IDisposable = empty;
|
||||
private badgeDisposable: IDisposable = Disposable.None;
|
||||
private mouseUpTimeout: number;
|
||||
|
||||
constructor(
|
||||
@@ -127,7 +141,8 @@ export class ActivityActionItem extends BaseActionItem {
|
||||
super(null, action, options);
|
||||
|
||||
this.themeService.onThemeChange(this.onThemeChange, this, this._callOnDispose);
|
||||
action.onDidChangeBadge(this.handleBadgeChangeEvenet, this, this._callOnDispose);
|
||||
action.onDidChangeActivity(this.updateActivity, this, this._callOnDispose);
|
||||
action.onDidChangeBadge(this.updateBadge, this, this._callOnDispose);
|
||||
}
|
||||
|
||||
protected get activity(): IActivity {
|
||||
@@ -159,14 +174,13 @@ export class ActivityActionItem extends BaseActionItem {
|
||||
}
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
render(container: HTMLElement): void {
|
||||
super.render(container);
|
||||
|
||||
// Make the container tab-able for keyboard navigation
|
||||
this.$container = $(container).attr({
|
||||
tabIndex: '0',
|
||||
role: 'button',
|
||||
title: this.activity.name
|
||||
role: 'button'
|
||||
});
|
||||
|
||||
// Try hard to prevent keyboard only focus feedback when using mouse
|
||||
@@ -186,19 +200,13 @@ export class ActivityActionItem extends BaseActionItem {
|
||||
|
||||
// Label
|
||||
this.$label = $('a.action-label').appendTo(this.builder);
|
||||
if (this.activity.cssClass) {
|
||||
this.$label.addClass(this.activity.cssClass);
|
||||
}
|
||||
if (!this.options.icon) {
|
||||
this.$label.text(this.getAction().label);
|
||||
}
|
||||
|
||||
this.$badge = this.builder.clone().div({ 'class': 'badge' }, badge => {
|
||||
this.$badgeContent = badge.div({ 'class': 'badge-content' });
|
||||
});
|
||||
|
||||
this.$badge.hide();
|
||||
|
||||
this.updateActivity();
|
||||
this.updateStyles();
|
||||
}
|
||||
|
||||
@@ -206,13 +214,23 @@ export class ActivityActionItem extends BaseActionItem {
|
||||
this.updateStyles();
|
||||
}
|
||||
|
||||
protected updateBadge(badge: IBadge, clazz?: string): void {
|
||||
if (!this.$badge || !this.$badgeContent) {
|
||||
protected updateActivity(): void {
|
||||
this.updateLabel();
|
||||
this.updateTitle(this.activity.name);
|
||||
this.updateBadge();
|
||||
}
|
||||
|
||||
protected updateBadge(): void {
|
||||
const action = this.getAction();
|
||||
if (!this.$badge || !this.$badgeContent || !(action instanceof ActivityAction)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const badge = action.getBadge();
|
||||
const clazz = action.getClass();
|
||||
|
||||
this.badgeDisposable.dispose();
|
||||
this.badgeDisposable = empty;
|
||||
this.badgeDisposable = Disposable.None;
|
||||
|
||||
this.$badgeContent.empty();
|
||||
this.$badge.hide();
|
||||
@@ -266,7 +284,19 @@ export class ActivityActionItem extends BaseActionItem {
|
||||
} else {
|
||||
title = this.activity.name;
|
||||
}
|
||||
this.updateTitle(title);
|
||||
}
|
||||
|
||||
private updateLabel(): void {
|
||||
if (this.activity.cssClass) {
|
||||
this.$label.addClass(this.activity.cssClass);
|
||||
}
|
||||
if (!this.options.icon) {
|
||||
this.$label.text(this.getAction().label);
|
||||
}
|
||||
}
|
||||
|
||||
private updateTitle(title: string): void {
|
||||
[this.$label, this.$badge, this.$container].forEach(b => {
|
||||
if (b) {
|
||||
b.attr('aria-label', title);
|
||||
@@ -275,14 +305,7 @@ export class ActivityActionItem extends BaseActionItem {
|
||||
});
|
||||
}
|
||||
|
||||
private handleBadgeChangeEvenet(): void {
|
||||
const action = this.getAction();
|
||||
if (action instanceof ActivityAction) {
|
||||
this.updateBadge(action.getBadge(), action.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
if (this.mouseUpTimeout) {
|
||||
@@ -305,7 +328,7 @@ export class CompositeOverflowActivityAction extends ActivityAction {
|
||||
});
|
||||
}
|
||||
|
||||
public run(event: any): TPromise<any> {
|
||||
run(event: any): TPromise<any> {
|
||||
this.showMenu();
|
||||
|
||||
return TPromise.as(true);
|
||||
@@ -328,7 +351,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem {
|
||||
super(action, { icon: true, colors }, themeService);
|
||||
}
|
||||
|
||||
public showMenu(): void {
|
||||
showMenu(): void {
|
||||
if (this.actions) {
|
||||
dispose(this.actions);
|
||||
}
|
||||
@@ -365,7 +388,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem {
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
this.actions = dispose(this.actions);
|
||||
@@ -380,7 +403,7 @@ class ManageExtensionAction extends Action {
|
||||
super('activitybar.manage.extension', nls.localize('manageExtension', "Manage Extension"));
|
||||
}
|
||||
|
||||
public run(id: string): TPromise<any> {
|
||||
run(id: string): TPromise<any> {
|
||||
return this.commandService.executeCommand('_extensions.manage', id);
|
||||
}
|
||||
}
|
||||
@@ -411,6 +434,8 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
if (!CompositeActionItem.manageExtensionAction) {
|
||||
CompositeActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction);
|
||||
}
|
||||
|
||||
compositeActivityAction.onDidChangeActivity(() => { this.compositeActivity = null; this.updateActivity(); }, this, this._callOnDispose);
|
||||
}
|
||||
|
||||
protected get activity(): IActivity {
|
||||
@@ -442,9 +467,12 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
return null;
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
render(container: HTMLElement): void {
|
||||
super.render(container);
|
||||
|
||||
this._updateChecked();
|
||||
this._updateEnabled();
|
||||
|
||||
this.$container.on('contextmenu', e => {
|
||||
dom.EventHelper.stop(e, true);
|
||||
|
||||
@@ -524,7 +552,7 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : null;
|
||||
}
|
||||
|
||||
public static getDraggedCompositeId(): string {
|
||||
static getDraggedCompositeId(): string {
|
||||
return CompositeActionItem.draggedCompositeId;
|
||||
}
|
||||
|
||||
@@ -532,7 +560,7 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
CompositeActionItem.draggedCompositeId = compositeId;
|
||||
}
|
||||
|
||||
public static clearDraggedComposite(): void {
|
||||
static clearDraggedComposite(): void {
|
||||
CompositeActionItem.draggedCompositeId = void 0;
|
||||
}
|
||||
|
||||
@@ -558,24 +586,28 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
});
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
focus(): void {
|
||||
this.$container.domFocus();
|
||||
}
|
||||
|
||||
protected _updateClass(): void {
|
||||
if (this.cssClass) {
|
||||
this.$badge.removeClass(this.cssClass);
|
||||
this.$label.removeClass(this.cssClass);
|
||||
}
|
||||
|
||||
this.cssClass = this.getAction().class;
|
||||
this.$badge.addClass(this.cssClass);
|
||||
if (this.cssClass) {
|
||||
this.$label.addClass(this.cssClass);
|
||||
}
|
||||
}
|
||||
|
||||
protected _updateChecked(): void {
|
||||
if (this.getAction().checked) {
|
||||
this.$container.addClass('checked');
|
||||
this.$container.attr('aria-label', nls.localize('compositeActive', "{0} active", this.$container.getHTMLElement().title));
|
||||
} else {
|
||||
this.$container.removeClass('checked');
|
||||
this.$container.attr('aria-label', this.$container.getHTMLElement().title);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,7 +619,7 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
CompositeActionItem.clearDraggedComposite();
|
||||
@@ -607,7 +639,7 @@ export class ToggleCompositePinnedAction extends Action {
|
||||
this.checked = this.activity && this.compositeBar.isPinned(this.activity.id);
|
||||
}
|
||||
|
||||
public run(context: string): TPromise<any> {
|
||||
run(context: string): TPromise<any> {
|
||||
const id = this.activity ? this.activity.id : context;
|
||||
|
||||
if (this.compositeBar.isPinned(id)) {
|
||||
|
||||
@@ -6,16 +6,24 @@
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Panel } from 'vs/workbench/browser/panel';
|
||||
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { IEditor, Position } from 'vs/platform/editor/common/editor';
|
||||
import { EditorInput, EditorOptions, IEditor, GroupIdentifier, IEditorMemento } from 'vs/workbench/common/editor';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { once, Event } from 'vs/base/common/event';
|
||||
import { isEmptyObject } from 'vs/base/common/types';
|
||||
import { DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
|
||||
|
||||
/**
|
||||
* The base class of editors in the workbench. Editors register themselves for specific editor inputs.
|
||||
* Editors are layed out in the editor part of the workbench. Only one editor can be open at a time.
|
||||
* Each editor has a minimized representation that is good enough to provide some information about the
|
||||
* state of the editor data.
|
||||
* Editors are layed out in the editor part of the workbench in editor groups. Multiple editors can be
|
||||
* open at the same time. Each editor has a minimized representation that is good enough to provide some
|
||||
* information about the state of the editor data.
|
||||
*
|
||||
* The workbench will keep an editor alive after it has been created and show/hide it based on
|
||||
* user interaction. The lifecycle of a editor goes in the order create(), setVisible(true|false),
|
||||
* layout(), setInput(), focus(), dispose(). During use of the workbench, a editor will often receive a
|
||||
@@ -24,30 +32,53 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
* This class is only intended to be subclassed and not instantiated.
|
||||
*/
|
||||
export abstract class BaseEditor extends Panel implements IEditor {
|
||||
protected _input: EditorInput;
|
||||
private _options: EditorOptions;
|
||||
private _position: Position;
|
||||
|
||||
constructor(id: string, telemetryService: ITelemetryService, themeService: IThemeService) {
|
||||
private static readonly EDITOR_MEMENTOS: Map<string, EditorMemento<any>> = new Map<string, EditorMemento<any>>();
|
||||
|
||||
readonly minimumWidth = DEFAULT_EDITOR_MIN_DIMENSIONS.width;
|
||||
readonly maximumWidth = DEFAULT_EDITOR_MAX_DIMENSIONS.width;
|
||||
readonly minimumHeight = DEFAULT_EDITOR_MIN_DIMENSIONS.height;
|
||||
readonly maximumHeight = DEFAULT_EDITOR_MAX_DIMENSIONS.height;
|
||||
|
||||
readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; }> = Event.None;
|
||||
|
||||
protected _input: EditorInput;
|
||||
|
||||
private _options: EditorOptions;
|
||||
private _group: IEditorGroup;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
telemetryService: ITelemetryService,
|
||||
themeService: IThemeService
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
}
|
||||
|
||||
public get input(): EditorInput {
|
||||
get input(): EditorInput {
|
||||
return this._input;
|
||||
}
|
||||
|
||||
public get options(): EditorOptions {
|
||||
get options(): EditorOptions {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
get group(): IEditorGroup {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: Clients should not call this method, the workbench calls this
|
||||
* method. Calling it otherwise may result in unexpected behavior.
|
||||
*
|
||||
* Sets the given input with the options to the part. An editor has to deal with the
|
||||
* situation that the same input is being set with different options.
|
||||
* Sets the given input with the options to the editor. The input is guaranteed
|
||||
* to be different from the previous input that was set using the input.matches()
|
||||
* method.
|
||||
*
|
||||
* The provided cancellation token should be used to test if the operation
|
||||
* was cancelled.
|
||||
*/
|
||||
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
|
||||
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
|
||||
this._input = input;
|
||||
this._options = options;
|
||||
|
||||
@@ -55,17 +86,28 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to indicate to the editor that the input should be cleared and resources associated with the
|
||||
* input should be freed.
|
||||
* Called to indicate to the editor that the input should be cleared and
|
||||
* resources associated with the input should be freed.
|
||||
*/
|
||||
public clearInput(): void {
|
||||
clearInput(): void {
|
||||
this._input = null;
|
||||
this._options = null;
|
||||
}
|
||||
|
||||
public create(parent: HTMLElement): void; // create is sync for editors
|
||||
public create(parent: HTMLElement): TPromise<void>;
|
||||
public create(parent: HTMLElement): TPromise<void> {
|
||||
/**
|
||||
* Note: Clients should not call this method, the workbench calls this
|
||||
* method. Calling it otherwise may result in unexpected behavior.
|
||||
*
|
||||
* Sets the given options to the editor. Clients should apply the options
|
||||
* to the current input.
|
||||
*/
|
||||
setOptions(options: EditorOptions): void {
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): void; // create is sync for editors
|
||||
create(parent: HTMLElement): TPromise<void>;
|
||||
create(parent: HTMLElement): TPromise<void> {
|
||||
const res = super.create(parent);
|
||||
|
||||
// Create Editor
|
||||
@@ -79,50 +121,183 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
||||
*/
|
||||
protected abstract createEditor(parent: HTMLElement): void;
|
||||
|
||||
/**
|
||||
* Subclasses can set this to false if it does not make sense to center editor input.
|
||||
*/
|
||||
public supportsCenteredLayout(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload this function to allow for passing in a position argument.
|
||||
*/
|
||||
public setVisible(visible: boolean, position?: Position): void; // setVisible is sync for editors
|
||||
public setVisible(visible: boolean, position?: Position): TPromise<void>;
|
||||
public setVisible(visible: boolean, position: Position = null): TPromise<void> {
|
||||
setVisible(visible: boolean, group?: IEditorGroup): void; // setVisible is sync for editors
|
||||
setVisible(visible: boolean, group?: IEditorGroup): TPromise<void>;
|
||||
setVisible(visible: boolean, group?: IEditorGroup): TPromise<void> {
|
||||
const promise = super.setVisible(visible);
|
||||
|
||||
// Propagate to Editor
|
||||
this.setEditorVisible(visible, position);
|
||||
this.setEditorVisible(visible, group);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
protected setEditorVisible(visible: boolean, position: Position = null): void {
|
||||
this._position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the position of the editor changes while it is visible.
|
||||
* Indicates that the editor control got visible or hidden in a specific group. A
|
||||
* editor instance will only ever be visible in one editor group.
|
||||
*
|
||||
* @param visible the state of visibility of this editor
|
||||
* @param group the editor group this editor is in.
|
||||
*/
|
||||
public changePosition(position: Position): void {
|
||||
this._position = position;
|
||||
protected setEditorVisible(visible: boolean, group: IEditorGroup): void {
|
||||
this._group = group;
|
||||
}
|
||||
|
||||
/**
|
||||
* The position this editor is showing in or null if none.
|
||||
*/
|
||||
public get position(): Position {
|
||||
return this._position;
|
||||
protected getEditorMemento<T>(storageService: IStorageService, editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
|
||||
const mementoKey = `${this.getId()}${key}`;
|
||||
|
||||
let editorMemento = BaseEditor.EDITOR_MEMENTOS.get(mementoKey);
|
||||
if (!editorMemento) {
|
||||
editorMemento = new EditorMemento(this.getId(), key, this.getMemento(storageService), limit, editorGroupService);
|
||||
BaseEditor.EDITOR_MEMENTOS.set(mementoKey, editorMemento);
|
||||
}
|
||||
|
||||
return editorMemento;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
shutdown(): void {
|
||||
|
||||
// Shutdown all editor memento for this editor type
|
||||
BaseEditor.EDITOR_MEMENTOS.forEach(editorMemento => {
|
||||
if (editorMemento.id === this.getId()) {
|
||||
editorMemento.shutdown();
|
||||
}
|
||||
});
|
||||
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._input = null;
|
||||
this._options = null;
|
||||
|
||||
// Super Dispose
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
interface MapGroupToMemento<T> {
|
||||
[group: number]: T;
|
||||
}
|
||||
|
||||
export class EditorMemento<T> implements IEditorMemento<T> {
|
||||
private cache: LRUCache<string, MapGroupToMemento<T>>;
|
||||
private cleanedUp = false;
|
||||
|
||||
constructor(
|
||||
private _id: string,
|
||||
private key: string,
|
||||
private memento: object,
|
||||
private limit: number,
|
||||
private editorGroupService: IEditorGroupsService
|
||||
) { }
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
saveState(group: IEditorGroup, resource: URI, state: T): void;
|
||||
saveState(group: IEditorGroup, editor: EditorInput, state: T): void;
|
||||
saveState(group: IEditorGroup, resourceOrEditor: URI | EditorInput, state: T): void {
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (!resource || !group) {
|
||||
return; // we are not in a good state to save any state for a resource
|
||||
}
|
||||
|
||||
const cache = this.doLoad();
|
||||
|
||||
let mementoForResource = cache.get(resource.toString());
|
||||
if (!mementoForResource) {
|
||||
mementoForResource = Object.create(null) as MapGroupToMemento<T>;
|
||||
cache.set(resource.toString(), mementoForResource);
|
||||
}
|
||||
|
||||
mementoForResource[group.id] = state;
|
||||
|
||||
// Automatically clear when editor input gets disposed if any
|
||||
if (resourceOrEditor instanceof EditorInput) {
|
||||
once(resourceOrEditor.onDispose)(() => {
|
||||
this.clearState(resource);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
loadState(group: IEditorGroup, resource: URI): T;
|
||||
loadState(group: IEditorGroup, editor: EditorInput): T;
|
||||
loadState(group: IEditorGroup, resourceOrEditor: URI | EditorInput): T {
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (!resource || !group) {
|
||||
return void 0; // we are not in a good state to load any state for a resource
|
||||
}
|
||||
|
||||
const cache = this.doLoad();
|
||||
|
||||
const mementoForResource = cache.get(resource.toString());
|
||||
if (mementoForResource) {
|
||||
return mementoForResource[group.id];
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
|
||||
clearState(resource: URI): void;
|
||||
clearState(editor: EditorInput): void;
|
||||
clearState(resourceOrEditor: URI | EditorInput): void {
|
||||
const resource = this.doGetResource(resourceOrEditor);
|
||||
if (resource) {
|
||||
const cache = this.doLoad();
|
||||
cache.delete(resource.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private doGetResource(resourceOrEditor: URI | EditorInput): URI {
|
||||
if (resourceOrEditor instanceof EditorInput) {
|
||||
return resourceOrEditor.getResource();
|
||||
}
|
||||
|
||||
return resourceOrEditor;
|
||||
}
|
||||
|
||||
private doLoad(): LRUCache<string, MapGroupToMemento<T>> {
|
||||
if (!this.cache) {
|
||||
this.cache = new LRUCache<string, MapGroupToMemento<T>>(this.limit);
|
||||
|
||||
// Restore from serialized map state
|
||||
const rawEditorMemento = this.memento[this.key];
|
||||
if (Array.isArray(rawEditorMemento)) {
|
||||
this.cache.fromJSON(rawEditorMemento);
|
||||
}
|
||||
}
|
||||
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
const cache = this.doLoad();
|
||||
|
||||
// Cleanup once during shutdown
|
||||
if (!this.cleanedUp) {
|
||||
this.cleanUp();
|
||||
this.cleanedUp = true;
|
||||
}
|
||||
|
||||
this.memento[this.key] = cache.toJSON();
|
||||
}
|
||||
|
||||
private cleanUp(): void {
|
||||
const cache = this.doLoad();
|
||||
|
||||
// Remove groups from states that no longer exist
|
||||
cache.forEach((mapGroupToMemento, resource) => {
|
||||
Object.keys(mapGroupToMemento).forEach(group => {
|
||||
const groupId: GroupIdentifier = Number(group);
|
||||
if (!this.editorGroupService.getGroup(groupId)) {
|
||||
delete mapGroupToMemento[groupId];
|
||||
|
||||
if (isEmptyObject(mapGroupToMemento)) {
|
||||
cache.delete(resource);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/bina
|
||||
*/
|
||||
export class BinaryResourceDiffEditor extends SideBySideEditor {
|
||||
|
||||
public static readonly ID = BINARY_DIFF_EDITOR_ID;
|
||||
static readonly ID = BINARY_DIFF_EDITOR_ID;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@@ -28,7 +28,7 @@ export class BinaryResourceDiffEditor extends SideBySideEditor {
|
||||
super(telemetryService, instantiationService, themeService);
|
||||
}
|
||||
|
||||
public getMetadata(): string {
|
||||
getMetadata(): string {
|
||||
const master = this.masterEditor;
|
||||
const details = this.detailsEditor;
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/parts/editor/resourceViewer';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export interface IOpenCallbacks {
|
||||
openInternal: (input: EditorInput, options: EditorOptions) => void;
|
||||
@@ -30,7 +32,8 @@ export interface IOpenCallbacks {
|
||||
*/
|
||||
export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
|
||||
private readonly _onMetadataChanged: Emitter<void>;
|
||||
private readonly _onMetadataChanged: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onMetadataChanged(): Event<void> { return this._onMetadataChanged.event; }
|
||||
|
||||
private callbacks: IOpenCallbacks;
|
||||
private metadata: string;
|
||||
@@ -42,21 +45,15 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
id: string,
|
||||
callbacks: IOpenCallbacks,
|
||||
telemetryService: ITelemetryService,
|
||||
themeService: IThemeService
|
||||
themeService: IThemeService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
|
||||
this._onMetadataChanged = new Emitter<void>();
|
||||
this.toUnbind.push(this._onMetadataChanged);
|
||||
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
public get onMetadataChanged(): Event<void> {
|
||||
return this._onMetadataChanged.event;
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
getTitle(): string {
|
||||
return this.input ? this.input.getName() : nls.localize('binaryEditor', "Binary Viewer");
|
||||
}
|
||||
|
||||
@@ -70,35 +67,28 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
this.binaryContainer.tabindex(0); // enable focus support from the editor part (do not remove)
|
||||
|
||||
// Custom Scrollbars
|
||||
this.scrollbar = new DomScrollableElement(binaryContainerElement, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto });
|
||||
this.scrollbar = this._register(new DomScrollableElement(binaryContainerElement, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto }));
|
||||
parent.appendChild(this.scrollbar.getDomNode());
|
||||
}
|
||||
|
||||
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
|
||||
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
|
||||
return super.setInput(input, options, token).then(() => {
|
||||
return input.resolve().then(model => {
|
||||
|
||||
// Return early for same input unless we force to open
|
||||
const forceOpen = options && options.forceOpen;
|
||||
if (!forceOpen && input.matches(this.input)) {
|
||||
return TPromise.wrap<void>(null);
|
||||
}
|
||||
|
||||
// Otherwise set input and resolve
|
||||
return super.setInput(input, options).then(() => {
|
||||
return input.resolve(true).then(model => {
|
||||
// Check for cancellation
|
||||
if (token.isCancellationRequested) {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
// Assert Model instance
|
||||
if (!(model instanceof BinaryEditorModel)) {
|
||||
return TPromise.wrapError<void>(new Error('Unable to open file as binary'));
|
||||
}
|
||||
|
||||
// Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile
|
||||
if (!this.input || this.input !== input) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Render Input
|
||||
this.resourceViewerContext = ResourceViewer.show(
|
||||
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() },
|
||||
this._fileService,
|
||||
this.binaryContainer.getHTMLElement(),
|
||||
this.scrollbar,
|
||||
resource => this.callbacks.openInternal(input, options),
|
||||
@@ -106,25 +96,22 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
meta => this.handleMetadataChanged(meta)
|
||||
);
|
||||
|
||||
return TPromise.as<void>(null);
|
||||
return void 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private handleMetadataChanged(meta: string): void {
|
||||
this.metadata = meta;
|
||||
|
||||
this._onMetadataChanged.fire();
|
||||
}
|
||||
|
||||
public getMetadata(): string {
|
||||
getMetadata(): string {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
public supportsCenteredLayout(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public clearInput(): void {
|
||||
clearInput(): void {
|
||||
|
||||
// Clear Meta
|
||||
this.handleMetadataChanged(null);
|
||||
@@ -135,7 +122,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
layout(dimension: Dimension): void {
|
||||
|
||||
// Pass on to Binary Container
|
||||
this.binaryContainer.size(dimension.width, dimension.height);
|
||||
@@ -145,15 +132,14 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
}
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
focus(): void {
|
||||
this.binaryContainer.domFocus();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
|
||||
// Destroy Container
|
||||
this.binaryContainer.destroy();
|
||||
this.scrollbar.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
147
src/vs/workbench/browser/parts/editor/breadcrumbs.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { BreadcrumbsWidget } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export const IBreadcrumbsService = createDecorator<IBreadcrumbsService>('IEditorBreadcrumbsService');
|
||||
|
||||
export interface IBreadcrumbsService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
register(group: GroupIdentifier, widget: BreadcrumbsWidget): IDisposable;
|
||||
|
||||
getWidget(group: GroupIdentifier): BreadcrumbsWidget;
|
||||
}
|
||||
|
||||
|
||||
export class BreadcrumbsService implements IBreadcrumbsService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private readonly _map = new Map<number, BreadcrumbsWidget>();
|
||||
|
||||
register(group: number, widget: BreadcrumbsWidget): IDisposable {
|
||||
if (this._map.has(group)) {
|
||||
throw new Error(`group (${group}) has already a widget`);
|
||||
}
|
||||
this._map.set(group, widget);
|
||||
return {
|
||||
dispose: () => this._map.delete(group)
|
||||
};
|
||||
}
|
||||
|
||||
getWidget(group: number): BreadcrumbsWidget {
|
||||
return this._map.get(group);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IBreadcrumbsService, BreadcrumbsService);
|
||||
|
||||
|
||||
//#region config
|
||||
|
||||
export abstract class BreadcrumbsConfig<T> {
|
||||
|
||||
name: string;
|
||||
value: T;
|
||||
onDidChange: Event<T>;
|
||||
abstract dispose(): void;
|
||||
|
||||
private constructor() {
|
||||
// internal
|
||||
}
|
||||
|
||||
static IsEnabled = BreadcrumbsConfig._stub<boolean>('breadcrumbs.enabled');
|
||||
static UseQuickPick = BreadcrumbsConfig._stub<boolean>('breadcrumbs.useQuickPick');
|
||||
static FilePath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.filePath');
|
||||
static SymbolPath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.symbolPath');
|
||||
|
||||
private static _stub<T>(name: string): { bindTo(service: IConfigurationService): BreadcrumbsConfig<T> } {
|
||||
return {
|
||||
bindTo(service) {
|
||||
let value: T = service.getValue(name);
|
||||
let onDidChange = new Emitter<T>();
|
||||
|
||||
let listener = service.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(name)) {
|
||||
value = service.getValue(name);
|
||||
onDidChange.fire(value);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
name,
|
||||
get value() {
|
||||
return value;
|
||||
},
|
||||
set value(newValue: T) {
|
||||
service.updateValue(name, newValue);
|
||||
value = newValue;
|
||||
},
|
||||
onDidChange: onDidChange.event,
|
||||
dispose(): void {
|
||||
listener.dispose();
|
||||
onDidChange.dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
||||
id: 'breadcrumbs',
|
||||
title: localize('title', "Breadcrumb Navigation"),
|
||||
order: 101,
|
||||
type: 'object',
|
||||
properties: {
|
||||
'breadcrumbs.enabled': {
|
||||
description: localize('enabled', "Enable/disable navigation breadcrumbs"),
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
// 'breadcrumbs.useQuickPick': {
|
||||
// description: localize('useQuickPick', "Use quick pick instead of breadcrumb-pickers."),
|
||||
// type: 'boolean',
|
||||
// default: false
|
||||
// },
|
||||
'breadcrumbs.filePath': {
|
||||
description: localize('filepath', "Controls whether and how file paths are shown in the breadcrumbs view."),
|
||||
type: 'string',
|
||||
default: 'on',
|
||||
enum: ['on', 'off', 'last'],
|
||||
enumDescriptions: [
|
||||
localize('filepath.on', "Show the file path in the breadcrumbs view."),
|
||||
localize('filepath.off', "Do not show the file path in the breadcrumbs view."),
|
||||
localize('filepath.last', "Only show the last element of the file path in the breadcrumbs view."),
|
||||
]
|
||||
},
|
||||
'breadcrumbs.symbolPath': {
|
||||
description: localize('symbolpath', "Controls whether and how symbols are shown in the breadcrumbs view."),
|
||||
type: 'string',
|
||||
default: 'on',
|
||||
enum: ['on', 'off', 'last'],
|
||||
enumDescriptions: [
|
||||
localize('symbolpath.on', "Show all symbols in the breadcrumbs view."),
|
||||
localize('symbolpath.off', "Do not show symbols in the breadcrumbs view."),
|
||||
localize('symbolpath.last', "Only show the current symbol in the breadcrumbs view."),
|
||||
]
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
//#endregion
|
||||
501
src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts
Normal file
@@ -0,0 +1,501 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { BreadcrumbsItem, BreadcrumbsWidget, IBreadcrumbsItemEvent } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget';
|
||||
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { combinedDisposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import 'vs/css!./media/breadcrumbscontrol';
|
||||
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { symbolKindToCssClass } from 'vs/editor/common/modes';
|
||||
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { FileKind, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { FileLabel } from 'vs/workbench/browser/labels';
|
||||
import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs';
|
||||
import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
|
||||
import { createBreadcrumbsPicker, BreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker';
|
||||
import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
|
||||
import { IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { tail } from 'vs/base/common/arrays';
|
||||
import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService';
|
||||
|
||||
class Item extends BreadcrumbsItem {
|
||||
|
||||
private readonly _disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
readonly element: BreadcrumbElement,
|
||||
readonly options: IBreadcrumbsControlOptions,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this._disposables);
|
||||
}
|
||||
|
||||
equals(other: BreadcrumbsItem): boolean {
|
||||
if (!(other instanceof Item)) {
|
||||
return false;
|
||||
}
|
||||
if (this.element instanceof FileElement && other.element instanceof FileElement) {
|
||||
return isEqual(this.element.uri, other.element.uri);
|
||||
}
|
||||
if (this.element instanceof TreeElement && other.element instanceof TreeElement) {
|
||||
return this.element.id === other.element.id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
render(container: HTMLElement): void {
|
||||
if (this.element instanceof FileElement) {
|
||||
// file/folder
|
||||
let label = this._instantiationService.createInstance(FileLabel, container, {});
|
||||
label.setFile(this.element.uri, {
|
||||
hidePath: true,
|
||||
hideIcon: this.element.kind === FileKind.FOLDER || !this.options.showFileIcons,
|
||||
fileKind: this.element.kind,
|
||||
fileDecorations: { colors: this.options.showDecorationColors, badges: false },
|
||||
});
|
||||
dom.addClass(container, FileKind[this.element.kind].toLowerCase());
|
||||
this._disposables.push(label);
|
||||
|
||||
} else if (this.element instanceof OutlineModel) {
|
||||
// has outline element but not in one
|
||||
let label = document.createElement('div');
|
||||
label.innerHTML = '…';
|
||||
label.className = 'hint-more';
|
||||
container.appendChild(label);
|
||||
|
||||
} else if (this.element instanceof OutlineGroup) {
|
||||
// provider
|
||||
let label = new IconLabel(container);
|
||||
label.setValue(this.element.provider.displayName);
|
||||
this._disposables.push(label);
|
||||
|
||||
} else if (this.element instanceof OutlineElement) {
|
||||
// symbol
|
||||
if (this.options.showSymbolIcons) {
|
||||
let icon = document.createElement('div');
|
||||
icon.className = symbolKindToCssClass(this.element.symbol.kind);
|
||||
container.appendChild(icon);
|
||||
dom.addClass(container, 'shows-symbol-icon');
|
||||
}
|
||||
let label = new IconLabel(container);
|
||||
let title = this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE');
|
||||
label.setValue(title);
|
||||
this._disposables.push(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface IBreadcrumbsControlOptions {
|
||||
showFileIcons: boolean;
|
||||
showSymbolIcons: boolean;
|
||||
showDecorationColors: boolean;
|
||||
extraClasses: string[];
|
||||
}
|
||||
|
||||
export class BreadcrumbsControl {
|
||||
|
||||
static HEIGHT = 25;
|
||||
|
||||
static readonly Payload_Reveal = {};
|
||||
static readonly Payload_RevealAside = {};
|
||||
static readonly Payload_Pick = {};
|
||||
|
||||
static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false);
|
||||
static CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false);
|
||||
|
||||
private readonly _ckBreadcrumbsVisible: IContextKey<boolean>;
|
||||
private readonly _ckBreadcrumbsActive: IContextKey<boolean>;
|
||||
|
||||
private readonly _cfUseQuickPick: BreadcrumbsConfig<boolean>;
|
||||
|
||||
readonly domNode: HTMLDivElement;
|
||||
private readonly _widget: BreadcrumbsWidget;
|
||||
|
||||
private _disposables = new Array<IDisposable>();
|
||||
private _breadcrumbsDisposables = new Array<IDisposable>();
|
||||
private _breadcrumbsPickerShowing = false;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
private readonly _options: IBreadcrumbsControlOptions,
|
||||
private readonly _editorGroup: EditorGroupView,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IContextViewService private readonly _contextViewService: IContextViewService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
@IQuickOpenService private readonly _quickOpenService: IQuickOpenService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IBreadcrumbsService breadcrumbsService: IBreadcrumbsService,
|
||||
) {
|
||||
this.domNode = document.createElement('div');
|
||||
dom.addClass(this.domNode, 'breadcrumbs-control');
|
||||
dom.addClasses(this.domNode, ..._options.extraClasses);
|
||||
dom.append(container, this.domNode);
|
||||
|
||||
this._widget = new BreadcrumbsWidget(this.domNode);
|
||||
this._widget.onDidSelectItem(this._onSelectEvent, this, this._disposables);
|
||||
this._widget.onDidFocusItem(this._onFocusEvent, this, this._disposables);
|
||||
this._widget.onDidChangeFocus(this._updateCkBreadcrumbsActive, this, this._disposables);
|
||||
this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService));
|
||||
|
||||
this._ckBreadcrumbsVisible = BreadcrumbsControl.CK_BreadcrumbsVisible.bindTo(this._contextKeyService);
|
||||
this._ckBreadcrumbsActive = BreadcrumbsControl.CK_BreadcrumbsActive.bindTo(this._contextKeyService);
|
||||
|
||||
this._cfUseQuickPick = BreadcrumbsConfig.UseQuickPick.bindTo(_configurationService);
|
||||
|
||||
this._disposables.push(breadcrumbsService.register(this._editorGroup.id, this._widget));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables = dispose(this._disposables);
|
||||
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
|
||||
this._ckBreadcrumbsVisible.reset();
|
||||
this._ckBreadcrumbsActive.reset();
|
||||
this._cfUseQuickPick.dispose();
|
||||
this._widget.dispose();
|
||||
this.domNode.remove();
|
||||
}
|
||||
|
||||
layout(dim: dom.Dimension): void {
|
||||
this._widget.layout(dim);
|
||||
}
|
||||
|
||||
isHidden(): boolean {
|
||||
return dom.hasClass(this.domNode, 'hidden');
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
|
||||
this._ckBreadcrumbsVisible.set(false);
|
||||
dom.toggleClass(this.domNode, 'hidden', true);
|
||||
}
|
||||
|
||||
update(): boolean {
|
||||
const input = this._editorGroup.activeEditor;
|
||||
this._breadcrumbsDisposables = dispose(this._breadcrumbsDisposables);
|
||||
|
||||
if (!input || !input.getResource() || (input.getResource().scheme !== Schemas.untitled && !this._fileService.canHandleResource(input.getResource()))) {
|
||||
// cleanup and return when there is no input or when
|
||||
// we cannot handle this input
|
||||
if (!this.isHidden()) {
|
||||
this.hide();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
dom.toggleClass(this.domNode, 'hidden', false);
|
||||
this._ckBreadcrumbsVisible.set(true);
|
||||
|
||||
let control = this._editorGroup.activeControl.getControl() as ICodeEditor;
|
||||
let model = new EditorBreadcrumbsModel(input.getResource(), isCodeEditor(control) ? control : undefined, this._workspaceService, this._configurationService);
|
||||
dom.toggleClass(this.domNode, 'relative-path', model.isRelative());
|
||||
|
||||
let updateBreadcrumbs = () => {
|
||||
let items = model.getElements().map(element => new Item(element, this._options, this._instantiationService));
|
||||
this._widget.setItems(items);
|
||||
this._widget.reveal(items[items.length - 1]);
|
||||
};
|
||||
let listener = model.onDidUpdate(updateBreadcrumbs);
|
||||
updateBreadcrumbs();
|
||||
this._breadcrumbsDisposables = [model, listener];
|
||||
|
||||
// close picker on hide/update
|
||||
this._breadcrumbsDisposables.push({
|
||||
dispose: () => {
|
||||
if (this._breadcrumbsPickerShowing) {
|
||||
this._contextViewService.hideContextView(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private _onFocusEvent(event: IBreadcrumbsItemEvent): void {
|
||||
if (event.item && this._breadcrumbsPickerShowing) {
|
||||
return this._widget.setSelection(event.item);
|
||||
}
|
||||
}
|
||||
|
||||
private _onSelectEvent(event: IBreadcrumbsItemEvent): void {
|
||||
if (!event.item) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._editorGroup.focus();
|
||||
const { element } = event.item as Item;
|
||||
|
||||
const group = this._getEditorGroup(event.payload);
|
||||
if (group !== undefined) {
|
||||
// reveal the item
|
||||
this._widget.setFocused(undefined);
|
||||
this._widget.setSelection(undefined);
|
||||
this._revealInEditor(event, element, group);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._cfUseQuickPick.value) {
|
||||
// using quick pick
|
||||
this._widget.setFocused(undefined);
|
||||
this._widget.setSelection(undefined);
|
||||
this._quickOpenService.show(element instanceof TreeElement ? '@' : '');
|
||||
return;
|
||||
}
|
||||
|
||||
// show picker
|
||||
let picker: BreadcrumbsPicker;
|
||||
this._contextViewService.showContextView({
|
||||
render: (parent: HTMLElement) => {
|
||||
picker = createBreadcrumbsPicker(this._instantiationService, parent, element);
|
||||
let listener = picker.onDidPickElement(data => {
|
||||
this._contextViewService.hideContextView(this);
|
||||
this._revealInEditor(event, data.target, this._getEditorGroup(data.payload && data.payload.originalEvent));
|
||||
});
|
||||
this._breadcrumbsPickerShowing = true;
|
||||
this._updateCkBreadcrumbsActive();
|
||||
|
||||
return combinedDisposable([listener, picker]);
|
||||
},
|
||||
getAnchor() {
|
||||
|
||||
let pickerHeight = 330;
|
||||
let pickerWidth = Math.max(220, dom.getTotalWidth(event.node));
|
||||
let pickerArrowSize = 8;
|
||||
let pickerArrowOffset: number;
|
||||
|
||||
let data = dom.getDomNodePagePosition(event.node.firstChild as HTMLElement);
|
||||
let y = data.top + data.height - pickerArrowSize;
|
||||
let x = data.left;
|
||||
if (x + pickerWidth >= window.innerWidth) {
|
||||
x = window.innerWidth - pickerWidth;
|
||||
}
|
||||
if (event.payload instanceof StandardMouseEvent) {
|
||||
pickerArrowOffset = event.payload.posx - x - pickerArrowSize;
|
||||
} else {
|
||||
pickerArrowOffset = (data.left + (data.width * .3)) - x;
|
||||
}
|
||||
picker.layout(pickerHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset));
|
||||
picker.setInput(element);
|
||||
return { x, y };
|
||||
},
|
||||
onHide: (data) => {
|
||||
this._breadcrumbsPickerShowing = false;
|
||||
this._updateCkBreadcrumbsActive();
|
||||
if (data === this) {
|
||||
this._widget.setFocused(undefined);
|
||||
this._widget.setSelection(undefined);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _updateCkBreadcrumbsActive(): void {
|
||||
const value = this._widget.isDOMFocused() || this._breadcrumbsPickerShowing;
|
||||
this._ckBreadcrumbsActive.set(value);
|
||||
}
|
||||
|
||||
private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): void {
|
||||
if (element instanceof FileElement) {
|
||||
if (element.kind === FileKind.FILE) {
|
||||
// open file in editor
|
||||
this._editorService.openEditor({ resource: element.uri }, group);
|
||||
} else {
|
||||
// show next picker
|
||||
let items = this._widget.getItems();
|
||||
let idx = items.indexOf(event.item);
|
||||
this._widget.setFocused(items[idx + 1]);
|
||||
this._widget.setSelection(items[idx + 1], BreadcrumbsControl.Payload_Pick);
|
||||
}
|
||||
|
||||
} else if (element instanceof OutlineElement) {
|
||||
// open symbol in editor
|
||||
let model = OutlineModel.get(element);
|
||||
this._editorService.openEditor({
|
||||
resource: model.textModel.uri,
|
||||
options: { selection: Range.collapseToStart(element.symbol.selectionRange) }
|
||||
}, group);
|
||||
}
|
||||
}
|
||||
|
||||
private _getEditorGroup(data: StandardMouseEvent | object): SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | undefined {
|
||||
if (data === BreadcrumbsControl.Payload_RevealAside || (data instanceof StandardMouseEvent && data.altKey)) {
|
||||
return SIDE_GROUP;
|
||||
} else if (data === BreadcrumbsControl.Payload_Reveal || (data instanceof StandardMouseEvent && data.metaKey)) {
|
||||
return ACTIVE_GROUP;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#region commands
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: 'breadcrumbs.focusAndSelect',
|
||||
title: localize('cmd.focus', "Focus Breadcrumbs")
|
||||
}
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: 'breadcrumbs.toggle',
|
||||
title: localize('cmd.toggle', "Toggle Breadcrumbs")
|
||||
}
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
group: '5_editor',
|
||||
order: 99,
|
||||
command: {
|
||||
id: 'breadcrumbs.toggle',
|
||||
title: localize('cmd.toggle', "Toggle Breadcrumbs")
|
||||
}
|
||||
});
|
||||
CommandsRegistry.registerCommand('breadcrumbs.toggle', accessor => {
|
||||
let config = accessor.get(IConfigurationService);
|
||||
let value = BreadcrumbsConfig.IsEnabled.bindTo(config).value;
|
||||
BreadcrumbsConfig.IsEnabled.bindTo(config).value = !value;
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.focus',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_SEMICOLON,
|
||||
when: BreadcrumbsControl.CK_BreadcrumbsVisible,
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
const item = tail(widget.getItems());
|
||||
widget.setFocused(item);
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.focusAndSelect',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT,
|
||||
when: BreadcrumbsControl.CK_BreadcrumbsVisible,
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
const item = tail(widget.getItems());
|
||||
widget.setFocused(item);
|
||||
widget.setSelection(item, BreadcrumbsControl.Payload_Pick);
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.focusNext',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.RightArrow,
|
||||
secondary: [KeyMod.CtrlCmd | KeyCode.RightArrow],
|
||||
mac: {
|
||||
primary: KeyCode.RightArrow,
|
||||
secondary: [KeyMod.Alt | KeyCode.RightArrow],
|
||||
},
|
||||
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
breadcrumbs.getWidget(groups.activeGroup.id).focusNext();
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.focusPrevious',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.LeftArrow,
|
||||
secondary: [KeyMod.CtrlCmd | KeyCode.LeftArrow],
|
||||
mac: {
|
||||
primary: KeyCode.LeftArrow,
|
||||
secondary: [KeyMod.Alt | KeyCode.LeftArrow],
|
||||
},
|
||||
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
breadcrumbs.getWidget(groups.activeGroup.id).focusPrev();
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.selectFocused',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.Enter,
|
||||
secondary: [KeyCode.DownArrow],
|
||||
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_Pick);
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.revealFocused',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.Space,
|
||||
secondary: [KeyMod.CtrlCmd | KeyCode.Enter],
|
||||
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_Reveal);
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.selectEditor',
|
||||
weight: KeybindingWeight.WorkbenchContrib + 1,
|
||||
primary: KeyCode.Escape,
|
||||
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive),
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
breadcrumbs.getWidget(groups.activeGroup.id).setFocused(undefined);
|
||||
breadcrumbs.getWidget(groups.activeGroup.id).setSelection(undefined);
|
||||
groups.activeGroup.activeControl.focus();
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'breadcrumbs.revealFocusedFromTreeAside',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Enter,
|
||||
when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive, WorkbenchListFocusContextKey),
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_RevealAside);
|
||||
}
|
||||
});
|
||||
//#endregion
|
||||
231
src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { size } from 'vs/base/common/collections';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
|
||||
export class FileElement {
|
||||
constructor(
|
||||
readonly uri: URI,
|
||||
readonly kind: FileKind
|
||||
) { }
|
||||
}
|
||||
|
||||
export type BreadcrumbElement = FileElement | OutlineModel | OutlineGroup | OutlineElement;
|
||||
|
||||
type FileInfo = { path: FileElement[], folder: IWorkspaceFolder };
|
||||
|
||||
export class EditorBreadcrumbsModel {
|
||||
|
||||
private readonly _disposables: IDisposable[] = [];
|
||||
private readonly _fileInfo: FileInfo;
|
||||
|
||||
private readonly _cfgFilePath: BreadcrumbsConfig<'on' | 'off' | 'last'>;
|
||||
private readonly _cfgSymbolPath: BreadcrumbsConfig<'on' | 'off' | 'last'>;
|
||||
|
||||
private _outlineElements: (OutlineModel | OutlineGroup | OutlineElement)[] = [];
|
||||
private _outlineDisposables: IDisposable[] = [];
|
||||
|
||||
private _onDidUpdate = new Emitter<this>();
|
||||
readonly onDidUpdate: Event<this> = this._onDidUpdate.event;
|
||||
|
||||
constructor(
|
||||
private readonly _uri: URI,
|
||||
private readonly _editor: ICodeEditor | undefined,
|
||||
@IWorkspaceContextService workspaceService: IWorkspaceContextService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
|
||||
this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(configurationService);
|
||||
this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(configurationService);
|
||||
|
||||
this._disposables.push(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this)));
|
||||
this._disposables.push(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this)));
|
||||
|
||||
this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(this._uri, workspaceService);
|
||||
this._bindToEditor();
|
||||
this._onDidUpdate.fire(this);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._cfgFilePath.dispose();
|
||||
this._cfgSymbolPath.dispose();
|
||||
dispose(this._disposables);
|
||||
}
|
||||
|
||||
isRelative(): boolean {
|
||||
return Boolean(this._fileInfo.folder);
|
||||
}
|
||||
|
||||
getElements(): ReadonlyArray<BreadcrumbElement> {
|
||||
let result: BreadcrumbElement[] = [];
|
||||
|
||||
// file path elements
|
||||
if (this._cfgFilePath.value === 'on') {
|
||||
result = result.concat(this._fileInfo.path);
|
||||
} else if (this._cfgFilePath.value === 'last' && this._fileInfo.path.length > 0) {
|
||||
result = result.concat(this._fileInfo.path.slice(-1));
|
||||
}
|
||||
|
||||
// symbol path elements
|
||||
if (this._cfgSymbolPath.value === 'on') {
|
||||
result = result.concat(this._outlineElements);
|
||||
} else if (this._cfgSymbolPath.value === 'last' && this._outlineElements.length > 0) {
|
||||
result = result.concat(this._outlineElements.slice(-1));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _initFilePathInfo(uri: URI, workspaceService: IWorkspaceContextService): FileInfo {
|
||||
|
||||
if (uri.scheme === Schemas.untitled) {
|
||||
return {
|
||||
folder: undefined,
|
||||
path: []
|
||||
};
|
||||
}
|
||||
|
||||
let info: FileInfo = {
|
||||
folder: workspaceService.getWorkspaceFolder(uri),
|
||||
path: []
|
||||
};
|
||||
|
||||
while (uri.path !== '/') {
|
||||
if (info.folder && isEqual(info.folder.uri, uri)) {
|
||||
break;
|
||||
}
|
||||
info.path.unshift(new FileElement(uri, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER));
|
||||
uri = uri.with({ path: paths.dirname(uri.path) });
|
||||
}
|
||||
|
||||
if (info.folder && workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
info.path.unshift(new FileElement(info.folder.uri, FileKind.ROOT_FOLDER));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
private _bindToEditor(): void {
|
||||
if (!this._editor) {
|
||||
return;
|
||||
}
|
||||
// update as model changes
|
||||
this._disposables.push(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline()));
|
||||
this._disposables.push(this._editor.onDidChangeModel(_ => this._updateOutline()));
|
||||
this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._updateOutline()));
|
||||
this._disposables.push(debounceEvent(this._editor.onDidChangeModelContent, _ => _, 350)(_ => this._updateOutline(true)));
|
||||
this._updateOutline();
|
||||
|
||||
// stop when editor dies
|
||||
this._disposables.push(this._editor.onDidDispose(() => this._outlineDisposables = dispose(this._outlineDisposables)));
|
||||
}
|
||||
|
||||
private _updateOutline(didChangeContent?: boolean): void {
|
||||
|
||||
this._outlineDisposables = dispose(this._outlineDisposables);
|
||||
if (!didChangeContent) {
|
||||
this._updateOutlineElements([]);
|
||||
}
|
||||
|
||||
const buffer = this._editor.getModel();
|
||||
if (!buffer || !DocumentSymbolProviderRegistry.has(buffer) || !isEqual(buffer.uri, this._uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const source = new CancellationTokenSource();
|
||||
const versionIdThen = buffer.getVersionId();
|
||||
const timeout = new TimeoutTimer();
|
||||
|
||||
this._outlineDisposables.push({
|
||||
dispose: () => {
|
||||
source.cancel();
|
||||
source.dispose();
|
||||
timeout.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
OutlineModel.create(buffer, source.token).then(model => {
|
||||
if (TreeElement.empty(model)) {
|
||||
// empty -> no outline elements
|
||||
this._updateOutlineElements([]);
|
||||
|
||||
} else {
|
||||
// copy the model
|
||||
model = model.adopt();
|
||||
|
||||
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition()));
|
||||
this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => {
|
||||
timeout.cancelAndSet(() => {
|
||||
if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId()) {
|
||||
this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition()));
|
||||
}
|
||||
}, 150);
|
||||
}));
|
||||
}
|
||||
}).catch(err => {
|
||||
this._updateOutlineElements([]);
|
||||
onUnexpectedError(err);
|
||||
});
|
||||
}
|
||||
|
||||
private _getOutlineElements(model: OutlineModel, position: IPosition): (OutlineModel | OutlineGroup | OutlineElement)[] {
|
||||
if (!model) {
|
||||
return [];
|
||||
}
|
||||
let item: OutlineGroup | OutlineElement = model.getItemEnclosingPosition(position);
|
||||
if (!item) {
|
||||
return [model];
|
||||
}
|
||||
let chain: (OutlineGroup | OutlineElement)[] = [];
|
||||
while (item) {
|
||||
chain.push(item);
|
||||
let parent = item.parent;
|
||||
if (parent instanceof OutlineModel) {
|
||||
break;
|
||||
}
|
||||
if (parent instanceof OutlineGroup && size(parent.parent.children) === 1) {
|
||||
break;
|
||||
}
|
||||
item = parent;
|
||||
}
|
||||
return chain.reverse();
|
||||
}
|
||||
|
||||
private _updateOutlineElements(elements: (OutlineModel | OutlineGroup | OutlineElement)[]): void {
|
||||
if (!equals(elements, this._outlineElements, EditorBreadcrumbsModel._outlineElementEquals)) {
|
||||
this._outlineElements = elements;
|
||||
this._onDidUpdate.fire(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static _outlineElementEquals(a: OutlineModel | OutlineGroup | OutlineElement, b: OutlineModel | OutlineGroup | OutlineElement): boolean {
|
||||
if (a === b) {
|
||||
return true;
|
||||
} else if (!a || !b) {
|
||||
return false;
|
||||
} else {
|
||||
return a.id === b.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
367
src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts
Normal file
@@ -0,0 +1,367 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { compareFileNames } from 'vs/base/common/comparers';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { dirname, isEqual } from 'vs/base/common/resources';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDataSource, IRenderer, ISelectionEvent, ISorter, ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import 'vs/css!./media/breadcrumbscontrol';
|
||||
import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
import { localize } from 'vs/nls';
|
||||
import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { HighlightingWorkbenchTree, IHighlightingTreeConfiguration, IHighlightingRenderer } from 'vs/platform/list/browser/listService';
|
||||
import { IThemeService, DARK } from 'vs/platform/theme/common/themeService';
|
||||
import { FileLabel } from 'vs/workbench/browser/labels';
|
||||
import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { breadcrumbsPickerBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { FuzzyScore, createMatches, fuzzyScore } from 'vs/base/common/filters';
|
||||
import { IWorkspaceContextService, IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker {
|
||||
let ctor: IConstructorSignature1<HTMLElement, BreadcrumbsPicker> = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker;
|
||||
return instantiationService.createInstance(ctor, parent);
|
||||
}
|
||||
|
||||
export abstract class BreadcrumbsPicker {
|
||||
|
||||
protected readonly _disposables = new Array<IDisposable>();
|
||||
protected readonly _domNode: HTMLDivElement;
|
||||
protected readonly _arrow: HTMLDivElement;
|
||||
protected readonly _treeContainer: HTMLDivElement;
|
||||
protected readonly _tree: HighlightingWorkbenchTree;
|
||||
protected readonly _focus: dom.IFocusTracker;
|
||||
|
||||
private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>();
|
||||
readonly onDidPickElement: Event<{ target: any, payload: any }> = this._onDidPickElement.event;
|
||||
|
||||
constructor(
|
||||
parent: HTMLElement,
|
||||
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
|
||||
@IThemeService protected readonly _themeService: IThemeService,
|
||||
) {
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.className = 'monaco-breadcrumbs-picker show-file-icons';
|
||||
parent.appendChild(this._domNode);
|
||||
|
||||
this._focus = dom.trackFocus(this._domNode);
|
||||
this._focus.onDidBlur(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined }), undefined, this._disposables);
|
||||
|
||||
const theme = this._themeService.getTheme();
|
||||
const color = theme.getColor(breadcrumbsPickerBackground);
|
||||
|
||||
this._arrow = document.createElement('div');
|
||||
this._arrow.style.width = '0';
|
||||
this._arrow.style.borderStyle = 'solid';
|
||||
this._arrow.style.borderWidth = '8px';
|
||||
this._arrow.style.borderColor = `transparent transparent ${color.toString()}`;
|
||||
this._domNode.appendChild(this._arrow);
|
||||
|
||||
this._treeContainer = document.createElement('div');
|
||||
this._treeContainer.style.background = color.toString();
|
||||
this._treeContainer.style.paddingTop = '2px';
|
||||
this._treeContainer.style.boxShadow = `0px 5px 8px ${(theme.type === DARK ? color.darken(.6) : color.darken(.2))}`;
|
||||
this._domNode.appendChild(this._treeContainer);
|
||||
|
||||
const treeConifg = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined });
|
||||
this._tree = this._instantiationService.createInstance(
|
||||
HighlightingWorkbenchTree,
|
||||
this._treeContainer,
|
||||
treeConifg,
|
||||
{ useShadows: false },
|
||||
{ placeholder: localize('placeholder', "Find") }
|
||||
);
|
||||
this._disposables.push(this._tree.onDidChangeSelection(e => {
|
||||
if (e.payload !== this._tree) {
|
||||
const target = this._getTargetFromSelectionEvent(e);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
setTimeout(_ => {// need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click
|
||||
this._onDidPickElement.fire({ target, payload: e.payload });
|
||||
}, 0);
|
||||
}
|
||||
}));
|
||||
|
||||
this._domNode.focus();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this._disposables);
|
||||
this._onDidPickElement.dispose();
|
||||
this._tree.dispose();
|
||||
this._focus.dispose();
|
||||
}
|
||||
|
||||
setInput(input: any): void {
|
||||
let actualInput = this._getInput(input);
|
||||
this._tree.setInput(actualInput).then(() => {
|
||||
let selection = this._getInitialSelection(this._tree, input);
|
||||
if (selection) {
|
||||
this._tree.reveal(selection, .5).then(() => {
|
||||
this._tree.setSelection([selection], this._tree);
|
||||
this._tree.setFocus(selection);
|
||||
this._tree.domFocus();
|
||||
});
|
||||
} else {
|
||||
this._tree.focusFirst();
|
||||
this._tree.setSelection([this._tree.getFocus()], this._tree);
|
||||
this._tree.domFocus();
|
||||
}
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
|
||||
layout(height: number, width: number, arrowSize: number, arrowOffset: number) {
|
||||
this._domNode.style.height = `${height}px`;
|
||||
this._domNode.style.width = `${width}px`;
|
||||
this._arrow.style.borderWidth = `${arrowSize}px`;
|
||||
this._arrow.style.marginLeft = `${arrowOffset}px`;
|
||||
|
||||
this._treeContainer.style.height = `${height - 2 * arrowSize}px`;
|
||||
this._treeContainer.style.width = `${width}px`;
|
||||
this._tree.layout();
|
||||
}
|
||||
|
||||
protected abstract _getInput(input: BreadcrumbElement): any;
|
||||
protected abstract _getInitialSelection(tree: ITree, input: BreadcrumbElement): any;
|
||||
protected abstract _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration;
|
||||
protected abstract _getTargetFromSelectionEvent(e: ISelectionEvent): any | undefined;
|
||||
}
|
||||
|
||||
//#region - Files
|
||||
|
||||
export class FileDataSource implements IDataSource {
|
||||
|
||||
private readonly _parents = new WeakMap<object, IWorkspaceFolder | IFileStat>();
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
) { }
|
||||
|
||||
getId(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): string {
|
||||
if (URI.isUri(element)) {
|
||||
return element.toString();
|
||||
} else if (IWorkspace.isIWorkspace(element)) {
|
||||
return element.id;
|
||||
} else if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
|
||||
return element.uri.toString();
|
||||
} else {
|
||||
return element.resource.toString();
|
||||
}
|
||||
}
|
||||
|
||||
hasChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): boolean {
|
||||
return URI.isUri(element) || IWorkspace.isIWorkspace(element) || IWorkspaceFolder.isIWorkspaceFolder(element) || element.isDirectory;
|
||||
}
|
||||
|
||||
getChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): TPromise<IWorkspaceFolder[] | IFileStat[]> {
|
||||
if (IWorkspace.isIWorkspace(element)) {
|
||||
return TPromise.as(element.folders).then(folders => {
|
||||
for (let child of folders) {
|
||||
this._parents.set(element, child);
|
||||
}
|
||||
return folders;
|
||||
});
|
||||
}
|
||||
let uri: URI;
|
||||
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
|
||||
uri = element.uri;
|
||||
} else if (URI.isUri(element)) {
|
||||
uri = element;
|
||||
} else {
|
||||
uri = element.resource;
|
||||
}
|
||||
return this._fileService.resolveFile(uri).then(stat => {
|
||||
for (let child of stat.children) {
|
||||
this._parents.set(stat, child);
|
||||
}
|
||||
return stat.children;
|
||||
});
|
||||
}
|
||||
|
||||
getParent(tree: ITree, element: IWorkspace | URI | IWorkspaceFolder | IFileStat): TPromise<IWorkspaceFolder | IFileStat> {
|
||||
return TPromise.as(this._parents.get(element));
|
||||
}
|
||||
}
|
||||
|
||||
export class FileRenderer implements IRenderer, IHighlightingRenderer {
|
||||
|
||||
private readonly _scores = new Map<string, FuzzyScore>();
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IConfigurationService private readonly _configService: IConfigurationService,
|
||||
) { }
|
||||
|
||||
getHeight(tree: ITree, element: any): number {
|
||||
return 22;
|
||||
}
|
||||
|
||||
getTemplateId(tree: ITree, element: any): string {
|
||||
return 'FileStat';
|
||||
}
|
||||
|
||||
renderTemplate(tree: ITree, templateId: string, container: HTMLElement) {
|
||||
return this._instantiationService.createInstance(FileLabel, container, { supportHighlights: true });
|
||||
}
|
||||
|
||||
renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: FileLabel): void {
|
||||
let fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations');
|
||||
let resource: URI;
|
||||
let fileKind: FileKind;
|
||||
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
|
||||
resource = element.uri;
|
||||
fileKind = FileKind.ROOT_FOLDER;
|
||||
} else {
|
||||
resource = element.resource;
|
||||
fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE;
|
||||
}
|
||||
templateData.setFile(resource, {
|
||||
fileKind,
|
||||
hidePath: true,
|
||||
fileDecorations: fileDecorations,
|
||||
matches: createMatches((this._scores.get(resource.toString()) || [, []])[1])
|
||||
});
|
||||
}
|
||||
|
||||
disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void {
|
||||
templateData.dispose();
|
||||
}
|
||||
|
||||
updateHighlights(tree: ITree, pattern: string): any {
|
||||
let nav = tree.getNavigator(undefined, false);
|
||||
let topScore: FuzzyScore;
|
||||
let topElement: any;
|
||||
while (nav.next()) {
|
||||
let element = nav.current() as IFileStat | IWorkspaceFolder;
|
||||
let score = fuzzyScore(pattern, element.name, undefined, true);
|
||||
this._scores.set(IWorkspaceFolder.isIWorkspaceFolder(element) ? element.uri.toString() : element.resource.toString(), score);
|
||||
if (!topScore || score && topScore[0] < score[0]) {
|
||||
topScore = score;
|
||||
topElement = element;
|
||||
}
|
||||
}
|
||||
return topElement;
|
||||
}
|
||||
}
|
||||
|
||||
export class FileSorter implements ISorter {
|
||||
compare(tree: ITree, a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number {
|
||||
if (IWorkspaceFolder.isIWorkspaceFolder(a) && IWorkspaceFolder.isIWorkspaceFolder(b)) {
|
||||
return a.index - b.index;
|
||||
} else {
|
||||
if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) {
|
||||
// same type -> compare on names
|
||||
return compareFileNames(a.name, b.name);
|
||||
} else if ((a as IFileStat).isDirectory) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
||||
|
||||
constructor(
|
||||
parent: HTMLElement,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService,
|
||||
) {
|
||||
super(parent, instantiationService, themeService);
|
||||
}
|
||||
|
||||
protected _getInput(input: BreadcrumbElement): any {
|
||||
let { uri, kind } = (input as FileElement);
|
||||
if (kind === FileKind.ROOT_FOLDER) {
|
||||
return this._workspaceService.getWorkspace();
|
||||
} else {
|
||||
return dirname(uri);
|
||||
}
|
||||
}
|
||||
|
||||
protected _getInitialSelection(tree: ITree, input: BreadcrumbElement): any {
|
||||
let { uri } = (input as FileElement);
|
||||
let nav = tree.getNavigator();
|
||||
while (nav.next()) {
|
||||
let cur = nav.current();
|
||||
let candidate = IWorkspaceFolder.isIWorkspaceFolder(cur) ? cur.uri : (cur as IFileStat).resource;
|
||||
if (isEqual(uri, candidate)) {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
|
||||
// todo@joh reuse explorer implementations?
|
||||
config.dataSource = this._instantiationService.createInstance(FileDataSource);
|
||||
config.renderer = this._instantiationService.createInstance(FileRenderer);
|
||||
config.sorter = new FileSorter();
|
||||
return config;
|
||||
}
|
||||
|
||||
protected _getTargetFromSelectionEvent(e: ISelectionEvent): any | undefined {
|
||||
let [first] = e.selection;
|
||||
if (first && !IWorkspaceFolder.isIWorkspaceFolder(first) && !(first as IFileStat).isDirectory) {
|
||||
return new FileElement((first as IFileStat).resource, FileKind.FILE);
|
||||
}
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region - Symbols
|
||||
|
||||
class HighlightingOutlineRenderer extends OutlineRenderer implements IHighlightingRenderer {
|
||||
|
||||
updateHighlights(tree: ITree, pattern: string): any {
|
||||
let model = OutlineModel.get(tree.getInput());
|
||||
return model.updateMatches(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
|
||||
protected _getInput(input: BreadcrumbElement): any {
|
||||
let element = input as TreeElement;
|
||||
let model = OutlineModel.get(element);
|
||||
model.updateMatches('');
|
||||
return model;
|
||||
}
|
||||
|
||||
protected _getInitialSelection(_tree: ITree, input: BreadcrumbElement): any {
|
||||
return input instanceof OutlineModel ? undefined : input;
|
||||
}
|
||||
|
||||
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
|
||||
config.dataSource = this._instantiationService.createInstance(OutlineDataSource);
|
||||
config.renderer = this._instantiationService.createInstance(HighlightingOutlineRenderer);
|
||||
config.sorter = new OutlineItemComparator();
|
||||
return config;
|
||||
}
|
||||
|
||||
protected _getTargetFromSelectionEvent(e: ISelectionEvent): any | undefined {
|
||||
if (e.payload && e.payload.didClickOnTwistie) {
|
||||
return;
|
||||
}
|
||||
let [first] = e.selection;
|
||||
if (first instanceof OutlineElement) {
|
||||
return first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@@ -28,19 +28,25 @@ import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import {
|
||||
CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, JoinTwoGroupsAction, OpenToSideAction, RevertAndCloseEditorAction,
|
||||
NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction, EvenGroupWidthsAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup, ShowEditorsInGroupOneAction,
|
||||
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, NavigateLastAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction,
|
||||
OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, ClearEditorHistoryAction, ShowEditorsInGroupTwoAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup, OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction,
|
||||
ShowEditorsInGroupThreeAction, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction, MoveEditorToNextGroupAction, MoveEditorToFirstGroupAction, MoveEditorToSecondGroupAction, MoveEditorToThirdGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction, OpenLastEditorInGroup
|
||||
CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, JoinTwoGroupsAction, OpenToSideFromQuickOpenAction, RevertAndCloseEditorAction,
|
||||
NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, ResetGroupSizesAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup,
|
||||
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, NavigateLastAction, ReopenClosedEditorAction,
|
||||
OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, ClearEditorHistoryAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup,
|
||||
OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction,
|
||||
MoveEditorToNextGroupAction, MoveEditorToFirstGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction, OpenLastEditorInGroup,
|
||||
ShowEditorsInActiveGroupAction, MoveEditorToLastGroupAction, OpenFirstEditorInGroup, MoveGroupUpAction, MoveGroupDownAction, FocusLastGroupAction, SplitEditorLeftAction, SplitEditorRightAction,
|
||||
SplitEditorUpAction, SplitEditorDownAction, MoveEditorToLeftGroupAction, MoveEditorToRightGroupAction, MoveEditorToAboveGroupAction, MoveEditorToBelowGroupAction, CloseAllEditorGroupsAction,
|
||||
JoinAllGroupsAction, FocusLeftGroup, FocusAboveGroup, FocusRightGroup, FocusBelowGroup, EditorLayoutSingleAction, EditorLayoutTwoColumnsAction, EditorLayoutThreeColumnsAction, EditorLayoutTwoByTwoGridAction,
|
||||
EditorLayoutTwoRowsAction, EditorLayoutThreeRowsAction, EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoRowsRightAction, NewEditorGroupLeftAction, NewEditorGroupRightAction,
|
||||
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction
|
||||
} from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { getQuickNavigateHandler, inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { GroupOnePicker, GroupTwoPicker, GroupThreePicker, AllEditorsPicker } from 'vs/workbench/browser/parts/editor/editorPicker';
|
||||
import { AllEditorsPicker, ActiveEditorGroupPicker } from 'vs/workbench/browser/parts/editor/editorPicker';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
// Register String Editor
|
||||
@@ -103,10 +109,9 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
|
||||
|
||||
constructor(
|
||||
@ITextFileService private textFileService: ITextFileService
|
||||
) {
|
||||
}
|
||||
) { }
|
||||
|
||||
public serialize(editorInput: EditorInput): string {
|
||||
serialize(editorInput: EditorInput): string {
|
||||
if (!this.textFileService.isHotExitEnabled) {
|
||||
return null; // never restore untitled unless hot exit is enabled
|
||||
}
|
||||
@@ -120,7 +125,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
|
||||
|
||||
let resource = untitledEditorInput.getResource();
|
||||
if (untitledEditorInput.hasAssociatedFilePath) {
|
||||
resource = URI.file(resource.fsPath); // untitled with associated file path use the file schema
|
||||
resource = resource.with({ scheme: Schemas.file }); // untitled with associated file path use the file schema
|
||||
}
|
||||
|
||||
const serialized: ISerializedUntitledEditorInput = {
|
||||
@@ -133,7 +138,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
|
||||
return JSON.stringify(serialized);
|
||||
}
|
||||
|
||||
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledEditorInput {
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledEditorInput {
|
||||
return instantiationService.invokeFunction<UntitledEditorInput>(accessor => {
|
||||
const deserialized: ISerializedUntitledEditorInput = JSON.parse(serializedEditorInput);
|
||||
const resource = !!deserialized.resourceJSON ? URI.revive(deserialized.resourceJSON) : URI.parse(deserialized.resource);
|
||||
@@ -141,7 +146,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
|
||||
const language = deserialized.modeId;
|
||||
const encoding = deserialized.encoding;
|
||||
|
||||
return accessor.get(IWorkbenchEditorService).createInput({ resource, filePath, language, encoding }) as UntitledEditorInput;
|
||||
return accessor.get(IEditorService).createInput({ resource, filePath, language, encoding }) as UntitledEditorInput;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -162,7 +167,7 @@ interface ISerializedSideBySideEditorInput {
|
||||
// Register Side by Side Editor Input Factory
|
||||
class SideBySideEditorInputFactory implements IEditorInputFactory {
|
||||
|
||||
public serialize(editorInput: EditorInput): string {
|
||||
serialize(editorInput: EditorInput): string {
|
||||
const input = <SideBySideEditorInput>editorInput;
|
||||
|
||||
if (input.details && input.master) {
|
||||
@@ -190,7 +195,7 @@ class SideBySideEditorInputFactory implements IEditorInputFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
|
||||
const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput);
|
||||
|
||||
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
|
||||
@@ -223,25 +228,25 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEOLAction, Chang
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL), 'Change File Encoding');
|
||||
|
||||
export class QuickOpenActionContributor extends ActionBarContributor {
|
||||
private openToSideActionInstance: OpenToSideAction;
|
||||
private openToSideActionInstance: OpenToSideFromQuickOpenAction;
|
||||
|
||||
constructor(@IInstantiationService private instantiationService: IInstantiationService) {
|
||||
super();
|
||||
}
|
||||
|
||||
public hasActions(context: any): boolean {
|
||||
hasActions(context: any): boolean {
|
||||
const entry = this.getEntry(context);
|
||||
|
||||
return !!entry;
|
||||
}
|
||||
|
||||
public getActions(context: any): IAction[] {
|
||||
getActions(context: any): IAction[] {
|
||||
const actions: Action[] = [];
|
||||
|
||||
const entry = this.getEntry(context);
|
||||
if (entry) {
|
||||
if (!this.openToSideActionInstance) {
|
||||
this.openToSideActionInstance = this.instantiationService.createInstance(OpenToSideAction);
|
||||
this.openToSideActionInstance = this.instantiationService.createInstance(OpenToSideFromQuickOpenAction);
|
||||
} else {
|
||||
this.openToSideActionInstance.updateClass();
|
||||
}
|
||||
@@ -269,50 +274,20 @@ const editorPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExp
|
||||
|
||||
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
|
||||
new QuickOpenHandlerDescriptor(
|
||||
GroupOnePicker,
|
||||
GroupOnePicker.ID,
|
||||
editorCommands.NAVIGATE_IN_GROUP_ONE_PREFIX,
|
||||
ActiveEditorGroupPicker,
|
||||
ActiveEditorGroupPicker.ID,
|
||||
editorCommands.NAVIGATE_IN_ACTIVE_GROUP_PREFIX,
|
||||
editorPickerContextKey,
|
||||
[
|
||||
{
|
||||
prefix: editorCommands.NAVIGATE_IN_GROUP_ONE_PREFIX,
|
||||
prefix: editorCommands.NAVIGATE_IN_ACTIVE_GROUP_PREFIX,
|
||||
needsEditor: false,
|
||||
description: nls.localize('groupOnePicker', "Show Editors in First Group")
|
||||
},
|
||||
{
|
||||
prefix: editorCommands.NAVIGATE_IN_GROUP_TWO_PREFIX,
|
||||
needsEditor: false,
|
||||
description: nls.localize('groupTwoPicker', "Show Editors in Second Group")
|
||||
},
|
||||
{
|
||||
prefix: editorCommands.NAVIGATE_IN_GROUP_THREE_PREFIX,
|
||||
needsEditor: false,
|
||||
description: nls.localize('groupThreePicker', "Show Editors in Third Group")
|
||||
description: nls.localize('groupOnePicker', "Show Editors in Active Group")
|
||||
}
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
|
||||
new QuickOpenHandlerDescriptor(
|
||||
GroupTwoPicker,
|
||||
GroupTwoPicker.ID,
|
||||
editorCommands.NAVIGATE_IN_GROUP_TWO_PREFIX,
|
||||
editorPickerContextKey,
|
||||
[]
|
||||
)
|
||||
);
|
||||
|
||||
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
|
||||
new QuickOpenHandlerDescriptor(
|
||||
GroupThreePicker,
|
||||
GroupThreePicker.ID,
|
||||
editorCommands.NAVIGATE_IN_GROUP_THREE_PREFIX,
|
||||
editorPickerContextKey,
|
||||
[]
|
||||
)
|
||||
);
|
||||
|
||||
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
|
||||
new QuickOpenHandlerDescriptor(
|
||||
AllEditorsPicker,
|
||||
@@ -333,48 +308,73 @@ Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpen
|
||||
const category = nls.localize('view', "View");
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditorInGroup, OpenNextEditorInGroup.ID, OpenNextEditorInGroup.LABEL), 'View: Open Next Editor in Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorInGroup, OpenPreviousEditorInGroup.ID, OpenPreviousEditorInGroup.LABEL), 'View: Open Previous Editor in Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLastEditorInGroup, OpenLastEditorInGroup.ID, OpenLastEditorInGroup.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_9 }), 'View: Open Last Editor in Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLastEditorInGroup, OpenLastEditorInGroup.ID, OpenLastEditorInGroup.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9], mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9] } }), 'View: Open Last Editor in Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFirstEditorInGroup, OpenFirstEditorInGroup.ID, OpenFirstEditorInGroup.LABEL), 'View: Open First Editor in Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorAction, OpenNextRecentlyUsedEditorAction.ID, OpenNextRecentlyUsedEditorAction.LABEL), 'View: Open Next Recently Used Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction.ID, OpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Open Previous Recently Used Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllEditorsAction, ShowAllEditorsAction.ID, ShowAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_P), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupOneAction, ShowEditorsInGroupOneAction.ID, ShowEditorsInGroupOneAction.LABEL), 'View: Show Editors in First Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupTwoAction, ShowEditorsInGroupTwoAction.ID, ShowEditorsInGroupTwoAction.LABEL), 'View: Show Editors in Second Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupThreeAction, ShowEditorsInGroupThreeAction.ID, ShowEditorsInGroupThreeAction.LABEL), 'View: Show Editors in Third Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInActiveGroupAction, ShowEditorsInActiveGroupAction.ID, ShowEditorsInActiveGroupAction.LABEL), 'View: Show Editors in Active Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditor, OpenNextEditor.ID, OpenNextEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET] } }), 'View: Open Next Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditor, OpenPreviousEditor.ID, OpenPreviousEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET] } }), 'View: Open Previous Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearRecentFilesAction, ClearRecentFilesAction.ID, ClearRecentFilesAction.LABEL), 'File: Clear Recently Opened', nls.localize('file', "File"));
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'View: Close All Editors', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors to the Left', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorGroupsAction, CloseAllEditorGroupsAction.ID, CloseAllEditorGroupsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W) }), 'View: Close All Editor Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors in Group to the Left', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorsInOtherGroupsAction, CloseEditorsInOtherGroupsAction.ID, CloseEditorsInOtherGroupsAction.LABEL), 'View: Close Editors in Other Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH }), 'View: Split Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorOrthogonalAction, SplitEditorOrthogonalAction.ID, SplitEditorOrthogonalAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_BACKSLASH) }), 'View: Split Editor Orthogonal', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorLeftAction, SplitEditorLeftAction.ID, SplitEditorLeftAction.LABEL), 'View: Split Editor Left', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorRightAction, SplitEditorRightAction.ID, SplitEditorRightAction.LABEL), 'View: Split Editor Right', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorUpAction, SplitEditorUpAction.ID, SplitEditorUpAction.LABEL), 'Split Editor Up', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorDownAction, SplitEditorDownAction.ID, SplitEditorDownAction.LABEL), 'View: Split Editor Down', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(JoinTwoGroupsAction, JoinTwoGroupsAction.ID, JoinTwoGroupsAction.LABEL), 'View: Join Editors of Two Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(JoinAllGroupsAction, JoinAllGroupsAction.ID, JoinAllGroupsAction.LABEL), 'View: Join Editors of All Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBetweenGroupsAction, NavigateBetweenGroupsAction.ID, NavigateBetweenGroupsAction.LABEL), 'View: Navigate Between Editor Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveGroupAction, FocusActiveGroupAction.ID, FocusActiveGroupAction.LABEL), 'View: Focus Active Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSecondGroupAction, FocusSecondGroupAction.ID, FocusSecondGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_2 }), 'View: Focus Second Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusThirdGroupAction, FocusThirdGroupAction.ID, FocusThirdGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_3 }), 'View: Focus Third Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLastEditorInStackAction, FocusLastEditorInStackAction.ID, FocusLastEditorInStackAction.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_0 } }), 'View: Open Last Editor in Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EvenGroupWidthsAction, EvenGroupWidthsAction.ID, EvenGroupWidthsAction.LABEL), 'View: Even Editor Group Widths', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ResetGroupSizesAction, ResetGroupSizesAction.ID, ResetGroupSizesAction.LABEL), 'View: Reset Editor Group Sizes', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Sidebar', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Minimize Other Editor Groups', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Maximize Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorLeftInGroupAction, MoveEditorLeftInGroupAction.ID, MoveEditorLeftInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorRightInGroupAction, MoveEditorRightInGroupAction.ID, MoveEditorRightInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupLeftAction, MoveGroupLeftAction.ID, MoveGroupLeftAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.LeftArrow) }), 'View: Move Editor Group Left', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupRightAction, MoveGroupRightAction.ID, MoveGroupRightAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.RightArrow) }), 'View: Move Editor Group Right', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupUpAction, MoveGroupUpAction.ID, MoveGroupUpAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.UpArrow) }), 'View: Move Editor Group Up', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupDownAction, MoveGroupDownAction.ID, MoveGroupDownAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.DownArrow) }), 'View: Move Editor Group Down', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToPreviousGroupAction, MoveEditorToPreviousGroupAction.ID, MoveEditorToPreviousGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToNextGroupAction, MoveEditorToNextGroupAction.ID, MoveEditorToNextGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToFirstGroupAction, MoveEditorToFirstGroupAction.ID, MoveEditorToFirstGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_1, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_1 } }), 'View: Move Editor into First Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToSecondGroupAction, MoveEditorToSecondGroupAction.ID, MoveEditorToSecondGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_2, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_2 } }), 'View: Move Editor into Second Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToThirdGroupAction, MoveEditorToThirdGroupAction.ID, MoveEditorToThirdGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_3, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_3 } }), 'View: Move Editor into Third Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Previous Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Next Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToLastGroupAction, MoveEditorToLastGroupAction.ID, MoveEditorToLastGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_9, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_9 } }), 'View: Move Editor into Last Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToLeftGroupAction, MoveEditorToLeftGroupAction.ID, MoveEditorToLeftGroupAction.LABEL), 'View: Move Editor into Left Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToRightGroupAction, MoveEditorToRightGroupAction.ID, MoveEditorToRightGroupAction.LABEL), 'View: Move Editor into Right Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToAboveGroupAction, MoveEditorToAboveGroupAction.ID, MoveEditorToAboveGroupAction.LABEL), 'View: Move Editor into Above Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToBelowGroupAction, MoveEditorToBelowGroupAction.ID, MoveEditorToBelowGroupAction.LABEL), 'View: Move Editor into Below Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveGroupAction, FocusActiveGroupAction.ID, FocusActiveGroupAction.LABEL), 'View: Focus Active Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLastGroupAction, FocusLastGroupAction.ID, FocusLastGroupAction.LABEL), 'View: Focus Last Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL), 'View: Focus Previous Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL), 'View: Focus Next Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLeftGroup, FocusLeftGroup.ID, FocusLeftGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Left Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusRightGroup, FocusRightGroup.ID, FocusRightGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Right Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusAboveGroup, FocusAboveGroup.ID, FocusAboveGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.UpArrow) }), 'View: Focus Above Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusBelowGroup, FocusBelowGroup.ID, FocusBelowGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.DownArrow) }), 'View: Focus Below Editor Group', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupLeftAction, NewEditorGroupLeftAction.ID, NewEditorGroupLeftAction.LABEL), 'View: New Editor Group to the Left', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupRightAction, NewEditorGroupRightAction.ID, NewEditorGroupRightAction.LABEL), 'View: New Editor Group to the Right', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupAboveAction, NewEditorGroupAboveAction.ID, NewEditorGroupAboveAction.LABEL), 'View: New Editor Group Above', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupBelowAction, NewEditorGroupBelowAction.ID, NewEditorGroupBelowAction.LABEL), 'View: New Editor Group Below', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBackwardsAction, NavigateBackwardsAction.ID, NavigateBackwardsAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }), 'Go Back');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLastAction, NavigateLastAction.ID, NavigateLastAction.LABEL), 'Go Last');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorFromHistoryAction, OpenPreviousEditorFromHistoryAction.ID, OpenPreviousEditorFromHistoryAction.LABEL), 'Open Previous Editor from History');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearEditorHistoryAction, ClearEditorHistoryAction.ID, ClearEditorHistoryAction.LABEL), 'Clear Editor History');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(RevertAndCloseEditorAction, RevertAndCloseEditorAction.ID, RevertAndCloseEditorAction.LABEL), 'View: Revert and Close Editor', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutSingleAction, EditorLayoutSingleAction.ID, EditorLayoutSingleAction.LABEL), 'View: Single Column Editor Layout', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoColumnsAction, EditorLayoutTwoColumnsAction.ID, EditorLayoutTwoColumnsAction.LABEL), 'View: Two Columns Editor Layout', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutThreeColumnsAction, EditorLayoutThreeColumnsAction.ID, EditorLayoutThreeColumnsAction.LABEL), 'View: Three Columns Editor Layout', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoRowsAction, EditorLayoutTwoRowsAction.ID, EditorLayoutTwoRowsAction.LABEL), 'View: Two Rows Editor Layout', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutThreeRowsAction, EditorLayoutThreeRowsAction.ID, EditorLayoutThreeRowsAction.LABEL), 'View: Three Rows Editor Layout', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoByTwoGridAction, EditorLayoutTwoByTwoGridAction.ID, EditorLayoutTwoByTwoGridAction.LABEL), 'View: Grid Editor Layout (2x2)', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoRowsRightAction, EditorLayoutTwoRowsRightAction.ID, EditorLayoutTwoRowsRightAction.LABEL), 'View: Two Rows Right Editor Layout', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsBottomAction.ID, EditorLayoutTwoColumnsBottomAction.LABEL), 'View: Two Columns Bottom Editor Layout', category);
|
||||
|
||||
// Register Editor Picker Actions including quick navigate support
|
||||
const openNextEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } };
|
||||
@@ -385,7 +385,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUs
|
||||
const quickOpenNavigateNextInEditorPickerId = 'workbench.action.quickOpenNavigateNextInEditorPicker';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: quickOpenNavigateNextInEditorPickerId,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
handler: getQuickNavigateHandler(quickOpenNavigateNextInEditorPickerId, true),
|
||||
when: editorPickerContext,
|
||||
primary: openNextEditorKeybinding.primary,
|
||||
@@ -395,30 +395,36 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
const quickOpenNavigatePreviousInEditorPickerId = 'workbench.action.quickOpenNavigatePreviousInEditorPicker';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: quickOpenNavigatePreviousInEditorPickerId,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
handler: getQuickNavigateHandler(quickOpenNavigatePreviousInEditorPickerId, false),
|
||||
when: editorPickerContext,
|
||||
primary: openPreviousEditorKeybinding.primary,
|
||||
mac: openPreviousEditorKeybinding.mac
|
||||
});
|
||||
|
||||
|
||||
// Editor Commands
|
||||
editorCommands.setup();
|
||||
|
||||
// Touch Bar
|
||||
if (isMacintosh) {
|
||||
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
|
||||
command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, iconPath: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')).fsPath } },
|
||||
command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')) } },
|
||||
group: 'navigation'
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
|
||||
command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, iconPath: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')).fsPath } },
|
||||
command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')) } },
|
||||
group: 'navigation'
|
||||
});
|
||||
}
|
||||
|
||||
// Empty Editor Group Context Menu
|
||||
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_UP, title: nls.localize('splitUp', "Split Up") }, group: '2_split', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitDown', "Split Down") }, group: '2_split', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_LEFT, title: nls.localize('splitLeft', "Split Left") }, group: '2_split', order: 30 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '2_split', order: 40 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: editorCommands.CLOSE_EDITOR_GROUP_COMMAND_ID, title: nls.localize('close', "Close") }, group: '3_close', order: 10, when: ContextKeyExpr.has('multipleEditorGroups') });
|
||||
|
||||
// Editor Title Context Menu
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close") }, group: '1_close', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOthers', "Close Others") }, group: '1_close', order: 20 });
|
||||
@@ -426,6 +432,10 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCo
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '1_close', order: 40 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '1_close', order: 50 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepOpen', "Keep Open") }, group: '3_preview', order: 10, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_UP, title: nls.localize('splitUp', "Split Up") }, group: '5_split', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitDown', "Split Down") }, group: '5_split', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_LEFT, title: nls.localize('splitLeft', "Split Left") }, group: '5_split', order: 30 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '5_split', order: 40 });
|
||||
|
||||
// Editor Title Menu
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_INLINE_MODE, title: nls.localize('toggleInlineView', "Toggle Inline View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') });
|
||||
@@ -433,9 +443,236 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
|
||||
|
||||
interface IEditorToolItem { id: string; title: string; iconDark: string; iconLight: string; }
|
||||
|
||||
function appendEditorToolItem(primary: IEditorToolItem, alternative: IEditorToolItem, when: ContextKeyExpr, order: number): void {
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: primary.id,
|
||||
title: primary.title,
|
||||
iconLocation: {
|
||||
dark: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${primary.iconDark}`)),
|
||||
light: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${primary.iconLight}`))
|
||||
}
|
||||
},
|
||||
alt: {
|
||||
id: alternative.id,
|
||||
title: alternative.title,
|
||||
iconLocation: {
|
||||
dark: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${alternative.iconDark}`)),
|
||||
light: URI.parse(require.toUrl(`vs/workbench/browser/parts/editor/media/${alternative.iconLight}`))
|
||||
}
|
||||
},
|
||||
group: 'navigation',
|
||||
when,
|
||||
order
|
||||
});
|
||||
}
|
||||
|
||||
// Editor Title Menu: Split Editor
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: SplitEditorAction.ID,
|
||||
title: nls.localize('splitEditorRight', "Split Editor Right"),
|
||||
iconDark: 'split-editor-horizontal-inverse.svg',
|
||||
iconLight: 'split-editor-horizontal.svg'
|
||||
}, {
|
||||
id: editorCommands.SPLIT_EDITOR_DOWN,
|
||||
title: nls.localize('splitEditorDown', "Split Editor Down"),
|
||||
iconDark: 'split-editor-vertical-inverse.svg',
|
||||
iconLight: 'split-editor-vertical.svg'
|
||||
},
|
||||
ContextKeyExpr.not('splitEditorsVertically'),
|
||||
100000 /* towards the end */
|
||||
);
|
||||
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: SplitEditorAction.ID,
|
||||
title: nls.localize('splitEditorDown', "Split Editor Down"),
|
||||
iconDark: 'split-editor-vertical-inverse.svg',
|
||||
iconLight: 'split-editor-vertical.svg'
|
||||
}, {
|
||||
id: editorCommands.SPLIT_EDITOR_RIGHT,
|
||||
title: nls.localize('splitEditorRight', "Split Editor Right"),
|
||||
iconDark: 'split-editor-horizontal-inverse.svg',
|
||||
iconLight: 'split-editor-horizontal.svg'
|
||||
},
|
||||
ContextKeyExpr.has('splitEditorsVertically'),
|
||||
100000 // towards the end
|
||||
);
|
||||
|
||||
// Editor Title Menu: Close Group (tabs disabled)
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
|
||||
title: nls.localize('close', "Close"),
|
||||
iconDark: 'close-big-inverse-alt.svg',
|
||||
iconLight: 'close-big-alt.svg'
|
||||
}, {
|
||||
id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
title: nls.localize('closeAll', "Close All"),
|
||||
iconDark: 'closeall-editors-inverse.svg',
|
||||
iconLight: 'closeall-editors.svg'
|
||||
},
|
||||
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.not('groupActiveEditorDirty')),
|
||||
1000000 // towards the end
|
||||
);
|
||||
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
|
||||
title: nls.localize('close', "Close"),
|
||||
iconDark: 'close-dirty-inverse-alt.svg',
|
||||
iconLight: 'close-dirty-alt.svg'
|
||||
}, {
|
||||
id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
title: nls.localize('closeAll', "Close All"),
|
||||
iconDark: 'closeall-editors-inverse.svg',
|
||||
iconLight: 'closeall-editors.svg'
|
||||
},
|
||||
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ContextKeyExpr.has('groupActiveEditorDirty')),
|
||||
1000000 // towards the end
|
||||
);
|
||||
|
||||
// Editor Commands for Command Palette
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepEditor', "Keep Editor"), category }, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeEditorsInGroup', "Close All Editors in Group"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeSavedEditors', "Close Saved Editors in Group"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOtherEditors', "Close Other Editors"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRightEditors', "Close Editors to the Right"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOtherEditors', "Close Other Editors in Group"), category } });
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRightEditors', "Close Editors to the Right in Group"), category } });
|
||||
|
||||
// File menu
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, {
|
||||
group: '1_editor',
|
||||
command: {
|
||||
id: ReopenClosedEditorAction.ID,
|
||||
title: nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, {
|
||||
group: 'z_clear',
|
||||
command: {
|
||||
id: ClearRecentFilesAction.ID,
|
||||
title: nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
// Layout menu
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
group: '2_appearance',
|
||||
title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"),
|
||||
submenu: MenuId.MenubarLayoutMenu,
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '1_split',
|
||||
command: {
|
||||
id: editorCommands.SPLIT_EDITOR_UP,
|
||||
title: nls.localize({ key: 'miSplitEditorUp', comment: ['&& denotes a mnemonic'] }, "Split &&Up")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '1_split',
|
||||
command: {
|
||||
id: editorCommands.SPLIT_EDITOR_DOWN,
|
||||
title: nls.localize({ key: 'miSplitEditorDown', comment: ['&& denotes a mnemonic'] }, "Split &&Down")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '1_split',
|
||||
command: {
|
||||
id: editorCommands.SPLIT_EDITOR_LEFT,
|
||||
title: nls.localize({ key: 'miSplitEditorLeft', comment: ['&& denotes a mnemonic'] }, "Split &&Left")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '1_split',
|
||||
command: {
|
||||
id: editorCommands.SPLIT_EDITOR_RIGHT,
|
||||
title: nls.localize({ key: 'miSplitEditorRight', comment: ['&& denotes a mnemonic'] }, "Split &&Right")
|
||||
},
|
||||
order: 4
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '2_layouts',
|
||||
command: {
|
||||
id: EditorLayoutSingleAction.ID,
|
||||
title: nls.localize({ key: 'miSingleColumnEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Single")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '2_layouts',
|
||||
command: {
|
||||
id: EditorLayoutTwoColumnsAction.ID,
|
||||
title: nls.localize({ key: 'miTwoColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Two Columns")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '2_layouts',
|
||||
command: {
|
||||
id: EditorLayoutThreeColumnsAction.ID,
|
||||
title: nls.localize({ key: 'miThreeColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&hree Columns")
|
||||
},
|
||||
order: 4
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '2_layouts',
|
||||
command: {
|
||||
id: EditorLayoutTwoRowsAction.ID,
|
||||
title: nls.localize({ key: 'miTwoRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&wo Rows")
|
||||
},
|
||||
order: 5
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '2_layouts',
|
||||
command: {
|
||||
id: EditorLayoutThreeRowsAction.ID,
|
||||
title: nls.localize({ key: 'miThreeRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "Three &&Rows")
|
||||
},
|
||||
order: 6
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '2_layouts',
|
||||
command: {
|
||||
id: EditorLayoutTwoByTwoGridAction.ID,
|
||||
title: nls.localize({ key: 'miTwoByTwoGridEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Grid (2x2)")
|
||||
},
|
||||
order: 7
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '2_layouts',
|
||||
command: {
|
||||
id: EditorLayoutTwoRowsRightAction.ID,
|
||||
title: nls.localize({ key: 'miTwoRowsRightEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two R&&ows Right")
|
||||
},
|
||||
order: 8
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, {
|
||||
group: '2_layouts',
|
||||
command: {
|
||||
id: EditorLayoutTwoColumnsBottomAction.ID,
|
||||
title: nls.localize({ key: 'miTwoColumnsBottomEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two &&Columns Bottom")
|
||||
},
|
||||
order: 9
|
||||
});
|
||||
|
||||
163
src/vs/workbench/browser/parts/editor/editor.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { GroupIdentifier, IWorkbenchEditorConfiguration, IWorkbenchEditorPartConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent } from 'vs/workbench/common/editor';
|
||||
import { EditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
|
||||
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
export const EDITOR_TITLE_HEIGHT = 35;
|
||||
|
||||
export const DEFAULT_EDITOR_MIN_DIMENSIONS = new Dimension(220, 70);
|
||||
export const DEFAULT_EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
|
||||
|
||||
export interface IEditorPartOptions extends IWorkbenchEditorPartConfiguration {
|
||||
iconTheme?: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
|
||||
showTabs: true,
|
||||
tabCloseButton: 'right',
|
||||
tabSizing: 'fit',
|
||||
showIcons: true,
|
||||
enablePreview: true,
|
||||
openPositioning: 'right',
|
||||
openSideBySideDirection: 'right',
|
||||
closeEmptyGroups: true,
|
||||
labelFormat: 'default',
|
||||
iconTheme: 'vs-seti'
|
||||
};
|
||||
|
||||
export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean {
|
||||
return event.affectsConfiguration('workbench.editor') || event.affectsConfiguration('workbench.iconTheme');
|
||||
}
|
||||
|
||||
export function getEditorPartOptions(config: IWorkbenchEditorConfiguration): IEditorPartOptions {
|
||||
const options: IEditorPartOptions = assign(Object.create(null), DEFAULT_EDITOR_PART_OPTIONS);
|
||||
|
||||
if (!config || !config.workbench) {
|
||||
return options;
|
||||
}
|
||||
|
||||
if (typeof config.workbench.iconTheme === 'string') {
|
||||
options.iconTheme = config.workbench.iconTheme;
|
||||
}
|
||||
|
||||
if (config.workbench.editor) {
|
||||
assign(options, config.workbench.editor);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
export interface IEditorPartOptionsChangeEvent {
|
||||
oldPartOptions: IEditorPartOptions;
|
||||
newPartOptions: IEditorPartOptions;
|
||||
}
|
||||
|
||||
export interface IEditorOpeningEvent extends IEditorIdentifier {
|
||||
options?: IEditorOptions;
|
||||
|
||||
/**
|
||||
* Allows to prevent the opening of an editor by providing a callback
|
||||
* that will be executed instead. By returning another editor promise
|
||||
* it is possible to override the opening with another editor. It is ok
|
||||
* to return a promise that resolves to NULL to prevent the opening
|
||||
* altogether.
|
||||
*/
|
||||
prevent(callback: () => Thenable<any>): void;
|
||||
}
|
||||
|
||||
export interface IEditorGroupsAccessor {
|
||||
readonly groups: IEditorGroupView[];
|
||||
readonly activeGroup: IEditorGroupView;
|
||||
|
||||
readonly partOptions: IEditorPartOptions;
|
||||
readonly onDidEditorPartOptionsChange: Event<IEditorPartOptionsChangeEvent>;
|
||||
|
||||
getGroup(identifier: GroupIdentifier): IEditorGroupView;
|
||||
getGroups(order: GroupsOrder): IEditorGroupView[];
|
||||
|
||||
activateGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView;
|
||||
|
||||
addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): IEditorGroupView;
|
||||
mergeGroup(group: IEditorGroupView | GroupIdentifier, target: IEditorGroupView | GroupIdentifier, options?: IMergeGroupOptions): IEditorGroupView;
|
||||
|
||||
moveGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView;
|
||||
copyGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView;
|
||||
|
||||
removeGroup(group: IEditorGroupView | GroupIdentifier): void;
|
||||
}
|
||||
|
||||
export interface IEditorGroupView extends IDisposable, ISerializableView, IEditorGroup {
|
||||
readonly group: EditorGroup;
|
||||
readonly whenRestored: TPromise<void>;
|
||||
readonly disposed: boolean;
|
||||
|
||||
readonly onDidFocus: Event<void>;
|
||||
readonly onWillDispose: Event<void>;
|
||||
readonly onWillOpenEditor: Event<IEditorOpeningEvent>;
|
||||
readonly onDidOpenEditorFail: Event<IEditorInput>;
|
||||
readonly onWillCloseEditor: Event<IEditorCloseEvent>;
|
||||
readonly onDidCloseEditor: Event<IEditorCloseEvent>;
|
||||
|
||||
isEmpty(): boolean;
|
||||
setActive(isActive: boolean): void;
|
||||
setLabel(label: string): void;
|
||||
relayout(): void;
|
||||
|
||||
shutdown(): void;
|
||||
}
|
||||
|
||||
export function getActiveTextEditorOptions(group: IEditorGroup, expectedActiveEditor?: IEditorInput, presetOptions?: EditorOptions): EditorOptions {
|
||||
const activeGroupCodeEditor = group.activeControl ? getCodeEditor(group.activeControl.getControl()) : void 0;
|
||||
if (activeGroupCodeEditor) {
|
||||
if (!expectedActiveEditor || expectedActiveEditor.matches(group.activeEditor)) {
|
||||
return TextEditorOptions.fromEditor(activeGroupCodeEditor, presetOptions);
|
||||
}
|
||||
}
|
||||
|
||||
return presetOptions || new EditorOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* A sub-interface of IEditorService to hide some workbench-core specific
|
||||
* events from clients.
|
||||
*/
|
||||
export interface EditorServiceImpl extends IEditorService {
|
||||
|
||||
/**
|
||||
* Emitted when an editor is closed.
|
||||
*/
|
||||
readonly onDidCloseEditor: Event<IEditorCloseEvent>;
|
||||
|
||||
/**
|
||||
* Emitted when an editor failed to open.
|
||||
*/
|
||||
readonly onDidOpenEditorFail: Event<IEditorIdentifier>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A sub-interface of IEditorGroupsService to hide some workbench-core specific
|
||||
* methods from clients.
|
||||
*/
|
||||
export interface EditorGroupsServiceImpl extends IEditorGroupsService {
|
||||
|
||||
/**
|
||||
* A promise that resolves when groups have been restored.
|
||||
*/
|
||||
readonly whenRestored: TPromise<void>;
|
||||
}
|
||||
@@ -6,14 +6,11 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ActiveEditorMoveArguments, ActiveEditorMovePositioning, ActiveEditorMovePositioningBy, EditorCommands, TextCompareEditorVisible, EditorInput, IEditorIdentifier, IEditorCommandsContext } from 'vs/workbench/common/editor';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditor, Position, POSITIONS, Direction, IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditor, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
|
||||
import { EditorStacksModel, EditorGroup } from 'vs/workbench/common/editor/editorStacksModel';
|
||||
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
@@ -22,26 +19,37 @@ import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
|
||||
export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
|
||||
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
|
||||
export const CLOSE_EDITORS_AND_GROUP_COMMAND_ID = 'workbench.action.closeEditorsAndGroup';
|
||||
export const CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID = 'workbench.action.closeEditorsToTheRight';
|
||||
export const CLOSE_EDITOR_COMMAND_ID = 'workbench.action.closeActiveEditor';
|
||||
export const CLOSE_EDITOR_GROUP_COMMAND_ID = 'workbench.action.closeGroup';
|
||||
export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOtherEditors';
|
||||
|
||||
export const MOVE_ACTIVE_EDITOR_COMMAND_ID = 'moveActiveEditor';
|
||||
export const LAYOUT_EDITOR_GROUPS_COMMAND_ID = 'layoutEditorGroups';
|
||||
export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
|
||||
export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup';
|
||||
export const TOGGLE_DIFF_INLINE_MODE = 'toggle.diff.editorMode';
|
||||
|
||||
export const NAVIGATE_IN_GROUP_ONE_PREFIX = 'edt one ';
|
||||
export const NAVIGATE_IN_GROUP_TWO_PREFIX = 'edt two ';
|
||||
export const NAVIGATE_IN_GROUP_THREE_PREFIX = 'edt three ';
|
||||
export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt ';
|
||||
export const SPLIT_EDITOR_UP = 'workbench.action.splitEditorUp';
|
||||
export const SPLIT_EDITOR_DOWN = 'workbench.action.splitEditorDown';
|
||||
export const SPLIT_EDITOR_LEFT = 'workbench.action.splitEditorLeft';
|
||||
export const SPLIT_EDITOR_RIGHT = 'workbench.action.splitEditorRight';
|
||||
|
||||
export function setup(): void {
|
||||
registerActiveEditorMoveCommand();
|
||||
registerDiffEditorCommands();
|
||||
registerOpenEditorAtIndexCommands();
|
||||
registerEditorCommands();
|
||||
export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt ';
|
||||
export const NAVIGATE_IN_ACTIVE_GROUP_PREFIX = 'edt active ';
|
||||
|
||||
export interface ActiveEditorMoveArguments {
|
||||
to?: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
|
||||
by?: 'tab' | 'group';
|
||||
value?: number;
|
||||
}
|
||||
|
||||
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
|
||||
@@ -49,17 +57,15 @@ const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean
|
||||
return false;
|
||||
}
|
||||
|
||||
const activeEditorMoveArg: ActiveEditorMoveArguments = arg;
|
||||
|
||||
if (!types.isString(activeEditorMoveArg.to)) {
|
||||
if (!types.isString(arg.to)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!types.isUndefined(activeEditorMoveArg.by) && !types.isString(activeEditorMoveArg.by)) {
|
||||
if (!types.isUndefined(arg.by) && !types.isString(arg.by)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!types.isUndefined(activeEditorMoveArg.value) && !types.isNumber(activeEditorMoveArg.value)) {
|
||||
if (!types.isUndefined(arg.value) && !types.isNumber(arg.value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -68,8 +74,8 @@ const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean
|
||||
|
||||
function registerActiveEditorMoveCommand(): void {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: EditorCommands.MoveActiveEditor,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
id: MOVE_ACTIVE_EDITOR_COMMAND_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: EditorContextKeys.editorTextFocus,
|
||||
primary: null,
|
||||
handler: (accessor, args: any) => moveActiveEditor(args, accessor),
|
||||
@@ -78,7 +84,7 @@ function registerActiveEditorMoveCommand(): void {
|
||||
args: [
|
||||
{
|
||||
name: nls.localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"),
|
||||
description: nls.localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move. By tab or by group.\n\t* 'value': Number value providing how many positions or an absolute position to move."),
|
||||
description: nls.localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."),
|
||||
constraint: isActiveEditorMoveArg
|
||||
}
|
||||
]
|
||||
@@ -86,98 +92,153 @@ function registerActiveEditorMoveCommand(): void {
|
||||
});
|
||||
}
|
||||
|
||||
function moveActiveEditor(args: ActiveEditorMoveArguments = {}, accessor: ServicesAccessor): void {
|
||||
args.to = args.to || ActiveEditorMovePositioning.RIGHT;
|
||||
args.by = args.by || ActiveEditorMovePositioningBy.TAB;
|
||||
args.value = types.isUndefined(args.value) ? 1 : args.value;
|
||||
function moveActiveEditor(args: ActiveEditorMoveArguments = Object.create(null), accessor: ServicesAccessor): void {
|
||||
args.to = args.to || 'right';
|
||||
args.by = args.by || 'tab';
|
||||
args.value = typeof args.value === 'number' ? args.value : 1;
|
||||
|
||||
const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor();
|
||||
if (activeEditor) {
|
||||
const activeControl = accessor.get(IEditorService).activeControl;
|
||||
if (activeControl) {
|
||||
switch (args.by) {
|
||||
case ActiveEditorMovePositioningBy.TAB:
|
||||
return moveActiveTab(args, activeEditor, accessor);
|
||||
case ActiveEditorMovePositioningBy.GROUP:
|
||||
return moveActiveEditorToGroup(args, activeEditor, accessor);
|
||||
case 'tab':
|
||||
return moveActiveTab(args, activeControl, accessor);
|
||||
case 'group':
|
||||
return moveActiveEditorToGroup(args, activeControl, accessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function moveActiveTab(args: ActiveEditorMoveArguments, activeEditor: IEditor, accessor: ServicesAccessor): void {
|
||||
const editorGroupsService: IEditorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorGroup = editorGroupsService.getStacksModel().groupAt(activeEditor.position);
|
||||
let index = editorGroup.indexOf(activeEditor.input);
|
||||
function moveActiveTab(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
|
||||
const group = control.group;
|
||||
let index = group.getIndexOfEditor(control.input);
|
||||
switch (args.to) {
|
||||
case ActiveEditorMovePositioning.FIRST:
|
||||
case 'first':
|
||||
index = 0;
|
||||
break;
|
||||
case ActiveEditorMovePositioning.LAST:
|
||||
index = editorGroup.count - 1;
|
||||
case 'last':
|
||||
index = group.count - 1;
|
||||
break;
|
||||
case ActiveEditorMovePositioning.LEFT:
|
||||
case 'left':
|
||||
index = index - args.value;
|
||||
break;
|
||||
case ActiveEditorMovePositioning.RIGHT:
|
||||
case 'right':
|
||||
index = index + args.value;
|
||||
break;
|
||||
case ActiveEditorMovePositioning.CENTER:
|
||||
index = Math.round(editorGroup.count / 2) - 1;
|
||||
case 'center':
|
||||
index = Math.round(group.count / 2) - 1;
|
||||
break;
|
||||
case ActiveEditorMovePositioning.POSITION:
|
||||
case 'position':
|
||||
index = args.value - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
index = index < 0 ? 0 : index >= editorGroup.count ? editorGroup.count - 1 : index;
|
||||
editorGroupsService.moveEditor(activeEditor.input, editorGroup, editorGroup, { index });
|
||||
index = index < 0 ? 0 : index >= group.count ? group.count - 1 : index;
|
||||
group.moveEditor(control.input, group, { index });
|
||||
}
|
||||
|
||||
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, activeEditor: IEditor, accessor: ServicesAccessor): void {
|
||||
let newPosition = activeEditor.position;
|
||||
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
const sourceGroup = control.group;
|
||||
let targetGroup: IEditorGroup;
|
||||
|
||||
switch (args.to) {
|
||||
case ActiveEditorMovePositioning.LEFT:
|
||||
newPosition = newPosition - 1;
|
||||
case 'left':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.LEFT }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.LEFT);
|
||||
}
|
||||
break;
|
||||
case ActiveEditorMovePositioning.RIGHT:
|
||||
newPosition = newPosition + 1;
|
||||
case 'right':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.RIGHT }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.RIGHT);
|
||||
}
|
||||
break;
|
||||
case ActiveEditorMovePositioning.FIRST:
|
||||
newPosition = Position.ONE;
|
||||
case 'up':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.UP }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.UP);
|
||||
}
|
||||
break;
|
||||
case ActiveEditorMovePositioning.LAST:
|
||||
newPosition = Position.THREE;
|
||||
case 'down':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.DOWN }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.DOWN);
|
||||
}
|
||||
break;
|
||||
case ActiveEditorMovePositioning.CENTER:
|
||||
newPosition = Position.TWO;
|
||||
case 'first':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.FIRST }, sourceGroup);
|
||||
break;
|
||||
case ActiveEditorMovePositioning.POSITION:
|
||||
newPosition = args.value - 1;
|
||||
case 'last':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.LAST }, sourceGroup);
|
||||
break;
|
||||
case 'previous':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.PREVIOUS }, sourceGroup);
|
||||
break;
|
||||
case 'next':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.NEXT }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, preferredSideBySideGroupDirection(configurationService));
|
||||
}
|
||||
break;
|
||||
case 'center':
|
||||
targetGroup = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)[(editorGroupService.count / 2) - 1];
|
||||
break;
|
||||
case 'position':
|
||||
targetGroup = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)[args.value - 1];
|
||||
break;
|
||||
}
|
||||
|
||||
newPosition = POSITIONS.indexOf(newPosition) !== -1 ? newPosition : activeEditor.position;
|
||||
accessor.get(IEditorGroupService).moveEditor(activeEditor.input, activeEditor.position, newPosition);
|
||||
if (targetGroup) {
|
||||
sourceGroup.moveEditor(control.input, targetGroup);
|
||||
targetGroup.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function registerEditorGroupsLayoutCommand(): void {
|
||||
CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => {
|
||||
if (!args || typeof args !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
editorGroupService.applyLayout(args);
|
||||
});
|
||||
}
|
||||
|
||||
export function mergeAllGroups(editorGroupService: IEditorGroupsService): void {
|
||||
const target = editorGroupService.activeGroup;
|
||||
editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).forEach(group => {
|
||||
if (group === target) {
|
||||
return; // keep target
|
||||
}
|
||||
|
||||
editorGroupService.mergeGroup(group, target);
|
||||
});
|
||||
}
|
||||
|
||||
function registerDiffEditorCommands(): void {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.compareEditor.nextChange',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: TextCompareEditorVisible,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: TextCompareEditorVisibleContext,
|
||||
primary: null,
|
||||
handler: accessor => navigateInDiffEditor(accessor, true)
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.compareEditor.previousChange',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
when: TextCompareEditorVisible,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: TextCompareEditorVisibleContext,
|
||||
primary: null,
|
||||
handler: accessor => navigateInDiffEditor(accessor, false)
|
||||
});
|
||||
|
||||
function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
|
||||
let editorService = accessor.get(IWorkbenchEditorService);
|
||||
const candidates = [editorService.getActiveEditor(), ...editorService.getVisibleEditors()].filter(e => e instanceof TextDiffEditor);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const candidates = [editorService.activeControl, ...editorService.visibleControls].filter(e => e instanceof TextDiffEditor);
|
||||
|
||||
if (candidates.length > 0) {
|
||||
next ? (<TextDiffEditor>candidates[0]).getDiffNavigator().next() : (<TextDiffEditor>candidates[0]).getDiffNavigator().previous();
|
||||
@@ -186,25 +247,17 @@ function registerDiffEditorCommands(): void {
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: TOGGLE_DIFF_INLINE_MODE,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
handler: (accessor, resource, context: IEditorCommandsContext) => {
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
let editor: IEditor;
|
||||
if (context) {
|
||||
const position = positionAndInput(editorGroupService, editorService, context).position;
|
||||
editor = editorService.getVisibleEditors()[position];
|
||||
} else {
|
||||
editor = editorService.getActiveEditor();
|
||||
}
|
||||
|
||||
if (editor instanceof TextDiffEditor) {
|
||||
const control = editor.getControl();
|
||||
const isInlineMode = !control.renderSideBySide;
|
||||
control.updateOptions(<IDiffEditorOptions>{
|
||||
const { control } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
|
||||
if (control instanceof TextDiffEditor) {
|
||||
const widget = control.getControl();
|
||||
const isInlineMode = !widget.renderSideBySide;
|
||||
widget.updateOptions(<IDiffEditorOptions>{
|
||||
renderSideBySide: isInlineMode
|
||||
});
|
||||
}
|
||||
@@ -221,19 +274,16 @@ function registerOpenEditorAtIndexCommands(): void {
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.openEditorAtIndex' + visibleIndex,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: KeyMod.Alt | toKeyCode(visibleIndex),
|
||||
mac: { primary: KeyMod.WinCtrl | toKeyCode(visibleIndex) },
|
||||
handler: accessor => {
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
|
||||
const active = editorService.getActiveEditor();
|
||||
if (active) {
|
||||
const group = editorGroupService.getStacksModel().groupAt(active.position);
|
||||
const editor = group.getEditor(editorIndex);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
|
||||
const activeControl = editorService.activeControl;
|
||||
if (activeControl) {
|
||||
const editor = activeControl.group.getEditor(editorIndex);
|
||||
if (editor) {
|
||||
return editorService.openEditor(editor).then(() => void 0);
|
||||
}
|
||||
@@ -262,169 +312,243 @@ function registerOpenEditorAtIndexCommands(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function registerEditorCommands() {
|
||||
function registerFocusEditorGroupAtIndexCommands(): void {
|
||||
|
||||
// Keybindings to focus a specific group (2-8) in the editor area
|
||||
for (let groupIndex = 1; groupIndex < 8; groupIndex++) {
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: toCommandId(groupIndex),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: KeyMod.CtrlCmd | toKeyCode(groupIndex),
|
||||
handler: accessor => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
// To keep backwards compatibility (pre-grid), allow to focus a group
|
||||
// that does not exist as long as it is the next group after the last
|
||||
// opened group. Otherwise we return.
|
||||
if (groupIndex > editorGroupService.count) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Group exists: just focus
|
||||
const groups = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE);
|
||||
if (groups[groupIndex]) {
|
||||
return groups[groupIndex].focus();
|
||||
}
|
||||
|
||||
// Group does not exist: create new by splitting the active one of the last group
|
||||
const direction = preferredSideBySideGroupDirection(configurationService);
|
||||
const lastGroup = editorGroupService.findGroup({ location: GroupLocation.LAST });
|
||||
const newGroup = editorGroupService.addGroup(lastGroup, direction);
|
||||
|
||||
// Focus
|
||||
newGroup.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toCommandId(index: number): string {
|
||||
switch (index) {
|
||||
case 1: return 'workbench.action.focusSecondEditorGroup';
|
||||
case 2: return 'workbench.action.focusThirdEditorGroup';
|
||||
case 3: return 'workbench.action.focusFourthEditorGroup';
|
||||
case 4: return 'workbench.action.focusFifthEditorGroup';
|
||||
case 5: return 'workbench.action.focusSixthEditorGroup';
|
||||
case 6: return 'workbench.action.focusSeventhEditorGroup';
|
||||
case 7: return 'workbench.action.focusEighthEditorGroup';
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
|
||||
function toKeyCode(index: number): KeyCode {
|
||||
switch (index) {
|
||||
case 1: return KeyCode.KEY_2;
|
||||
case 2: return KeyCode.KEY_3;
|
||||
case 3: return KeyCode.KEY_4;
|
||||
case 4: return KeyCode.KEY_5;
|
||||
case 5: return KeyCode.KEY_6;
|
||||
case 6: return KeyCode.KEY_7;
|
||||
case 7: return KeyCode.KEY_8;
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function splitEditor(editorGroupService: IEditorGroupsService, direction: GroupDirection, context?: IEditorCommandsContext): void {
|
||||
let sourceGroup: IEditorGroup;
|
||||
if (context && typeof context.groupId === 'number') {
|
||||
sourceGroup = editorGroupService.getGroup(context.groupId);
|
||||
} else {
|
||||
sourceGroup = editorGroupService.activeGroup;
|
||||
}
|
||||
|
||||
// Add group
|
||||
const newGroup = editorGroupService.addGroup(sourceGroup, direction);
|
||||
|
||||
// Split editor (if it can be split)
|
||||
let editorToCopy: IEditorInput;
|
||||
if (context && typeof context.editorIndex === 'number') {
|
||||
editorToCopy = sourceGroup.getEditor(context.editorIndex);
|
||||
} else {
|
||||
editorToCopy = sourceGroup.activeEditor;
|
||||
}
|
||||
|
||||
if (editorToCopy && (editorToCopy as EditorInput).supportsSplitEditor()) {
|
||||
sourceGroup.copyEditor(editorToCopy, newGroup);
|
||||
}
|
||||
|
||||
// Focus
|
||||
newGroup.focus();
|
||||
}
|
||||
|
||||
function registerSplitEditorCommands() {
|
||||
[
|
||||
{ id: SPLIT_EDITOR_UP, direction: GroupDirection.UP },
|
||||
{ id: SPLIT_EDITOR_DOWN, direction: GroupDirection.DOWN },
|
||||
{ id: SPLIT_EDITOR_LEFT, direction: GroupDirection.LEFT },
|
||||
{ id: SPLIT_EDITOR_RIGHT, direction: GroupDirection.RIGHT }
|
||||
].forEach(({ id, direction }) => {
|
||||
CommandsRegistry.registerCommand(id, function (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) {
|
||||
splitEditor(accessor.get(IEditorGroupsService), direction, getCommandsContext(resourceOrContext, context));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function registerCloseEditorCommands() {
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_SAVED_EDITORS_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
|
||||
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const model = editorGroupService.getStacksModel();
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
if (contexts.length === 0 && model.activeGroup) {
|
||||
// If command is triggered from the command palette use the active group
|
||||
contexts.push({ groupId: model.activeGroup.id });
|
||||
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
|
||||
|
||||
const activeGroup = editorGroupService.activeGroup;
|
||||
if (contexts.length === 0) {
|
||||
contexts.push({ groupId: activeGroup.id }); // active group as fallback
|
||||
}
|
||||
|
||||
let positionOne: { savedOnly: boolean } = void 0;
|
||||
let positionTwo: { savedOnly: boolean } = void 0;
|
||||
let positionThree: { savedOnly: boolean } = void 0;
|
||||
contexts.forEach(c => {
|
||||
switch (model.positionOfGroup(model.getGroup(c.groupId))) {
|
||||
case Position.ONE: positionOne = { savedOnly: true }; break;
|
||||
case Position.TWO: positionTwo = { savedOnly: true }; break;
|
||||
case Position.THREE: positionThree = { savedOnly: true }; break;
|
||||
}
|
||||
});
|
||||
|
||||
return editorService.closeEditors({ positionOne, positionTwo, positionThree });
|
||||
return TPromise.join(distinct(contexts.map(c => c.groupId)).map(groupId =>
|
||||
editorGroupService.getGroup(groupId).closeEditors({ savedOnly: true })
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
|
||||
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
|
||||
const distinctGroupIds = distinct(contexts.map(c => c.groupId));
|
||||
const model = editorGroupService.getStacksModel();
|
||||
|
||||
if (distinctGroupIds.length) {
|
||||
return editorService.closeEditors(distinctGroupIds.map(gid => model.positionOfGroup(model.getGroup(gid))));
|
||||
}
|
||||
const activeEditor = editorService.getActiveEditor();
|
||||
if (activeEditor) {
|
||||
return editorService.closeEditors(activeEditor.position);
|
||||
if (distinctGroupIds.length === 0) {
|
||||
distinctGroupIds.push(editorGroupService.activeGroup.id);
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
return TPromise.join(distinctGroupIds.map(groupId =>
|
||||
editorGroupService.getGroup(groupId).closeAllEditors()
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_EDITOR_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
|
||||
win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
|
||||
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
|
||||
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
const groupIds = distinct(contexts.map(context => context.groupId));
|
||||
const model = editorGroupService.getStacksModel();
|
||||
|
||||
const editorsToClose = new Map<Position, IEditorInput[]>();
|
||||
|
||||
groupIds.forEach(groupId => {
|
||||
const group = model.getGroup(groupId);
|
||||
const position = model.positionOfGroup(group);
|
||||
if (position >= 0) {
|
||||
const inputs = contexts.map(c => {
|
||||
if (c && groupId === c.groupId && types.isNumber(c.editorIndex)) {
|
||||
return group.getEditor(c.editorIndex);
|
||||
}
|
||||
|
||||
return group.activeEditor;
|
||||
}).filter(input => !!input);
|
||||
|
||||
if (inputs.length) {
|
||||
editorsToClose.set(position, inputs);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (editorsToClose.size === 0) {
|
||||
const activeEditor = editorService.getActiveEditor();
|
||||
if (activeEditor) {
|
||||
return editorService.closeEditor(activeEditor.position, activeEditor.input);
|
||||
}
|
||||
const activeGroup = editorGroupService.activeGroup;
|
||||
if (contexts.length === 0 && activeGroup.activeEditor) {
|
||||
contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) }); // active editor as fallback
|
||||
}
|
||||
|
||||
return editorService.closeEditors({
|
||||
positionOne: editorsToClose.get(Position.ONE),
|
||||
positionTwo: editorsToClose.get(Position.TWO),
|
||||
positionThree: editorsToClose.get(Position.THREE)
|
||||
});
|
||||
const groupIds = distinct(contexts.map(context => context.groupId));
|
||||
|
||||
return TPromise.join(groupIds.map(groupId => {
|
||||
const group = editorGroupService.getGroup(groupId);
|
||||
const editors = contexts
|
||||
.filter(context => context.groupId === groupId)
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
|
||||
|
||||
return group.closeEditors(editors);
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_EDITOR_GROUP_COMMAND_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext),
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
|
||||
win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
|
||||
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const commandsContext = getCommandsContext(resourceOrContext, context);
|
||||
|
||||
let group: IEditorGroup;
|
||||
if (commandsContext && typeof commandsContext.groupId === 'number') {
|
||||
group = editorGroupService.getGroup(commandsContext.groupId);
|
||||
} else {
|
||||
group = editorGroupService.activeGroup;
|
||||
}
|
||||
|
||||
editorGroupService.removeGroup(group);
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
|
||||
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
|
||||
const model = editorGroupService.getStacksModel();
|
||||
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
|
||||
|
||||
if (contexts.length === 0) {
|
||||
// Cover the case when run from command palette
|
||||
const activeGroup = model.activeGroup;
|
||||
const activeEditor = editorService.getActiveEditorInput();
|
||||
if (activeGroup && activeEditor) {
|
||||
contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.indexOf(activeEditor) });
|
||||
}
|
||||
const activeGroup = editorGroupService.activeGroup;
|
||||
if (contexts.length === 0 && activeGroup.activeEditor) {
|
||||
contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) }); // active editor as fallback
|
||||
}
|
||||
|
||||
const groupIds = distinct(contexts.map(context => context.groupId));
|
||||
const editorsToClose = new Map<Position, IEditorInput[]>();
|
||||
groupIds.forEach(groupId => {
|
||||
const group = model.getGroup(groupId);
|
||||
const inputsToSkip = contexts.map(c => {
|
||||
if (c.groupId === groupId && types.isNumber(c.editorIndex)) {
|
||||
return group.getEditor(c.editorIndex);
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}).filter(input => !!input);
|
||||
return TPromise.join(groupIds.map(groupId => {
|
||||
const group = editorGroupService.getGroup(groupId);
|
||||
const editors = contexts
|
||||
.filter(context => context.groupId === groupId)
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
|
||||
const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
|
||||
|
||||
const toClose = group.getEditors().filter(input => inputsToSkip.indexOf(input) === -1);
|
||||
editorsToClose.set(model.positionOfGroup(group), toClose);
|
||||
});
|
||||
|
||||
return editorService.closeEditors({
|
||||
positionOne: editorsToClose.get(Position.ONE),
|
||||
positionTwo: editorsToClose.get(Position.TWO),
|
||||
positionThree: editorsToClose.get(Position.THREE)
|
||||
});
|
||||
return group.closeEditors(editorsToClose);
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
const { position, input } = positionAndInput(editorGroupService, editorService, context);
|
||||
|
||||
if (typeof position === 'number' && input) {
|
||||
return editorService.closeEditors(position, { except: input, direction: Direction.RIGHT });
|
||||
const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
|
||||
if (group && editor) {
|
||||
return group.closeEditors({ direction: CloseDirection.RIGHT, except: editor });
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
@@ -433,17 +557,15 @@ function registerEditorCommands() {
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: KEEP_EDITOR_COMMAND_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter),
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
const { position, input } = positionAndInput(editorGroupService, editorService, context);
|
||||
|
||||
if (typeof position === 'number' && input) {
|
||||
return editorGroupService.pinEditor(position, input);
|
||||
const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
|
||||
if (group && editor) {
|
||||
return group.pinEditor(editor);
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
@@ -452,88 +574,98 @@ function registerEditorCommands() {
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: SHOW_EDITORS_IN_GROUP,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: void 0,
|
||||
primary: void 0,
|
||||
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupService);
|
||||
const editorService = accessor.get(IWorkbenchEditorService);
|
||||
handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const quickOpenService = accessor.get(IQuickOpenService);
|
||||
|
||||
const stacks = editorGroupService.getStacksModel();
|
||||
const groupCount = stacks.groups.length;
|
||||
if (groupCount <= 1) {
|
||||
if (editorGroupService.count <= 1) {
|
||||
return quickOpenService.show(NAVIGATE_ALL_EDITORS_GROUP_PREFIX);
|
||||
}
|
||||
|
||||
const { position } = positionAndInput(editorGroupService, editorService, context);
|
||||
|
||||
switch (position) {
|
||||
case Position.TWO:
|
||||
return quickOpenService.show(NAVIGATE_IN_GROUP_TWO_PREFIX);
|
||||
case Position.THREE:
|
||||
return quickOpenService.show(NAVIGATE_IN_GROUP_THREE_PREFIX);
|
||||
const commandsContext = getCommandsContext(resourceOrContext, context);
|
||||
if (commandsContext && typeof commandsContext.groupId === 'number') {
|
||||
editorGroupService.activateGroup(editorGroupService.getGroup(commandsContext.groupId)); // we need the group to be active
|
||||
}
|
||||
|
||||
return quickOpenService.show(NAVIGATE_IN_GROUP_ONE_PREFIX);
|
||||
return quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX);
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: '_workbench.printStacksModel',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
|
||||
handler(accessor: ServicesAccessor) {
|
||||
console.log(`${accessor.get(IEditorGroupService).getStacksModel().toString()}\n\n`);
|
||||
},
|
||||
when: void 0,
|
||||
primary: void 0
|
||||
});
|
||||
CommandsRegistry.registerCommand(CLOSE_EDITORS_AND_GROUP_COMMAND_ID, (accessor: ServicesAccessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: '_workbench.validateStacksModel',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
|
||||
handler(accessor: ServicesAccessor) {
|
||||
(<EditorStacksModel>accessor.get(IEditorGroupService).getStacksModel()).validate();
|
||||
},
|
||||
when: void 0,
|
||||
primary: void 0
|
||||
const { group } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
|
||||
if (group) {
|
||||
return group.closeAllEditors().then(() => {
|
||||
if (group.count === 0 && editorGroupService.getGroup(group.id) /* could be gone by now */) {
|
||||
editorGroupService.removeGroup(group); // only remove group if it is now empty
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return void 0;
|
||||
});
|
||||
}
|
||||
|
||||
function positionAndInput(editorGroupService: IEditorGroupService, editorService: IWorkbenchEditorService, context?: IEditorCommandsContext): { position: Position, input: IEditorInput } {
|
||||
|
||||
// Resolve from context
|
||||
const model = editorGroupService.getStacksModel();
|
||||
const group = context ? model.getGroup(context.groupId) : undefined;
|
||||
let position = group ? model.positionOfGroup(group) : undefined;
|
||||
let input = group && types.isNumber(context.editorIndex) ? group.getEditor(context.editorIndex) : undefined;
|
||||
|
||||
// If position or input are not passed in take the position and input of the active editor.
|
||||
const active = editorService.getActiveEditor();
|
||||
if (active) {
|
||||
position = typeof position === 'number' ? position : active.position;
|
||||
input = input ? input : <EditorInput>active.input;
|
||||
function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext {
|
||||
if (URI.isUri(resourceOrContext)) {
|
||||
return context;
|
||||
}
|
||||
|
||||
return { position, input };
|
||||
if (resourceOrContext && typeof resourceOrContext.groupId === 'number') {
|
||||
return resourceOrContext;
|
||||
}
|
||||
|
||||
if (context && typeof context.groupId === 'number') {
|
||||
return context;
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
|
||||
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext, listService: IListService): IEditorCommandsContext[] {
|
||||
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor: IEditorInput, control: IEditor } {
|
||||
|
||||
// Resolve from context
|
||||
let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined;
|
||||
let editor = group && typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : undefined;
|
||||
let control = group ? group.activeControl : undefined;
|
||||
|
||||
// Fallback to active group as needed
|
||||
if (!group) {
|
||||
group = editorGroupService.activeGroup;
|
||||
editor = <EditorInput>group.activeEditor;
|
||||
control = group.activeControl;
|
||||
}
|
||||
|
||||
return { group, editor, control };
|
||||
}
|
||||
|
||||
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] {
|
||||
|
||||
// First check for a focused list to return the selected items from
|
||||
const list = listService.lastFocusedList;
|
||||
if (list instanceof List && list.isDOMFocused()) {
|
||||
const elementToContext = (element: IEditorIdentifier | EditorGroup) =>
|
||||
element instanceof EditorGroup ? { groupId: element.id, editorIndex: undefined } : { groupId: element.group.id, editorIndex: element.group.indexOf(element.editor) };
|
||||
const onlyEditorGroupAndEditor = (e: IEditorIdentifier | EditorGroup) => e instanceof EditorGroup || ('editor' in e && 'group' in e);
|
||||
const elementToContext = (element: IEditorIdentifier | IEditorGroup) => {
|
||||
if (isEditorGroup(element)) {
|
||||
return { groupId: element.id, editorIndex: void 0 };
|
||||
}
|
||||
|
||||
const focusedElements: (IEditorIdentifier | EditorGroup)[] = list.getFocusedElements().filter(onlyEditorGroupAndEditor);
|
||||
// need to take into account when editor context is { group: group }
|
||||
const focus = editorContext ? editorContext : focusedElements.length ? focusedElements.map(elementToContext)[0] : undefined;
|
||||
return { groupId: element.groupId, editorIndex: editorGroupService.getGroup(element.groupId).getIndexOfEditor(element.editor) };
|
||||
};
|
||||
|
||||
const onlyEditorGroupAndEditor = (e: IEditorIdentifier | IEditorGroup) => isEditorGroup(e) || isEditorIdentifier(e);
|
||||
|
||||
const focusedElements: (IEditorIdentifier | IEditorGroup)[] = list.getFocusedElements().filter(onlyEditorGroupAndEditor);
|
||||
const focus = editorContext ? editorContext : focusedElements.length ? focusedElements.map(elementToContext)[0] : void 0; // need to take into account when editor context is { group: group }
|
||||
|
||||
if (focus) {
|
||||
const selection: (IEditorIdentifier | EditorGroup)[] = list.getSelectedElements().filter(onlyEditorGroupAndEditor);
|
||||
const selection: (IEditorIdentifier | IEditorGroup)[] = list.getSelectedElements().filter(onlyEditorGroupAndEditor);
|
||||
|
||||
// Only respect selection if it contains focused element
|
||||
if (selection && selection.some(s => s instanceof EditorGroup ? s.id === focus.groupId : s.group.id === focus.groupId && s.group.indexOf(s.editor) === focus.editorIndex)) {
|
||||
if (selection && selection.some(s => isEditorGroup(s) ? s.id === focus.groupId : s.groupId === focus.groupId && editorGroupService.getGroup(s.groupId).getIndexOfEditor(s.editor) === focus.editorIndex)) {
|
||||
return selection.map(elementToContext);
|
||||
}
|
||||
|
||||
@@ -544,3 +676,25 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
|
||||
// Otherwise go with passed in context
|
||||
return !!editorContext ? [editorContext] : [];
|
||||
}
|
||||
|
||||
function isEditorGroup(thing: any): thing is IEditorGroup {
|
||||
const group = thing as IEditorGroup;
|
||||
|
||||
return group && typeof group.id === 'number' && Array.isArray(group.editors);
|
||||
}
|
||||
|
||||
function isEditorIdentifier(thing: any): thing is IEditorIdentifier {
|
||||
const identifier = thing as IEditorIdentifier;
|
||||
|
||||
return identifier && typeof identifier.groupId === 'number';
|
||||
}
|
||||
|
||||
export function setup(): void {
|
||||
registerActiveEditorMoveCommand();
|
||||
registerEditorGroupsLayoutCommand();
|
||||
registerDiffEditorCommands();
|
||||
registerOpenEditorAtIndexCommands();
|
||||
registerCloseEditorCommands();
|
||||
registerFocusEditorGroupAtIndexCommands();
|
||||
registerSplitEditorCommands();
|
||||
}
|
||||
|
||||
247
src/vs/workbench/browser/parts/editor/editorControl.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { dispose, Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { Dimension, show, hide, addClass } from 'vs/base/browser/dom';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IEditorRegistry, Extensions as EditorExtensions, IEditorDescriptor } from 'vs/workbench/browser/editor';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
|
||||
import { toWinJsPromise } from 'vs/base/common/async';
|
||||
import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface IOpenEditorResult {
|
||||
readonly control: BaseEditor;
|
||||
readonly editorChanged: boolean;
|
||||
}
|
||||
|
||||
export class EditorControl extends Disposable {
|
||||
|
||||
get minimumWidth() { return this._activeControl ? this._activeControl.minimumWidth : DEFAULT_EDITOR_MIN_DIMENSIONS.width; }
|
||||
get minimumHeight() { return this._activeControl ? this._activeControl.minimumHeight : DEFAULT_EDITOR_MIN_DIMENSIONS.height; }
|
||||
get maximumWidth() { return this._activeControl ? this._activeControl.maximumWidth : DEFAULT_EDITOR_MAX_DIMENSIONS.width; }
|
||||
get maximumHeight() { return this._activeControl ? this._activeControl.maximumHeight : DEFAULT_EDITOR_MAX_DIMENSIONS.height; }
|
||||
|
||||
private _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onDidFocus(): Event<void> { return this._onDidFocus.event; }
|
||||
|
||||
private _onDidSizeConstraintsChange = this._register(new Emitter<{ width: number; height: number; }>());
|
||||
get onDidSizeConstraintsChange(): Event<{ width: number; height: number; }> { return this._onDidSizeConstraintsChange.event; }
|
||||
|
||||
private _activeControl: BaseEditor;
|
||||
private controls: BaseEditor[] = [];
|
||||
|
||||
private activeControlDisposeables: IDisposable[] = [];
|
||||
private dimension: Dimension;
|
||||
private editorOperation: LongRunningOperation;
|
||||
|
||||
constructor(
|
||||
private parent: HTMLElement,
|
||||
private groupView: IEditorGroupView,
|
||||
@IPartService private partService: IPartService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IProgressService progressService: IProgressService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.editorOperation = this._register(new LongRunningOperation(progressService));
|
||||
}
|
||||
|
||||
get activeControl(): BaseEditor {
|
||||
return this._activeControl;
|
||||
}
|
||||
|
||||
openEditor(editor: EditorInput, options?: EditorOptions): TPromise<IOpenEditorResult> {
|
||||
|
||||
// Editor control
|
||||
const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editor);
|
||||
const control = this.doShowEditorControl(descriptor, options);
|
||||
|
||||
// Set input
|
||||
return this.doSetInput(control, editor, options).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
|
||||
}
|
||||
|
||||
private doShowEditorControl(descriptor: IEditorDescriptor, options: EditorOptions): BaseEditor {
|
||||
|
||||
// Return early if the currently active editor control can handle the input
|
||||
if (this._activeControl && descriptor.describes(this._activeControl)) {
|
||||
return this._activeControl;
|
||||
}
|
||||
|
||||
// Hide active one first
|
||||
this.doHideActiveEditorControl();
|
||||
|
||||
// Create editor
|
||||
const control = this.doCreateEditorControl(descriptor);
|
||||
|
||||
// Set editor as active
|
||||
this.doSetActiveControl(control);
|
||||
|
||||
// Show editor
|
||||
this.parent.appendChild(control.getContainer());
|
||||
show(control.getContainer());
|
||||
|
||||
// Indicate to editor that it is now visible
|
||||
control.setVisible(true, this.groupView);
|
||||
|
||||
// Layout
|
||||
if (this.dimension) {
|
||||
control.layout(this.dimension);
|
||||
}
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
private doCreateEditorControl(descriptor: IEditorDescriptor): BaseEditor {
|
||||
|
||||
// Instantiate editor
|
||||
const control = this.doInstantiateEditorControl(descriptor);
|
||||
|
||||
// Create editor container as needed
|
||||
if (!control.getContainer()) {
|
||||
const controlInstanceContainer = document.createElement('div');
|
||||
addClass(controlInstanceContainer, 'editor-instance');
|
||||
controlInstanceContainer.id = descriptor.getId();
|
||||
|
||||
control.create(controlInstanceContainer);
|
||||
}
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
private doInstantiateEditorControl(descriptor: IEditorDescriptor): BaseEditor {
|
||||
|
||||
// Return early if already instantiated
|
||||
const existingControl = this.controls.filter(control => descriptor.describes(control))[0];
|
||||
if (existingControl) {
|
||||
return existingControl;
|
||||
}
|
||||
|
||||
// Otherwise instantiate new
|
||||
const control = this._register(descriptor.instantiate(this.instantiationService));
|
||||
this.controls.push(control);
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
private doSetActiveControl(control: BaseEditor) {
|
||||
this._activeControl = control;
|
||||
|
||||
// Clear out previous active control listeners
|
||||
this.activeControlDisposeables = dispose(this.activeControlDisposeables);
|
||||
|
||||
// Listen to control changes
|
||||
if (control) {
|
||||
this.activeControlDisposeables.push(control.onDidSizeConstraintsChange(e => this._onDidSizeConstraintsChange.fire(e)));
|
||||
this.activeControlDisposeables.push(control.onDidFocus(() => this._onDidFocus.fire()));
|
||||
}
|
||||
|
||||
// Indicate that size constraints could have changed due to new editor
|
||||
this._onDidSizeConstraintsChange.fire();
|
||||
}
|
||||
|
||||
private doSetInput(control: BaseEditor, editor: EditorInput, options: EditorOptions): TPromise<boolean> {
|
||||
|
||||
// If the input did not change, return early and only apply the options
|
||||
// unless the options instruct us to force open it even if it is the same
|
||||
const forceReload = options && options.forceReload;
|
||||
const inputMatches = control.input && control.input.matches(editor);
|
||||
if (inputMatches && !forceReload) {
|
||||
|
||||
// Forward options
|
||||
control.setOptions(options);
|
||||
|
||||
// Still focus as needed
|
||||
const focus = !options || !options.preserveFocus;
|
||||
if (focus) {
|
||||
control.focus();
|
||||
}
|
||||
|
||||
return TPromise.as(false);
|
||||
}
|
||||
|
||||
// Show progress while setting input after a certain timeout. If the workbench is opening
|
||||
// be more relaxed about progress showing by increasing the delay a little bit to reduce flicker.
|
||||
const operation = this.editorOperation.start(this.partService.isCreated() ? 800 : 3200);
|
||||
|
||||
// Call into editor control
|
||||
const editorWillChange = !inputMatches;
|
||||
return toWinJsPromise(control.setInput(editor, options, operation.token)).then(() => {
|
||||
|
||||
// Focus (unless prevented or another operation is running)
|
||||
if (operation.isCurrent()) {
|
||||
const focus = !options || !options.preserveFocus;
|
||||
if (focus) {
|
||||
control.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Operation done
|
||||
operation.stop();
|
||||
|
||||
return editorWillChange;
|
||||
}, e => {
|
||||
|
||||
// Operation done
|
||||
operation.stop();
|
||||
|
||||
return TPromise.wrapError(e);
|
||||
});
|
||||
}
|
||||
|
||||
private doHideActiveEditorControl(): void {
|
||||
if (!this._activeControl) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop any running operation
|
||||
this.editorOperation.stop();
|
||||
|
||||
// Remove control from parent and hide
|
||||
const controlInstanceContainer = this._activeControl.getContainer();
|
||||
this.parent.removeChild(controlInstanceContainer);
|
||||
hide(controlInstanceContainer);
|
||||
|
||||
// Indicate to editor control
|
||||
this._activeControl.clearInput();
|
||||
this._activeControl.setVisible(false, this.groupView);
|
||||
|
||||
// Clear active control
|
||||
this.doSetActiveControl(null);
|
||||
}
|
||||
|
||||
closeEditor(editor: EditorInput): void {
|
||||
if (this._activeControl && editor.matches(this._activeControl.input)) {
|
||||
this.doHideActiveEditorControl();
|
||||
}
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
this.dimension = dimension;
|
||||
|
||||
if (this._activeControl && this.dimension) {
|
||||
this._activeControl.layout(this.dimension);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
|
||||
// Forward to all editor controls
|
||||
this.controls.forEach(editor => editor.shutdown());
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.activeControlDisposeables = dispose(this.activeControlDisposeables);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
534
src/vs/workbench/browser/parts/editor/editorDropTarget.ts
Normal file
@@ -0,0 +1,534 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/editordroptarget';
|
||||
import { LocalSelectionTransfer, DraggedEditorIdentifier, ResourcesDropHandler, DraggedEditorGroupIdentifier, DragAndDropObserver } from 'vs/workbench/browser/dnd';
|
||||
import { addDisposableListener, EventType, EventHelper, isAncestor, toggleClass, addClass, removeClass } from 'vs/base/browser/dom';
|
||||
import { IEditorGroupsAccessor, EDITOR_TITLE_HEIGHT, IEditorGroupView, getActiveTextEditorOptions } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { EDITOR_DRAG_AND_DROP_BACKGROUND, Themable } from 'vs/workbench/common/theme';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
interface IDropOperation {
|
||||
splitDirection?: GroupDirection;
|
||||
}
|
||||
|
||||
class DropOverlay extends Themable {
|
||||
|
||||
private static OVERLAY_ID = 'monaco-workbench-editor-drop-overlay';
|
||||
|
||||
private container: HTMLElement;
|
||||
private overlay: HTMLElement;
|
||||
|
||||
private currentDropOperation: IDropOperation;
|
||||
private _disposed: boolean;
|
||||
|
||||
private readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
|
||||
private readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
|
||||
|
||||
constructor(
|
||||
private accessor: IEditorGroupsAccessor,
|
||||
private groupView: IEditorGroupView,
|
||||
themeService: IThemeService,
|
||||
private instantiationService: IInstantiationService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this.create();
|
||||
}
|
||||
|
||||
get disposed(): boolean {
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
private create(): void {
|
||||
const overlayOffsetHeight = this.getOverlayOffsetHeight();
|
||||
|
||||
// Container
|
||||
this.container = document.createElement('div');
|
||||
this.container.id = DropOverlay.OVERLAY_ID;
|
||||
this.container.style.top = `${overlayOffsetHeight}px`;
|
||||
|
||||
// Parent
|
||||
this.groupView.element.appendChild(this.container);
|
||||
addClass(this.groupView.element, 'dragged-over');
|
||||
this._register(toDisposable(() => {
|
||||
this.groupView.element.removeChild(this.container);
|
||||
removeClass(this.groupView.element, 'dragged-over');
|
||||
}));
|
||||
|
||||
// Overlay
|
||||
this.overlay = document.createElement('div');
|
||||
addClass(this.overlay, 'editor-group-overlay-indicator');
|
||||
this.container.appendChild(this.overlay);
|
||||
|
||||
// Overlay Event Handling
|
||||
this.registerListeners();
|
||||
|
||||
// Styles
|
||||
this.updateStyles();
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
|
||||
// Overlay drop background
|
||||
this.overlay.style.backgroundColor = this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND);
|
||||
|
||||
// Overlay contrast border (if any)
|
||||
const activeContrastBorderColor = this.getColor(activeContrastBorder);
|
||||
this.overlay.style.outlineColor = activeContrastBorderColor;
|
||||
this.overlay.style.outlineOffset = activeContrastBorderColor ? '-2px' : null;
|
||||
this.overlay.style.outlineStyle = activeContrastBorderColor ? 'dashed' : null;
|
||||
this.overlay.style.outlineWidth = activeContrastBorderColor ? '2px' : null;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(new DragAndDropObserver(this.container, {
|
||||
onDragEnter: e => void 0,
|
||||
onDragOver: e => {
|
||||
const isDraggingGroup = this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype);
|
||||
const isDraggingEditor = this.editorTransfer.hasData(DraggedEditorIdentifier.prototype);
|
||||
|
||||
// Update the dropEffect to "copy" if there is no local data to be dragged because
|
||||
// in that case we can only copy the data into and not move it from its source
|
||||
if (!isDraggingEditor && !isDraggingGroup) {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
// Find out if operation is valid
|
||||
const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier) : true;
|
||||
if (!isCopy) {
|
||||
const sourceGroupView = this.findSourceGroupView();
|
||||
if (sourceGroupView === this.groupView) {
|
||||
if (isDraggingGroup || (isDraggingEditor && sourceGroupView.count < 2)) {
|
||||
this.hideOverlay();
|
||||
return; // do not allow to drop group/editor on itself if this results in an empty group
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Position overlay
|
||||
this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup);
|
||||
},
|
||||
|
||||
onDragLeave: e => this.dispose(),
|
||||
onDragEnd: e => this.dispose(),
|
||||
|
||||
onDrop: e => {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
// Dispose overlay
|
||||
this.dispose();
|
||||
|
||||
// Handle drop if we have a valid operation
|
||||
if (this.currentDropOperation) {
|
||||
this.handleDrop(e, this.currentDropOperation.splitDirection);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(addDisposableListener(this.container, EventType.MOUSE_OVER, () => {
|
||||
// Under some circumstances we have seen reports where the drop overlay is not being
|
||||
// cleaned up and as such the editor area remains under the overlay so that you cannot
|
||||
// type into the editor anymore. This seems related to using VMs and DND via host and
|
||||
// guest OS, though some users also saw it without VMs.
|
||||
// To protect against this issue we always destroy the overlay as soon as we detect a
|
||||
// mouse event over it. The delay is used to guarantee we are not interfering with the
|
||||
// actual DROP event that can also trigger a mouse over event.
|
||||
setTimeout(() => {
|
||||
this.dispose();
|
||||
}, 300);
|
||||
}));
|
||||
}
|
||||
|
||||
private findSourceGroupView(): IEditorGroupView {
|
||||
|
||||
// Check for group transfer
|
||||
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
|
||||
return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier);
|
||||
}
|
||||
|
||||
// Check for editor transfer
|
||||
else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier.groupId);
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
|
||||
private handleDrop(event: DragEvent, splitDirection?: GroupDirection): void {
|
||||
|
||||
// Determine target group
|
||||
const ensureTargetGroup = () => {
|
||||
let targetGroup: IEditorGroupView;
|
||||
if (typeof splitDirection === 'number') {
|
||||
targetGroup = this.accessor.addGroup(this.groupView, splitDirection);
|
||||
} else {
|
||||
targetGroup = this.groupView;
|
||||
}
|
||||
|
||||
return targetGroup;
|
||||
};
|
||||
|
||||
// Check for group transfer
|
||||
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
|
||||
const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier;
|
||||
|
||||
// Return if the drop is a no-op
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditorGroup);
|
||||
if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Split to new group
|
||||
let targetGroup: IEditorGroupView;
|
||||
if (typeof splitDirection === 'number') {
|
||||
if (this.isCopyOperation(event)) {
|
||||
targetGroup = this.accessor.copyGroup(sourceGroup, this.groupView, splitDirection);
|
||||
} else {
|
||||
targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection);
|
||||
}
|
||||
}
|
||||
|
||||
// Merge into existing group
|
||||
else {
|
||||
if (this.isCopyOperation(event)) {
|
||||
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView, { mode: MergeGroupMode.COPY_EDITORS });
|
||||
} else {
|
||||
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView);
|
||||
}
|
||||
}
|
||||
|
||||
this.accessor.activateGroup(targetGroup);
|
||||
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
|
||||
}
|
||||
|
||||
// Check for editor transfer
|
||||
else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
|
||||
const targetGroup = ensureTargetGroup();
|
||||
|
||||
// Return if the drop is a no-op
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
|
||||
if (sourceGroup === targetGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Open in target group
|
||||
const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true }));
|
||||
targetGroup.openEditor(draggedEditor.editor, options);
|
||||
|
||||
// Ensure target has focus
|
||||
targetGroup.focus();
|
||||
|
||||
// Close in source group unless we copy
|
||||
const copyEditor = this.isCopyOperation(event, draggedEditor);
|
||||
if (!copyEditor) {
|
||||
sourceGroup.closeEditor(draggedEditor.editor);
|
||||
}
|
||||
|
||||
this.editorTransfer.clearData(DraggedEditorIdentifier.prototype);
|
||||
}
|
||||
|
||||
// Check for URI transfer
|
||||
else {
|
||||
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ });
|
||||
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup.focus());
|
||||
}
|
||||
}
|
||||
|
||||
private isCopyOperation(e: DragEvent, draggedEditor?: IEditorIdentifier): boolean {
|
||||
if (draggedEditor && !(draggedEditor.editor as EditorInput).supportsSplitEditor()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
|
||||
}
|
||||
|
||||
private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean): void {
|
||||
const preferSplitVertically = this.accessor.partOptions.openSideBySideDirection === 'right';
|
||||
|
||||
const editorControlWidth = this.groupView.element.clientWidth;
|
||||
const editorControlHeight = this.groupView.element.clientHeight - this.getOverlayOffsetHeight();
|
||||
|
||||
let edgeWidthThresholdFactor: number;
|
||||
if (isDraggingGroup) {
|
||||
edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction
|
||||
} else {
|
||||
edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors
|
||||
}
|
||||
|
||||
let edgeHeightThresholdFactor: number;
|
||||
if (isDraggingGroup) {
|
||||
edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction
|
||||
} else {
|
||||
edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors
|
||||
}
|
||||
|
||||
const edgeWidthThreshold = editorControlWidth * edgeWidthThresholdFactor;
|
||||
const edgeHeightThreshold = editorControlHeight * edgeHeightThresholdFactor;
|
||||
|
||||
const splitWidthThreshold = editorControlWidth / 3; // offer to split left/right at 33%
|
||||
const splitHeightThreshold = editorControlHeight / 3; // offer to split up/down at 33%
|
||||
|
||||
// Enable to debug the drop threshold square
|
||||
// let child = this.overlay.children.item(0) as HTMLElement || this.overlay.appendChild(document.createElement('div'));
|
||||
// child.style.backgroundColor = 'red';
|
||||
// child.style.position = 'absolute';
|
||||
// child.style.width = (groupViewWidth - (2 * edgeWidthThreshold)) + 'px';
|
||||
// child.style.height = (groupViewHeight - (2 * edgeHeightThreshold)) + 'px';
|
||||
// child.style.left = edgeWidthThreshold + 'px';
|
||||
// child.style.top = edgeHeightThreshold + 'px';
|
||||
|
||||
// No split if mouse is above certain threshold in the center of the view
|
||||
let splitDirection: GroupDirection;
|
||||
if (
|
||||
mousePosX > edgeWidthThreshold && mousePosX < editorControlWidth - edgeWidthThreshold &&
|
||||
mousePosY > edgeHeightThreshold && mousePosY < editorControlHeight - edgeHeightThreshold
|
||||
) {
|
||||
splitDirection = void 0;
|
||||
}
|
||||
|
||||
// Offer to split otherwise
|
||||
else {
|
||||
|
||||
// User prefers to split vertically: offer a larger hitzone
|
||||
// for this direction like so:
|
||||
// ----------------------------------------------
|
||||
// | | SPLIT UP | |
|
||||
// | SPLIT |-----------------------| SPLIT |
|
||||
// | | MERGE | |
|
||||
// | LEFT |-----------------------| RIGHT |
|
||||
// | | SPLIT DOWN | |
|
||||
// ----------------------------------------------
|
||||
if (preferSplitVertically) {
|
||||
if (mousePosX < splitWidthThreshold) {
|
||||
splitDirection = GroupDirection.LEFT;
|
||||
} else if (mousePosX > splitWidthThreshold * 2) {
|
||||
splitDirection = GroupDirection.RIGHT;
|
||||
} else if (mousePosY < editorControlHeight / 2) {
|
||||
splitDirection = GroupDirection.UP;
|
||||
} else {
|
||||
splitDirection = GroupDirection.DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// User prefers to split horizontally: offer a larger hitzone
|
||||
// for this direction like so:
|
||||
// ----------------------------------------------
|
||||
// | SPLIT UP |
|
||||
// |--------------------------------------------|
|
||||
// | SPLIT LEFT | MERGE | SPLIT RIGHT |
|
||||
// |--------------------------------------------|
|
||||
// | SPLIT DOWN |
|
||||
// ----------------------------------------------
|
||||
else {
|
||||
if (mousePosY < splitHeightThreshold) {
|
||||
splitDirection = GroupDirection.UP;
|
||||
} else if (mousePosY > splitHeightThreshold * 2) {
|
||||
splitDirection = GroupDirection.DOWN;
|
||||
} else if (mousePosX < editorControlWidth / 2) {
|
||||
splitDirection = GroupDirection.LEFT;
|
||||
} else {
|
||||
splitDirection = GroupDirection.RIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw overlay based on split direction
|
||||
switch (splitDirection) {
|
||||
case GroupDirection.UP:
|
||||
this.doPositionOverlay({ top: '0', left: '0', width: '100%', height: '50%' });
|
||||
break;
|
||||
case GroupDirection.DOWN:
|
||||
this.doPositionOverlay({ top: '50%', left: '0', width: '100%', height: '50%' });
|
||||
break;
|
||||
case GroupDirection.LEFT:
|
||||
this.doPositionOverlay({ top: '0', left: '0', width: '50%', height: '100%' });
|
||||
break;
|
||||
case GroupDirection.RIGHT:
|
||||
this.doPositionOverlay({ top: '0', left: '50%', width: '50%', height: '100%' });
|
||||
break;
|
||||
default:
|
||||
this.doPositionOverlay({ top: '0', left: '0', width: '100%', height: '100%' });
|
||||
}
|
||||
|
||||
// Make sure the overlay is visible now
|
||||
this.overlay.style.opacity = '1';
|
||||
|
||||
// Enable transition after a timeout to prevent initial animation
|
||||
setTimeout(() => addClass(this.overlay, 'overlay-move-transition'), 0);
|
||||
|
||||
// Remember as current split direction
|
||||
this.currentDropOperation = { splitDirection };
|
||||
}
|
||||
|
||||
private doPositionOverlay(options: { top: string, left: string, width: string, height: string }): void {
|
||||
|
||||
// Container
|
||||
const offsetHeight = this.getOverlayOffsetHeight();
|
||||
if (offsetHeight) {
|
||||
this.container.style.height = `calc(100% - ${offsetHeight}px)`;
|
||||
} else {
|
||||
this.container.style.height = '100%';
|
||||
}
|
||||
|
||||
// Overlay
|
||||
this.overlay.style.top = options.top;
|
||||
this.overlay.style.left = options.left;
|
||||
this.overlay.style.width = options.width;
|
||||
this.overlay.style.height = options.height;
|
||||
}
|
||||
|
||||
private getOverlayOffsetHeight(): number {
|
||||
if (!this.groupView.isEmpty() && this.accessor.partOptions.showTabs) {
|
||||
return EDITOR_TITLE_HEIGHT; // show overlay below title if group shows tabs
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private hideOverlay(): void {
|
||||
|
||||
// Reset overlay
|
||||
this.doPositionOverlay({ top: '0', left: '0', width: '100%', height: '100%' });
|
||||
this.overlay.style.opacity = '0';
|
||||
removeClass(this.overlay, 'overlay-move-transition');
|
||||
|
||||
// Reset current operation
|
||||
this.currentDropOperation = void 0;
|
||||
}
|
||||
|
||||
contains(element: HTMLElement): boolean {
|
||||
return element === this.container || element === this.overlay;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
this._disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
export class EditorDropTarget extends Themable {
|
||||
|
||||
private _overlay: DropOverlay;
|
||||
|
||||
private counter = 0;
|
||||
|
||||
private readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
|
||||
private readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
|
||||
|
||||
constructor(
|
||||
private accessor: IEditorGroupsAccessor,
|
||||
private container: HTMLElement,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private get overlay(): DropOverlay {
|
||||
if (this._overlay && !this._overlay.disposed) {
|
||||
return this._overlay;
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(addDisposableListener(this.container, EventType.DRAG_ENTER, e => this.onDragEnter(e)));
|
||||
this._register(addDisposableListener(this.container, EventType.DRAG_LEAVE, () => this.onDragLeave()));
|
||||
[this.container, window].forEach(node => this._register(addDisposableListener(node as HTMLElement, EventType.DRAG_END, () => this.onDragEnd())));
|
||||
}
|
||||
|
||||
private onDragEnter(event: DragEvent): void {
|
||||
this.counter++;
|
||||
|
||||
// Validate transfer
|
||||
if (
|
||||
!this.editorTransfer.hasData(DraggedEditorIdentifier.prototype) &&
|
||||
!this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype) &&
|
||||
!event.dataTransfer.types.length // see https://github.com/Microsoft/vscode/issues/25789
|
||||
) {
|
||||
event.dataTransfer.dropEffect = 'none';
|
||||
return; // unsupported transfer
|
||||
}
|
||||
|
||||
// Signal DND start
|
||||
this.updateContainer(true);
|
||||
|
||||
const target = event.target as HTMLElement;
|
||||
if (target) {
|
||||
|
||||
// Somehow we managed to move the mouse quickly out of the current overlay, so destroy it
|
||||
if (this.overlay && !this.overlay.contains(target)) {
|
||||
this.disposeOverlay();
|
||||
}
|
||||
|
||||
// Create overlay over target
|
||||
if (!this.overlay) {
|
||||
const targetGroupView = this.findTargetGroupView(target);
|
||||
if (targetGroupView) {
|
||||
this._overlay = new DropOverlay(this.accessor, targetGroupView, this.themeService, this.instantiationService);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onDragLeave(): void {
|
||||
this.counter--;
|
||||
|
||||
if (this.counter === 0) {
|
||||
this.updateContainer(false);
|
||||
}
|
||||
}
|
||||
|
||||
private onDragEnd(): void {
|
||||
this.counter = 0;
|
||||
|
||||
this.updateContainer(false);
|
||||
this.disposeOverlay();
|
||||
}
|
||||
|
||||
private findTargetGroupView(child: HTMLElement): IEditorGroupView {
|
||||
const groups = this.accessor.groups;
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
const groupView = groups[i];
|
||||
|
||||
if (isAncestor(child, groupView.element)) {
|
||||
return groupView;
|
||||
}
|
||||
}
|
||||
|
||||
return void 0;
|
||||
}
|
||||
|
||||
private updateContainer(isDraggedOver: boolean): void {
|
||||
toggleClass(this.container, 'dragged-over', isDraggedOver);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
this.disposeOverlay();
|
||||
}
|
||||
|
||||
private disposeOverlay(): void {
|
||||
if (this.overlay) {
|
||||
this.overlay.dispose();
|
||||
this._overlay = void 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
1451
src/vs/workbench/browser/parts/editor/editorGroupView.ts
Normal file
@@ -8,7 +8,6 @@ import 'vs/css!./media/editorpicker';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as nls from 'vs/nls';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
@@ -16,61 +15,55 @@ import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { getIconClasses } from 'vs/workbench/browser/labels';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
|
||||
import { Position } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { EditorInput, toResource, IEditorGroup, IEditorStacksModel } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, toResource } from 'vs/workbench/common/editor';
|
||||
import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
|
||||
export class EditorPickerEntry extends QuickOpenEntryGroup {
|
||||
private stacks: IEditorStacksModel;
|
||||
|
||||
constructor(
|
||||
private editor: EditorInput,
|
||||
private _group: IEditorGroup,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IModeService private modeService: IModeService,
|
||||
@IModelService private modelService: IModelService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService
|
||||
@IModelService private modelService: IModelService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.stacks = editorGroupService.getStacksModel();
|
||||
}
|
||||
|
||||
public getLabelOptions(): IIconLabelValueOptions {
|
||||
getLabelOptions(): IIconLabelValueOptions {
|
||||
return {
|
||||
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource()),
|
||||
italic: this._group.isPreview(this.editor)
|
||||
italic: !this._group.isPinned(this.editor)
|
||||
};
|
||||
}
|
||||
|
||||
public getLabel(): string {
|
||||
getLabel(): string {
|
||||
return this.editor.getName();
|
||||
}
|
||||
|
||||
public getIcon(): string {
|
||||
getIcon(): string {
|
||||
return this.editor.isDirty() ? 'dirty' : '';
|
||||
}
|
||||
|
||||
public get group(): IEditorGroup {
|
||||
get group(): IEditorGroup {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
getResource(): URI {
|
||||
return toResource(this.editor, { supportSideBySide: true });
|
||||
}
|
||||
|
||||
public getAriaLabel(): string {
|
||||
getAriaLabel(): string {
|
||||
return nls.localize('entryAriaLabel', "{0}, editor group picker", this.getLabel());
|
||||
}
|
||||
|
||||
public getDescription(): string {
|
||||
getDescription(): string {
|
||||
return this.editor.getDescription();
|
||||
}
|
||||
|
||||
public run(mode: Mode, context: IEntryRunContext): boolean {
|
||||
run(mode: Mode, context: IEntryRunContext): boolean {
|
||||
if (mode === Mode.OPEN) {
|
||||
return this.runOpen(context);
|
||||
}
|
||||
@@ -79,7 +72,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
|
||||
}
|
||||
|
||||
private runOpen(context: IEntryRunContext): boolean {
|
||||
this.editorService.openEditor(this.editor, null, this.stacks.positionOfGroup(this.group)).done(null, errors.onUnexpectedError);
|
||||
this._group.openEditor(this.editor);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -90,15 +83,15 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
|
||||
|
||||
constructor(
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService protected editorGroupService: IEditorGroupService
|
||||
@IEditorService protected editorService: IEditorService,
|
||||
@IEditorGroupsService protected editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.scorerCache = Object.create(null);
|
||||
}
|
||||
|
||||
public getResults(searchValue: string): TPromise<QuickOpenModel> {
|
||||
getResults(searchValue: string): TPromise<QuickOpenModel> {
|
||||
const editorEntries = this.getEditorEntries();
|
||||
if (!editorEntries.length) {
|
||||
return TPromise.as(null);
|
||||
@@ -123,11 +116,11 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
|
||||
});
|
||||
|
||||
// Sorting
|
||||
const stacks = this.editorGroupService.getStacksModel();
|
||||
if (query.value) {
|
||||
const groups = this.editorGroupService.getGroups(GroupsOrder.CREATION_TIME);
|
||||
entries.sort((e1, e2) => {
|
||||
if (e1.group !== e2.group) {
|
||||
return stacks.positionOfGroup(e1.group) - stacks.positionOfGroup(e2.group);
|
||||
return groups.indexOf(e1.group) - groups.indexOf(e2.group); // older groups first
|
||||
}
|
||||
|
||||
return compareItemsByScore(e1, e2, query, true, QuickOpenItemAccessor, this.scorerCache);
|
||||
@@ -135,11 +128,11 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
|
||||
}
|
||||
|
||||
// Grouping (for more than one group)
|
||||
if (stacks.groups.length > 1) {
|
||||
if (this.editorGroupService.count > 1) {
|
||||
let lastGroup: IEditorGroup;
|
||||
entries.forEach(e => {
|
||||
if (!lastGroup || lastGroup !== e.group) {
|
||||
e.setGroupLabel(nls.localize('groupLabel', "Group: {0}", e.group.label));
|
||||
e.setGroupLabel(e.group.label);
|
||||
e.setShowBorder(!!lastGroup);
|
||||
lastGroup = e.group;
|
||||
}
|
||||
@@ -149,28 +142,26 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
|
||||
return TPromise.as(new QuickOpenModel(entries));
|
||||
}
|
||||
|
||||
public onClose(canceled: boolean): void {
|
||||
onClose(canceled: boolean): void {
|
||||
this.scorerCache = Object.create(null);
|
||||
}
|
||||
|
||||
protected abstract getEditorEntries(): EditorPickerEntry[];
|
||||
}
|
||||
|
||||
export abstract class EditorGroupPicker extends BaseEditorPicker {
|
||||
export class ActiveEditorGroupPicker extends BaseEditorPicker {
|
||||
|
||||
static readonly ID = 'workbench.picker.activeEditors';
|
||||
|
||||
protected getEditorEntries(): EditorPickerEntry[] {
|
||||
const stacks = this.editorGroupService.getStacksModel();
|
||||
const group = stacks.groupAt(this.getPosition());
|
||||
if (!group) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return group.getEditors(true).map((editor, index) => this.instantiationService.createInstance(EditorPickerEntry, editor, group));
|
||||
return this.group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE).map((editor, index) => this.instantiationService.createInstance(EditorPickerEntry, editor, this.group));
|
||||
}
|
||||
|
||||
protected abstract getPosition(): Position;
|
||||
private get group(): IEditorGroup {
|
||||
return this.editorGroupService.activeGroup;
|
||||
}
|
||||
|
||||
public getEmptyLabel(searchString: string): string {
|
||||
getEmptyLabel(searchString: string): string {
|
||||
if (searchString) {
|
||||
return nls.localize('noResultsFoundInGroup', "No matching opened editor found in group");
|
||||
}
|
||||
@@ -178,76 +169,45 @@ export abstract class EditorGroupPicker extends BaseEditorPicker {
|
||||
return nls.localize('noOpenedEditors', "List of opened editors is currently empty in group");
|
||||
}
|
||||
|
||||
public getAutoFocus(searchValue: string, context: { model: IModel<QuickOpenEntry>, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus {
|
||||
getAutoFocus(searchValue: string, context: { model: IModel<QuickOpenEntry>, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus {
|
||||
if (searchValue || !context.quickNavigateConfiguration) {
|
||||
return {
|
||||
autoFocusFirstEntry: true
|
||||
};
|
||||
}
|
||||
|
||||
const stacks = this.editorGroupService.getStacksModel();
|
||||
const group = stacks.groupAt(this.getPosition());
|
||||
if (!group) {
|
||||
return super.getAutoFocus(searchValue, context);
|
||||
}
|
||||
|
||||
const isShiftNavigate = (context.quickNavigateConfiguration && context.quickNavigateConfiguration.keybindings.some(k => {
|
||||
const [firstPart, chordPart] = k.getParts();
|
||||
if (chordPart) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return firstPart.shiftKey;
|
||||
}));
|
||||
|
||||
if (isShiftNavigate) {
|
||||
return {
|
||||
autoFocusLastEntry: true
|
||||
};
|
||||
}
|
||||
|
||||
const editors = this.group.count;
|
||||
return {
|
||||
autoFocusFirstEntry: group.count === 1,
|
||||
autoFocusSecondEntry: group.count > 1
|
||||
autoFocusFirstEntry: editors === 1,
|
||||
autoFocusSecondEntry: editors > 1
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupOnePicker extends EditorGroupPicker {
|
||||
|
||||
public static readonly ID = 'workbench.picker.editors.one';
|
||||
|
||||
protected getPosition(): Position {
|
||||
return Position.ONE;
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupTwoPicker extends EditorGroupPicker {
|
||||
|
||||
public static readonly ID = 'workbench.picker.editors.two';
|
||||
|
||||
protected getPosition(): Position {
|
||||
return Position.TWO;
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupThreePicker extends EditorGroupPicker {
|
||||
|
||||
public static readonly ID = 'workbench.picker.editors.three';
|
||||
|
||||
protected getPosition(): Position {
|
||||
return Position.THREE;
|
||||
}
|
||||
}
|
||||
|
||||
export class AllEditorsPicker extends BaseEditorPicker {
|
||||
|
||||
public static readonly ID = 'workbench.picker.editors';
|
||||
static readonly ID = 'workbench.picker.editors';
|
||||
|
||||
protected getEditorEntries(): EditorPickerEntry[] {
|
||||
const entries: EditorPickerEntry[] = [];
|
||||
|
||||
const stacks = this.editorGroupService.getStacksModel();
|
||||
stacks.groups.forEach((group, position) => {
|
||||
group.getEditors().forEach((editor, index) => {
|
||||
this.editorGroupService.getGroups(GroupsOrder.CREATION_TIME).forEach(group => {
|
||||
group.editors.forEach(editor => {
|
||||
entries.push(this.instantiationService.createInstance(EditorPickerEntry, editor, group));
|
||||
});
|
||||
});
|
||||
@@ -255,7 +215,7 @@ export class AllEditorsPicker extends BaseEditorPicker {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public getEmptyLabel(searchString: string): string {
|
||||
getEmptyLabel(searchString: string): string {
|
||||
if (searchString) {
|
||||
return nls.localize('noResultsFound', "No matching opened editor found");
|
||||
}
|
||||
@@ -263,7 +223,7 @@ export class AllEditorsPicker extends BaseEditorPicker {
|
||||
return nls.localize('noOpenedEditorsAllGroups', "List of opened editors is currently empty");
|
||||
}
|
||||
|
||||
public getAutoFocus(searchValue: string, context: { model: IModel<QuickOpenEntry>, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus {
|
||||
getAutoFocus(searchValue: string, context: { model: IModel<QuickOpenEntry>, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus {
|
||||
if (searchValue) {
|
||||
return {
|
||||
autoFocusFirstEntry: true
|
||||
|
||||
@@ -20,7 +20,7 @@ import { language, LANGUAGE_DEFAULT, AccessibilitySupport } from 'vs/base/common
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { IMode } from 'vs/editor/common/modes';
|
||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IEditorAction } from 'vs/editor/common/editorCommon';
|
||||
@@ -30,21 +30,19 @@ import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/
|
||||
import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/editor/contrib/indentation/indentation';
|
||||
import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor';
|
||||
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
|
||||
import { IEditor as IBaseEditor, IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { SUPPORTED_ENCODINGS, IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { TabFocus } from 'vs/editor/common/config/commonEditorConfig';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { getCodeEditor as getEditorWidget, getCodeOrDiffEditor } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
@@ -54,7 +52,7 @@ import { attachButtonStyler } from 'vs/platform/theme/common/styler';
|
||||
import { widgetShadow, editorWidgetBackground, foreground, darken, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditor, isCodeEditor, isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
|
||||
@@ -64,11 +62,11 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr
|
||||
class SideBySideEditorEncodingSupport implements IEncodingSupport {
|
||||
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
|
||||
|
||||
public getEncoding(): string {
|
||||
getEncoding(): string {
|
||||
return this.master.getEncoding(); // always report from modified (right hand) side
|
||||
}
|
||||
|
||||
public setEncoding(encoding: string, mode: EncodingMode): void {
|
||||
setEncoding(encoding: string, mode: EncodingMode): void {
|
||||
[this.master, this.details].forEach(s => s.setEncoding(encoding, mode));
|
||||
}
|
||||
}
|
||||
@@ -133,7 +131,7 @@ class StateChange {
|
||||
this.metadata = false;
|
||||
}
|
||||
|
||||
public combine(other: StateChange) {
|
||||
combine(other: StateChange) {
|
||||
this.indentation = this.indentation || other.indentation;
|
||||
this.selectionStatus = this.selectionStatus || other.selectionStatus;
|
||||
this.mode = this.mode || other.mode;
|
||||
@@ -158,28 +156,28 @@ interface StateDelta {
|
||||
|
||||
class State {
|
||||
private _selectionStatus: string;
|
||||
public get selectionStatus(): string { return this._selectionStatus; }
|
||||
get selectionStatus(): string { return this._selectionStatus; }
|
||||
|
||||
private _mode: string;
|
||||
public get mode(): string { return this._mode; }
|
||||
get mode(): string { return this._mode; }
|
||||
|
||||
private _encoding: string;
|
||||
public get encoding(): string { return this._encoding; }
|
||||
get encoding(): string { return this._encoding; }
|
||||
|
||||
private _EOL: string;
|
||||
public get EOL(): string { return this._EOL; }
|
||||
get EOL(): string { return this._EOL; }
|
||||
|
||||
private _indentation: string;
|
||||
public get indentation(): string { return this._indentation; }
|
||||
get indentation(): string { return this._indentation; }
|
||||
|
||||
private _tabFocusMode: boolean;
|
||||
public get tabFocusMode(): boolean { return this._tabFocusMode; }
|
||||
get tabFocusMode(): boolean { return this._tabFocusMode; }
|
||||
|
||||
private _screenReaderMode: boolean;
|
||||
public get screenReaderMode(): boolean { return this._screenReaderMode; }
|
||||
get screenReaderMode(): boolean { return this._screenReaderMode; }
|
||||
|
||||
private _metadata: string;
|
||||
public get metadata(): string { return this._metadata; }
|
||||
get metadata(): string { return this._metadata; }
|
||||
|
||||
constructor() {
|
||||
this._selectionStatus = null;
|
||||
@@ -191,7 +189,7 @@ class State {
|
||||
this._metadata = null;
|
||||
}
|
||||
|
||||
public update(update: StateDelta): StateChange {
|
||||
update(update: StateDelta): StateChange {
|
||||
const e = new StateChange();
|
||||
let somethingChanged = false;
|
||||
|
||||
@@ -282,7 +280,6 @@ function hide(el: HTMLElement): void {
|
||||
}
|
||||
|
||||
export class EditorStatus implements IStatusbarItem {
|
||||
|
||||
private state: State;
|
||||
private element: HTMLElement;
|
||||
private tabFocusModeElement: HTMLElement;
|
||||
@@ -300,8 +297,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
private screenReaderExplanation: ScreenReaderDetectedExplanation;
|
||||
|
||||
constructor(
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
|
||||
@@ -314,7 +310,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.state = new State();
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): IDisposable {
|
||||
render(container: HTMLElement): IDisposable {
|
||||
this.element = append(container, $('.editor-statusbar-item'));
|
||||
|
||||
this.tabFocusModeElement = append(this.element, $('a.editor-status-tabfocusmode.status-bar-info'));
|
||||
@@ -370,7 +366,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
}
|
||||
}
|
||||
},
|
||||
this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()),
|
||||
this.editorService.onDidActiveEditorChange(() => this.updateStatusBar()),
|
||||
this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)),
|
||||
this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange(e.resource)),
|
||||
TabFocus.onDidChangeTabFocus(e => this.onTabFocusModeChange()),
|
||||
@@ -545,73 +541,81 @@ export class EditorStatus implements IStatusbarItem {
|
||||
TabFocus.setTabFocusMode(false);
|
||||
}
|
||||
|
||||
private onEditorsChanged(): void {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
const control = getEditorWidget(activeEditor);
|
||||
private updateStatusBar(): void {
|
||||
const activeControl = this.editorService.activeControl;
|
||||
const activeCodeEditor = activeControl ? getCodeEditor(activeControl.getControl()) : void 0;
|
||||
|
||||
// Update all states
|
||||
this.onScreenReaderModeChange(control);
|
||||
this.onSelectionChange(control);
|
||||
this.onModeChange(control);
|
||||
this.onEOLChange(control);
|
||||
this.onEncodingChange(activeEditor);
|
||||
this.onIndentationChange(control);
|
||||
this.onMetadataChange(activeEditor);
|
||||
this.onScreenReaderModeChange(activeCodeEditor);
|
||||
this.onSelectionChange(activeCodeEditor);
|
||||
this.onModeChange(activeCodeEditor);
|
||||
this.onEOLChange(activeCodeEditor);
|
||||
this.onEncodingChange(activeControl);
|
||||
this.onIndentationChange(activeCodeEditor);
|
||||
this.onMetadataChange(activeControl);
|
||||
|
||||
// Dispose old active editor listeners
|
||||
dispose(this.activeEditorListeners);
|
||||
|
||||
// Attach new listeners to active editor
|
||||
if (control) {
|
||||
if (activeCodeEditor) {
|
||||
|
||||
// Hook Listener for Configuration changes
|
||||
this.activeEditorListeners.push(control.onDidChangeConfiguration((event: IConfigurationChangedEvent) => {
|
||||
this.activeEditorListeners.push(activeCodeEditor.onDidChangeConfiguration((event: IConfigurationChangedEvent) => {
|
||||
if (event.accessibilitySupport) {
|
||||
this.onScreenReaderModeChange(control);
|
||||
this.onScreenReaderModeChange(activeCodeEditor);
|
||||
}
|
||||
}));
|
||||
|
||||
// Hook Listener for Selection changes
|
||||
this.activeEditorListeners.push(control.onDidChangeCursorPosition((event: ICursorPositionChangedEvent) => {
|
||||
this.onSelectionChange(control);
|
||||
this.activeEditorListeners.push(activeCodeEditor.onDidChangeCursorPosition((event: ICursorPositionChangedEvent) => {
|
||||
this.onSelectionChange(activeCodeEditor);
|
||||
}));
|
||||
|
||||
// Hook Listener for mode changes
|
||||
this.activeEditorListeners.push(control.onDidChangeModelLanguage((event: IModelLanguageChangedEvent) => {
|
||||
this.onModeChange(control);
|
||||
this.activeEditorListeners.push(activeCodeEditor.onDidChangeModelLanguage((event: IModelLanguageChangedEvent) => {
|
||||
this.onModeChange(activeCodeEditor);
|
||||
}));
|
||||
|
||||
// Hook Listener for content changes
|
||||
this.activeEditorListeners.push(control.onDidChangeModelContent((e) => {
|
||||
this.onEOLChange(control);
|
||||
this.activeEditorListeners.push(activeCodeEditor.onDidChangeModelContent((e) => {
|
||||
this.onEOLChange(activeCodeEditor);
|
||||
|
||||
let selections = activeCodeEditor.getSelections();
|
||||
for (let i = 0; i < e.changes.length; i++) {
|
||||
if (selections.some(selection => Range.areIntersecting(selection, e.changes[i].range))) {
|
||||
this.onSelectionChange(activeCodeEditor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// Hook Listener for content options changes
|
||||
this.activeEditorListeners.push(control.onDidChangeModelOptions((event: IModelOptionsChangedEvent) => {
|
||||
this.onIndentationChange(control);
|
||||
this.activeEditorListeners.push(activeCodeEditor.onDidChangeModelOptions((event: IModelOptionsChangedEvent) => {
|
||||
this.onIndentationChange(activeCodeEditor);
|
||||
}));
|
||||
}
|
||||
|
||||
// Handle binary editors
|
||||
else if (activeEditor instanceof BaseBinaryResourceEditor || activeEditor instanceof BinaryResourceDiffEditor) {
|
||||
else if (activeControl instanceof BaseBinaryResourceEditor || activeControl instanceof BinaryResourceDiffEditor) {
|
||||
const binaryEditors: BaseBinaryResourceEditor[] = [];
|
||||
if (activeEditor instanceof BinaryResourceDiffEditor) {
|
||||
const details = activeEditor.getDetailsEditor();
|
||||
if (activeControl instanceof BinaryResourceDiffEditor) {
|
||||
const details = activeControl.getDetailsEditor();
|
||||
if (details instanceof BaseBinaryResourceEditor) {
|
||||
binaryEditors.push(details);
|
||||
}
|
||||
|
||||
const master = activeEditor.getMasterEditor();
|
||||
const master = activeControl.getMasterEditor();
|
||||
if (master instanceof BaseBinaryResourceEditor) {
|
||||
binaryEditors.push(master);
|
||||
}
|
||||
} else {
|
||||
binaryEditors.push(activeEditor);
|
||||
binaryEditors.push(activeControl);
|
||||
}
|
||||
|
||||
binaryEditors.forEach(editor => {
|
||||
this.activeEditorListeners.push(editor.onMetadataChanged(metadata => {
|
||||
this.onMetadataChange(activeEditor);
|
||||
this.onMetadataChange(activeControl);
|
||||
}));
|
||||
});
|
||||
}
|
||||
@@ -742,7 +746,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.updateState(info);
|
||||
}
|
||||
|
||||
private onEncodingChange(e: IBaseEditor): void {
|
||||
private onEncodingChange(e?: IBaseEditor): void {
|
||||
if (e && !this.isActiveEditor(e)) {
|
||||
return;
|
||||
}
|
||||
@@ -750,7 +754,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
const info: StateDelta = { encoding: null };
|
||||
|
||||
// We only support text based editors
|
||||
if (getEditorWidget(e)) {
|
||||
if (e && (isCodeEditor(e.getControl()) || isDiffEditor(e.getControl()))) {
|
||||
const encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(e.input);
|
||||
if (encodingSupport) {
|
||||
const rawEncoding = encodingSupport.getEncoding();
|
||||
@@ -767,11 +771,11 @@ export class EditorStatus implements IStatusbarItem {
|
||||
}
|
||||
|
||||
private onResourceEncodingChange(resource: uri): void {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
if (activeEditor) {
|
||||
const activeResource = toResource(activeEditor.input, { supportSideBySide: true });
|
||||
const activeControl = this.editorService.activeControl;
|
||||
if (activeControl) {
|
||||
const activeResource = toResource(activeControl.input, { supportSideBySide: true });
|
||||
if (activeResource && activeResource.toString() === resource.toString()) {
|
||||
return this.onEncodingChange(<IBaseEditor>activeEditor); // only update if the encoding changed for the active resource
|
||||
return this.onEncodingChange(<IBaseEditor>activeControl); // only update if the encoding changed for the active resource
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -782,10 +786,10 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.updateState(info);
|
||||
}
|
||||
|
||||
private isActiveEditor(e: IBaseEditor): boolean {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
private isActiveEditor(control: IBaseEditor): boolean {
|
||||
const activeControl = this.editorService.activeControl;
|
||||
|
||||
return activeEditor && e && activeEditor === e;
|
||||
return activeControl && activeControl === control;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -798,7 +802,7 @@ function isWritableCodeEditor(codeEditor: ICodeEditor): boolean {
|
||||
}
|
||||
|
||||
function isWritableBaseEditor(e: IBaseEditor): boolean {
|
||||
return isWritableCodeEditor(getEditorWidget(e));
|
||||
return e && isWritableCodeEditor(getCodeEditor(e.getControl()));
|
||||
}
|
||||
|
||||
export class ShowLanguageExtensionsAction extends Action {
|
||||
@@ -822,15 +826,15 @@ export class ShowLanguageExtensionsAction extends Action {
|
||||
|
||||
export class ChangeModeAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.editor.changeLanguageMode';
|
||||
public static readonly LABEL = nls.localize('changeMode', "Change Language Mode");
|
||||
static readonly ID = 'workbench.action.editor.changeLanguageMode';
|
||||
static readonly LABEL = nls.localize('changeMode', "Change Language Mode");
|
||||
|
||||
constructor(
|
||||
actionId: string,
|
||||
actionLabel: string,
|
||||
@IModeService private modeService: IModeService,
|
||||
@IModelService private modelService: IModelService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService,
|
||||
@IPreferencesService private preferencesService: IPreferencesService,
|
||||
@@ -840,15 +844,14 @@ export class ChangeModeAction extends Action {
|
||||
super(actionId, actionLabel);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
let activeEditor = this.editorService.getActiveEditor();
|
||||
const editorWidget = getEditorWidget(activeEditor);
|
||||
if (!editorWidget) {
|
||||
run(): TPromise<any> {
|
||||
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
|
||||
if (!activeTextEditorWidget) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
|
||||
const textModel = editorWidget.getModel();
|
||||
const resource = toResource(activeEditor.input, { supportSideBySide: true });
|
||||
const textModel = activeTextEditorWidget.getModel();
|
||||
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: true });
|
||||
|
||||
let hasLanguageSupport = !!resource;
|
||||
if (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
|
||||
@@ -946,17 +949,16 @@ export class ChangeModeAction extends Action {
|
||||
}
|
||||
|
||||
// Change mode for active editor
|
||||
activeEditor = this.editorService.getActiveEditor();
|
||||
const codeOrDiffEditor = getCodeOrDiffEditor(activeEditor);
|
||||
const activeEditor = this.editorService.activeControl;
|
||||
const activeTextEditorWidget = this.editorService.activeTextEditorWidget;
|
||||
const models: ITextModel[] = [];
|
||||
if (codeOrDiffEditor.codeEditor) {
|
||||
const codeEditorModel = codeOrDiffEditor.codeEditor.getModel();
|
||||
if (isCodeEditor(activeTextEditorWidget)) {
|
||||
const codeEditorModel = activeTextEditorWidget.getModel();
|
||||
if (codeEditorModel) {
|
||||
models.push(codeEditorModel);
|
||||
}
|
||||
}
|
||||
if (codeOrDiffEditor.diffEditor) {
|
||||
const diffEditorModel = codeOrDiffEditor.diffEditor.getModel();
|
||||
} else if (isDiffEditor(activeTextEditorWidget)) {
|
||||
const diffEditorModel = activeTextEditorWidget.getModel();
|
||||
if (diffEditorModel) {
|
||||
if (diffEditorModel.original) {
|
||||
models.push(diffEditorModel.original);
|
||||
@@ -1045,42 +1047,42 @@ export interface IChangeEOLEntry extends IPickOpenEntry {
|
||||
|
||||
class ChangeIndentationAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.editor.changeIndentation';
|
||||
public static readonly LABEL = nls.localize('changeIndentation', "Change Indentation");
|
||||
static readonly ID = 'workbench.action.editor.changeIndentation';
|
||||
static readonly LABEL = nls.localize('changeIndentation', "Change Indentation");
|
||||
|
||||
constructor(
|
||||
actionId: string,
|
||||
actionLabel: string,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService
|
||||
) {
|
||||
super(actionId, actionLabel);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
const control = getEditorWidget(activeEditor);
|
||||
if (!control) {
|
||||
run(): TPromise<any> {
|
||||
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
|
||||
if (!activeTextEditorWidget) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
if (!isWritableCodeEditor(control)) {
|
||||
|
||||
if (!isWritableCodeEditor(activeTextEditorWidget)) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
|
||||
}
|
||||
|
||||
const picks = [
|
||||
control.getAction(IndentUsingSpaces.ID),
|
||||
control.getAction(IndentUsingTabs.ID),
|
||||
control.getAction(DetectIndentation.ID),
|
||||
control.getAction(IndentationToSpacesAction.ID),
|
||||
control.getAction(IndentationToTabsAction.ID),
|
||||
control.getAction(TrimTrailingWhitespaceAction.ID)
|
||||
activeTextEditorWidget.getAction(IndentUsingSpaces.ID),
|
||||
activeTextEditorWidget.getAction(IndentUsingTabs.ID),
|
||||
activeTextEditorWidget.getAction(DetectIndentation.ID),
|
||||
activeTextEditorWidget.getAction(IndentationToSpacesAction.ID),
|
||||
activeTextEditorWidget.getAction(IndentationToTabsAction.ID),
|
||||
activeTextEditorWidget.getAction(TrimTrailingWhitespaceAction.ID)
|
||||
].map((a: IEditorAction) => {
|
||||
return {
|
||||
id: a.id,
|
||||
label: a.label,
|
||||
detail: (language === LANGUAGE_DEFAULT) ? null : a.alias,
|
||||
run: () => {
|
||||
control.focus();
|
||||
activeTextEditorWidget.focus();
|
||||
a.run();
|
||||
}
|
||||
};
|
||||
@@ -1095,30 +1097,29 @@ class ChangeIndentationAction extends Action {
|
||||
|
||||
export class ChangeEOLAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.editor.changeEOL';
|
||||
public static readonly LABEL = nls.localize('changeEndOfLine', "Change End of Line Sequence");
|
||||
static readonly ID = 'workbench.action.editor.changeEOL';
|
||||
static readonly LABEL = nls.localize('changeEndOfLine', "Change End of Line Sequence");
|
||||
|
||||
constructor(
|
||||
actionId: string,
|
||||
actionLabel: string,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService
|
||||
) {
|
||||
super(actionId, actionLabel);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
let activeEditor = this.editorService.getActiveEditor();
|
||||
const editorWidget = getEditorWidget(activeEditor);
|
||||
if (!editorWidget) {
|
||||
run(): TPromise<any> {
|
||||
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
|
||||
if (!activeTextEditorWidget) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
|
||||
if (!isWritableCodeEditor(editorWidget)) {
|
||||
if (!isWritableCodeEditor(activeTextEditorWidget)) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
|
||||
}
|
||||
|
||||
const textModel = editorWidget.getModel();
|
||||
const textModel = activeTextEditorWidget.getModel();
|
||||
|
||||
const EOLOptions: IChangeEOLEntry[] = [
|
||||
{ label: nlsEOLLF, eol: EndOfLineSequence.LF },
|
||||
@@ -1129,11 +1130,10 @@ export class ChangeEOLAction extends Action {
|
||||
|
||||
return this.quickOpenService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), autoFocus: { autoFocusIndex: selectedIndex } }).then(eol => {
|
||||
if (eol) {
|
||||
activeEditor = this.editorService.getActiveEditor();
|
||||
const editorWidget = getEditorWidget(activeEditor);
|
||||
if (editorWidget && isWritableCodeEditor(editorWidget)) {
|
||||
const textModel = editorWidget.getModel();
|
||||
textModel.setEOL(eol.eol);
|
||||
const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget);
|
||||
if (activeCodeEditor && isWritableCodeEditor(activeCodeEditor)) {
|
||||
const textModel = activeCodeEditor.getModel();
|
||||
textModel.pushEOL(eol.eol);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1142,13 +1142,13 @@ export class ChangeEOLAction extends Action {
|
||||
|
||||
export class ChangeEncodingAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.editor.changeEncoding';
|
||||
public static readonly LABEL = nls.localize('changeEncoding', "Change File Encoding");
|
||||
static readonly ID = 'workbench.action.editor.changeEncoding';
|
||||
static readonly LABEL = nls.localize('changeEncoding', "Change File Encoding");
|
||||
|
||||
constructor(
|
||||
actionId: string,
|
||||
actionLabel: string,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService,
|
||||
@ITextResourceConfigurationService private textResourceConfigurationService: ITextResourceConfigurationService,
|
||||
@IFileService private fileService: IFileService
|
||||
@@ -1156,13 +1156,13 @@ export class ChangeEncodingAction extends Action {
|
||||
super(actionId, actionLabel);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
let activeEditor = this.editorService.getActiveEditor();
|
||||
if (!getEditorWidget(activeEditor) || !activeEditor.input) {
|
||||
run(): TPromise<any> {
|
||||
if (!getCodeEditor(this.editorService.activeTextEditorWidget)) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
|
||||
let encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(activeEditor.input);
|
||||
let activeControl = this.editorService.activeControl;
|
||||
let encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(activeControl.input);
|
||||
if (!encodingSupport) {
|
||||
return this.quickOpenService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]);
|
||||
}
|
||||
@@ -1181,7 +1181,7 @@ export class ChangeEncodingAction extends Action {
|
||||
|
||||
if (encodingSupport instanceof UntitledEditorInput) {
|
||||
pickActionPromise = TPromise.as(saveWithEncodingPick);
|
||||
} else if (!isWritableBaseEditor(activeEditor)) {
|
||||
} else if (!isWritableBaseEditor(activeControl)) {
|
||||
pickActionPromise = TPromise.as(reopenWithEncodingPick);
|
||||
} else {
|
||||
pickActionPromise = this.quickOpenService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
|
||||
@@ -1192,7 +1192,7 @@ export class ChangeEncodingAction extends Action {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
const resource = toResource(activeEditor.input, { supportSideBySide: true });
|
||||
const resource = toResource(activeControl.input, { supportSideBySide: true });
|
||||
|
||||
return TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */)
|
||||
.then(() => {
|
||||
@@ -1249,8 +1249,8 @@ export class ChangeEncodingAction extends Action {
|
||||
autoFocus: { autoFocusIndex: typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : void 0 }
|
||||
}).then(encoding => {
|
||||
if (encoding) {
|
||||
activeEditor = this.editorService.getActiveEditor();
|
||||
encodingSupport = toEditorWithEncodingSupport(activeEditor.input);
|
||||
activeControl = this.editorService.activeControl;
|
||||
encodingSupport = toEditorWithEncodingSupport(activeControl.input);
|
||||
if (encodingSupport && encodingSupport.getEncoding() !== encoding.id) {
|
||||
encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
|
||||
}
|
||||
@@ -1274,7 +1274,7 @@ class ScreenReaderDetectedExplanation extends Themable {
|
||||
super(themeService);
|
||||
}
|
||||
|
||||
public get visible(): boolean {
|
||||
get visible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
@@ -1294,7 +1294,7 @@ class ScreenReaderDetectedExplanation extends Themable {
|
||||
}
|
||||
}
|
||||
|
||||
public show(anchorElement: HTMLElement): void {
|
||||
show(anchorElement: HTMLElement): void {
|
||||
this._visible = true;
|
||||
|
||||
this.contextViewService.showContextView({
|
||||
@@ -1318,7 +1318,7 @@ class ScreenReaderDetectedExplanation extends Themable {
|
||||
});
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
hide(): void {
|
||||
this.contextViewService.hideContextView();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench>.part.editor>.content .editor-group-container:not(.active) .breadcrumbs-control {
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.selected .monaco-icon-label,
|
||||
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.focused .monaco-icon-label {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
|
||||
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.selected .hint-more,
|
||||
.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.focused .hint-more {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
|
||||
/* todo@joh move somewhere else */
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree>.input {
|
||||
padding: 5px 9px;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree>.tree {
|
||||
height: calc(100% - 36px);
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree.inactive>.input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree.inactive>.tree {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree .monaco-highlighted-label .highlight{
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#424242"/></svg>
|
||||
|
After Width: | Height: | Size: 253 B |
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#C5C5C5"/></svg>
|
||||
|
After Width: | Height: | Size: 253 B |
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#C5C5C5"/></svg>
|
||||
|
After Width: | Height: | Size: 253 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#424242"/></svg>
|
||||
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 253 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle fill="#424242" cx="8" cy="8" r="4"/></svg>
|
||||
|
After Width: | Height: | Size: 167 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle fill="#C5C5C5" cx="8" cy="8" r="4"/></svg>
|
||||
|
After Width: | Height: | Size: 167 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#e8e8e8" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.428 8L12 10.573 10.572 12 8 9.428 5.428 12 4 10.573 6.572 8 4 5.428 5.427 4 8 6.572 10.573 4 12 5.428 9.428 8z" fill="#E8E8E8"/></svg>
|
||||
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 241 B |
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 307 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
|
||||
|
After Width: | Height: | Size: 307 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.428 8L12 10.573 10.572 12 8 9.428 5.428 12 4 10.573 6.572 8 4 5.428 5.427 4 8 6.572 10.573 4 12 5.428 9.428 8z" fill="#424242"/></svg>
|
||||
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 241 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><path fill="#1E1E1E" d="M16 12h-2v2h-2v2H0V5h2V3h2V1h12v11z"/><g fill="#C5C5C5"><path d="M3 5h9v8h1V4H3zM5 2v1h9v8h1V2zM1 6v9h10V6H1zm8 7H7.5L6 11.5 4.5 13H3l2.3-2.3L3 8.5h1.5L6 10l1.5-1.5H9l-2.3 2.3L9 13z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 335 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><path fill="#F6F6F6" d="M16 12h-2v2h-2v2H0V5h2V3h2V1h12v11z"/><g fill="#424242"><path d="M3 5h9v8h1V4H3zM5 2v1h9v8h1V2zM1 6v9h10V6H1zm8 7H7.5L6 11.5 4.5 13H3l2.3-2.3L3 8.5h1.5L6 10l1.5-1.5H9l-2.3 2.3L9 13z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 335 B |
@@ -1,92 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench > .editor > .content.dragging > .monaco-sash {
|
||||
display: none; /* hide sashes while dragging editors around */
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor .one-editor-silo.centered .editor-container {
|
||||
border-style: none solid;
|
||||
border-width: 0px 1px;
|
||||
}
|
||||
|
||||
#monaco-workbench-editor-move-overlay,
|
||||
#monaco-workbench-editor-drop-overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
#monaco-workbench-editor-drop-overlay {
|
||||
opacity: 0; /* initially not visible until moving around */
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo {
|
||||
position: absolute;
|
||||
box-sizing: border-box; /* use border box to be able to draw a border as separator between editors */
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo.editor-one {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo.dragging {
|
||||
z-index: 70;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.dragging {
|
||||
border-left: 1px solid;
|
||||
border-right: 1px solid;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.dragging {
|
||||
border-top: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-two,
|
||||
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.editor-three {
|
||||
border-left: 1px solid;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-two,
|
||||
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.editor-three {
|
||||
border-top: 1px solid;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.vertical-layout > .one-editor-silo.draggedunder {
|
||||
transition: left 200ms ease-out;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.vertical-layout > .editor-three.draggedunder {
|
||||
transition-property: right;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.horizontal-layout > .one-editor-silo.draggedunder {
|
||||
transition: top 200ms ease-out;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content.horizontal-layout > .editor-three.draggedunder {
|
||||
transition-property: bottom;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo > .container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench > .editor > .content > .one-editor-silo > .container > .editor-container {
|
||||
height: calc(100% - 35px); /* Editor is below editor title */
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
#monaco-workbench-editor-drop-overlay {
|
||||
position: absolute;
|
||||
z-index: 10000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#monaco-workbench-editor-drop-overlay > .editor-group-overlay-indicator {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
pointer-events: none; /* very important to not take events away from the parent */
|
||||
|
||||
opacity: 0; /* hidden initially */
|
||||
transition: opacity 150ms ease-out;
|
||||
}
|
||||
|
||||
#monaco-workbench-editor-drop-overlay > .editor-group-overlay-indicator.overlay-move-transition {
|
||||
transition: top 70ms ease-out, left 70ms ease-out, width 70ms ease-out, height 70ms ease-out, opacity 150ms ease-out;
|
||||
}
|
||||
120
src/vs/workbench/browser/parts/editor/media/editorgroupview.css
Normal file
@@ -0,0 +1,120 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Container */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.empty {
|
||||
opacity: 0.5; /* dimmed to indicate inactive state */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.empty.active,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.empty.dragged-over {
|
||||
opacity: 1; /* indicate active/dragged-over group through undimmed state */
|
||||
}
|
||||
|
||||
/* Letterpress */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-letterpress {
|
||||
display: none; /* only visible when empty */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.empty > .editor-group-letterpress {
|
||||
display: block;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
height: calc(100% - 70px); /* centered below toolbar */
|
||||
max-width: 260px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
background-size: 70% 70%;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content.empty .editor-group-container.empty > .editor-group-letterpress {
|
||||
background-size: 100% 100%; /* larger for empty editor part */
|
||||
height: 100%; /* no toolbar in this case */
|
||||
}
|
||||
|
||||
/* Title */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.title-border-bottom::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
background-color: var(--title-border-bottom-color);
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.empty > .title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Toolbar */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content:not(.empty) .editor-group-container.empty > .editor-group-container-toolbar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .action-label {
|
||||
display: block;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
min-width: 28px;
|
||||
background-size: 16px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group {
|
||||
background-image: url('close-big.svg');
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group,
|
||||
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .editor-group-container-toolbar .close-editor-group {
|
||||
background-image: url('close-big-inverse.svg');
|
||||
}
|
||||
|
||||
/* Editor */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .editor-container {
|
||||
height: calc(100% - 35px); /* below title control */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.empty > .editor-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .editor-container > .editor-instance {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .grid-view-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/** Letter press styling for empty editor */
|
||||
.monaco-workbench > .part.editor.empty {
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
background-size: 260px 260px;
|
||||
}
|
||||
@@ -70,10 +70,10 @@
|
||||
}
|
||||
|
||||
.monaco-shell.vs .screen-reader-detected-explanation .cancel {
|
||||
background: url('close-big.svg') center center no-repeat;
|
||||
background: url('close-statusview.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel,
|
||||
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel {
|
||||
background: url('close-big-dark.svg') center center no-repeat;
|
||||
background: url('close-statusview-inverse.svg') center center no-repeat;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Title Label */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label {
|
||||
line-height: 35px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
position: relative;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .monaco-icon-label::before {
|
||||
height: 35px; /* tweak the icon size of the editor labels when icons are enabled */
|
||||
}
|
||||
|
||||
/* Title Actions */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions {
|
||||
display: flex;
|
||||
flex: initial;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .title-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action {
|
||||
background: url('close-dirty.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action,
|
||||
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action {
|
||||
background: url('close-dirty-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action:hover {
|
||||
background: url('close.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action:hover,
|
||||
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.dirty .title-actions .close-editor-action:hover {
|
||||
background: url('close-inverse.svg') center center no-repeat;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title > .label-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
/* Title Label */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label {
|
||||
line-height: 35px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
position: relative;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .no-tabs.title-label {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .monaco-icon-label::before {
|
||||
height: 35px; /* tweak the icon size of the editor labels when icons are enabled */
|
||||
}
|
||||
|
||||
/* Breadcrumbs */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control {
|
||||
flex: 1 50%;
|
||||
overflow: hidden;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.preview .monaco-breadcrumb-item {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
|
||||
content: '/';
|
||||
opacity: 1;
|
||||
height: inherit;
|
||||
width: inherit;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
/* {{SQL CARBON EDIT}} */
|
||||
.monaco-workbench.windows > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
|
||||
content: '/';
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before {
|
||||
/* workspace folder, item following workspace folder, or relative path -> hide first seperator */
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::after {
|
||||
/* use dot separator for workspace folder */
|
||||
content: '•';
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child {
|
||||
padding-right: 4px; /* does not have trailing separator*/
|
||||
}
|
||||
|
||||
/* Title Actions */
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions {
|
||||
display: flex;
|
||||
flex: initial;
|
||||
opacity: 0.5;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .title-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
|
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2b282e}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 15H0V1h16v14z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M1 2v12h14V2H1zm13 11H2v-3h12v3zm0-5H2V5h12v3z" id="iconBg"/><g id="iconFg" style="display: none;"><path class="icon-vs-fg" d="M14 8H2V5h12v3zm0 2H2v3h12v-3z"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>SplitScreenVertical_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M16,1V15H0V1Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M1,2V14H15V2ZM7,13H2V5H7Zm7,0H9V5h5Z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 519 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 15H0V1h16v14z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M1 2v12h14V2H1zm13 11H2v-3h12v3zm0-5H2V5h12v3z" id="iconBg"/><g id="iconFg" style="display: none;"><path class="icon-vs-fg" d="M14 8H2V5h12v3zm0 2H2v3h12v-3z"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>SplitScreenVertical_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M16,1V15H0V1Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M1,2V14H15V2ZM7,13H2V5H7Zm7,0H9V5h5Z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 519 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 -1 16 16" enable-background="new 0 -1 16 16"><path fill="#C5C5C5" d="M1 1v12h14v-12h-14zm1 3h4.999v8h-4.999v-8zm12 8h-5.001v-8h5.001v8z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>SplitScreenHorizontal_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M16,1V15H0V1Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M1,2V14H15V2ZM14,13H2V10H14Zm0-5H2V5H14Z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 525 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 -1 16 16" enable-background="new 0 -1 16 16"><path fill="#656565" d="M1 1v12h14v-12h-14zm1 3h4.999v8h-4.999v-8zm12 8h-5.001v-8h5.001v8z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>SplitScreenHorizontal_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M16,1V15H0V1Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M1,2V14H15V2ZM14,13H2V10H14Zm0-5H2V5H14Z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 525 B |
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#C5C5C5;}
|
||||
</style>
|
||||
<g id="outline">
|
||||
</g>
|
||||
<g id="icon_x5F_bg">
|
||||
<path class="st0" d="M7.6,5H3c0,0-1,0-1,1c0,0.8,0,5.4,0,8c0,1,1,1,1,1s1.5,0,3,0s3,0,3,0s1,0,1-1c0-2.6,0-6.9,0-6.9L7.6,5z M9,14
|
||||
H3V6h4v2h2V14z"/>
|
||||
<path class="st0" d="M9.6,3H5c0,0-1,0-1,1h5v0.9L10.2,6H11v7c1,0,1-1,1-1V5.1L9.6,3z"/>
|
||||
<path class="st0" d="M11.6,1H7c0,0-1,0-1,1h5v0.9L12.2,4H13v7c1,0,1-1,1-1V3.1L11.6,1z"/>
|
||||
</g>
|
||||
<g id="color_x5F_action">
|
||||
</g>
|
||||
<g id="icon_x5F_fg">
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 822 B |
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#656565;}
|
||||
</style>
|
||||
<g id="outline">
|
||||
</g>
|
||||
<g id="icon_x5F_bg">
|
||||
<path class="st0" d="M7.6,5H3c0,0-1,0-1,1c0,0.8,0,5.4,0,8c0,1,1,1,1,1s1.5,0,3,0s3,0,3,0s1,0,1-1c0-2.6,0-6.9,0-6.9L7.6,5z M9,14
|
||||
H3V6h4v2h2V14z"/>
|
||||
<path class="st0" d="M9.6,3H5c0,0-1,0-1,1h5v0.9L10.2,6H11v7c1,0,1-1,1-1V5.1L9.6,3z"/>
|
||||
<path class="st0" d="M11.6,1H7c0,0-1,0-1,1h5v0.9L12.2,4H13v7c1,0,1-1,1-1V3.1L11.6,1z"/>
|
||||
</g>
|
||||
<g id="color_x5F_action">
|
||||
</g>
|
||||
<g id="icon_x5F_fg">
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 822 B |
@@ -1,228 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Title Container */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.empty {
|
||||
background: inherit !important; /* prevents some ugly flickering when opening first tab */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.tabs > .monaco-scrollable-element {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.tabs > .monaco-scrollable-element .scrollbar {
|
||||
z-index: 3; /* on top of tabs */
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Tabs Container */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container {
|
||||
display: flex;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container.scroll {
|
||||
overflow: scroll !important;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Tab */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab {
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
height: 35px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-right,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-off {
|
||||
padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab close button is not left */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-fit {
|
||||
width: 120px;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink {
|
||||
min-width: 60px;
|
||||
flex-basis: 0; /* all tabs are even */
|
||||
flex-grow: 1; /* all tabs grow even */
|
||||
max-width: fit-content;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-left::after,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-off::after {
|
||||
content: "";
|
||||
display: flex;
|
||||
flex: 0;
|
||||
width: 5px; /* Reserve space to hide tab fade when close button is left or off (fixes https://github.com/Microsoft/vscode/issues/45728) */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-left {
|
||||
min-width: 80px; /* make more room for close button when it shows to the left */
|
||||
padding-right: 5px; /* we need less room when sizing is shrink */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dragged {
|
||||
will-change: transform; /* forces tab to be drawn on a separate layer (fixes https://github.com/Microsoft/vscode/issues/18733) */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dragged-over div {
|
||||
pointer-events: none; /* prevents cursor flickering (fixes https://github.com/Microsoft/vscode/issues/38753) */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-left {
|
||||
flex-direction: row-reverse;
|
||||
padding-left: 0;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
/* Tab Label */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink .tab-label {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink > .tab-label::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 5px;
|
||||
opacity: 1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-description-container {
|
||||
overflow: visible; /* fixes https://github.com/Microsoft/vscode/issues/20182 */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container {
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .monaco-icon-label::before {
|
||||
height: 16px; /* tweak the icon size of the editor labels when icons are enabled */
|
||||
}
|
||||
|
||||
/* Tab Close */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab > .tab-close {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
width: 28px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close {
|
||||
flex: 0;
|
||||
overflow: hidden; /* let the close button be pushed out of view when sizing is set to shrink to make more room... */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty.close-button-right.sizing-shrink > .tab-close,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-right.sizing-shrink:hover > .tab-close {
|
||||
overflow: visible; /* ...but still show the close button on hover and when dirty */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off > .tab-close {
|
||||
display: none; /* hide the close action bar when we are configured to hide it */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.active > .tab-close .action-label, /* always show it for active tab */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab > .tab-close .action-label:focus, /* always show it on focus */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab:hover > .tab-close .action-label, /* always show it on hover */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.active:hover > .tab-close .action-label, /* always show it on hover */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab.dirty > .tab-close .action-label { /* always show it for dirty tabs */
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active > .tab-close .action-label, /* show dimmed for inactive group */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* show dimmed for inactive group */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty > .tab-close .action-label, /* show dimmed for inactive group */
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab:hover > .tab-close .action-label { /* show dimmed for inactive group */
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab > .tab-close .action-label {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-size: 16px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action {
|
||||
background: url('close-dirty.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action,
|
||||
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action {
|
||||
background: url('close-dirty-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover {
|
||||
background: url('close.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover,
|
||||
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dirty .close-editor-action:hover {
|
||||
background: url('close-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
/* No Tab Close Button */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off {
|
||||
padding-right: 10px; /* give a little bit more room if close button is off */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-off {
|
||||
padding-right: 5px; /* we need less room when sizing is shrink */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off.dirty {
|
||||
background-repeat: no-repeat;
|
||||
background-position-y: center;
|
||||
background-position-x: calc(100% - 6px); /* to the right of the tab label */
|
||||
padding-right: 28px; /* make room for dirty indication when we are running without close button */
|
||||
}
|
||||
|
||||
.vs .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off.dirty {
|
||||
background-image: url('close-dirty.svg');
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off.dirty,
|
||||
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off.dirty {
|
||||
background-image: url('close-dirty-inverse.svg');
|
||||
}
|
||||
|
||||
/* Editor Actions */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions {
|
||||
cursor: default;
|
||||
flex: initial;
|
||||
padding-left: 4px;
|
||||
}
|
||||
281
src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css
Normal file
@@ -0,0 +1,281 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Title Container */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .monaco-scrollable-element {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs > .monaco-scrollable-element .scrollbar {
|
||||
z-index: 3; /* on top of tabs */
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Tabs Container */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container {
|
||||
display: flex;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container.scroll {
|
||||
overflow: scroll !important;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Tab */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab {
|
||||
position: relative;
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
height: 35px;
|
||||
box-sizing: border-box;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-right,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon-theme.close-button-off {
|
||||
padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab close button is not left */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit {
|
||||
width: 120px;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink {
|
||||
min-width: 60px;
|
||||
flex-basis: 0; /* all tabs are even */
|
||||
flex-grow: 1; /* all tabs grow even */
|
||||
max-width: fit-content;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-left::after,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-off::after {
|
||||
content: '';
|
||||
display: flex;
|
||||
flex: 0;
|
||||
width: 5px; /* Reserve space to hide tab fade when close button is left or off (fixes https://github.com/Microsoft/vscode/issues/45728) */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-left {
|
||||
min-width: 80px; /* make more room for close button when it shows to the left */
|
||||
padding-right: 5px; /* we need less room when sizing is shrink */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged {
|
||||
will-change: transform; /* forces tab to be drawn on a separate layer (fixes https://github.com/Microsoft/vscode/issues/18733) */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged-over div {
|
||||
pointer-events: none; /* prevents cursor flickering (fixes https://github.com/Microsoft/vscode/issues/38753) */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-left {
|
||||
flex-direction: row-reverse;
|
||||
padding-left: 0;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
/* Tab border top/bottom */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-top-container,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-bottom-container {
|
||||
display: none; /* hidden by default until a color is provided (see below) */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 6; /* over possible title border */
|
||||
pointer-events: none;
|
||||
background-color: var(--tab-border-top-color);
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 6; /* over possible title border */
|
||||
pointer-events: none;
|
||||
background-color: var(--tab-border-bottom-color);
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
/* Tab Label */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink .tab-label {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 5px;
|
||||
opacity: 1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-description-container {
|
||||
overflow: visible; /* fixes https://github.com/Microsoft/vscode/issues/20182 */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container {
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-description-container {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before {
|
||||
height: 16px; /* tweak the icon size of the editor labels when icons are enabled */
|
||||
}
|
||||
|
||||
/* Tab Close */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-close {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
width: 28px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close {
|
||||
flex: 0;
|
||||
overflow: hidden; /* let the close button be pushed out of view when sizing is set to shrink to make more room... */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.close-button-right.sizing-shrink > .tab-close,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink:hover > .tab-close {
|
||||
overflow: visible; /* ...but still show the close button on hover and when dirty */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off > .tab-close {
|
||||
display: none; /* hide the close action bar when we are configured to hide it */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active > .tab-close .action-label, /* always show it for active tab */
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-close .action-label:focus, /* always show it on focus */
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover > .tab-close .action-label, /* always show it on hover */
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* always show it on hover */
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-close .action-label { /* always show it for dirty tabs */
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-close .action-label, /* show dimmed for inactive group */
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, /* show dimmed for inactive group */
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-close .action-label, /* show dimmed for inactive group */
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-close .action-label { /* show dimmed for inactive group */
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-close .action-label {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-size: 16px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action {
|
||||
background: url('close-dirty.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action,
|
||||
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action {
|
||||
background: url('close-dirty-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover {
|
||||
background: url('close.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover,
|
||||
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty .close-editor-action:hover {
|
||||
background: url('close-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
/* No Tab Close Button */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off {
|
||||
padding-right: 10px; /* give a little bit more room if close button is off */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-off {
|
||||
padding-right: 5px; /* we need less room when sizing is shrink */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty {
|
||||
background-repeat: no-repeat;
|
||||
background-position-y: center;
|
||||
background-position-x: calc(100% - 6px); /* to the right of the tab label */
|
||||
padding-right: 28px; /* make room for dirty indication when we are running without close button */
|
||||
}
|
||||
|
||||
.vs .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty {
|
||||
background-image: url('close-dirty.svg');
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty,
|
||||
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty {
|
||||
background-image: url('close-dirty-inverse.svg');
|
||||
}
|
||||
|
||||
/* Editor Actions */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions {
|
||||
cursor: default;
|
||||
flex: initial;
|
||||
padding-left: 4px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
/* Breadcrumbs */
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control {
|
||||
flex: 1 100%;
|
||||
height: 25px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label::before {
|
||||
height: 18px; /* tweak the icon size of the editor labels when icons are enabled */
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item {
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
/* .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:not(:last-child):not(:hover):not(.focused):not(.file) {
|
||||
min-width: 33px;
|
||||
} */
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
|
||||
.vs .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace {
|
||||
opacity: 1;
|
||||
background: url('Paragraph_16x_nohalo.svg') center center no-repeat;
|
||||
background: url('paragraph.svg') center center no-repeat;
|
||||
}
|
||||
.vs-dark .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace,
|
||||
.hc-black .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace {
|
||||
opacity: 1;
|
||||
background: url('Paragraph_16x_nohalo_inversep.svg') center center no-repeat;
|
||||
background: url('paragraph-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .monaco-workbench .textdiff-editor-action.toggleIgnoreTrimWhitespace.is-checked {
|
||||
|
||||
@@ -5,36 +5,31 @@
|
||||
|
||||
/* Editor Label */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label {
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label {
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo.centered > .container > .title .title-label {
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a {
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a {
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .monaco-icon-label::before,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .monaco-icon-label::before,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label span,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label span {
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .monaco-icon-label::before,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label h2,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Title Actions */
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label {
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions .action-label,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions .action-label {
|
||||
display: block;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
@@ -44,69 +39,38 @@
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label,
|
||||
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label {
|
||||
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions .action-label,
|
||||
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions .action-label {
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .editor-actions .action-label .label,
|
||||
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-actions .action-label .label {
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .editor-actions .action-label .label,
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions .action-label .label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Drag Cursor */
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo > .container > .title,
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo > .container > .title.tabs .scrollbar .slider,
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo > .container > .title .monaco-icon-label::before,
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo > .container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo > .container > .title .title-label span {
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title {
|
||||
cursor: -webkit-grab;
|
||||
}
|
||||
|
||||
#monaco-workbench-editor-move-overlay,
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag,
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag > .container > .title,
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag > .container > .title.tabs .scrollbar .slider,
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag > .container > .title .monaco-icon-label::before,
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag > .container > .title .title-label a,
|
||||
.monaco-workbench > .part.editor > .content.multiple-groups > .one-editor-silo.drag > .container > .title .title-label span {
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
|
||||
.monaco-workbench .close-editor-action {
|
||||
.monaco-workbench > .part.editor > .content .editor-group-container > .title .close-editor-action {
|
||||
background: url('close.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .close-editor-action,
|
||||
.hc-black .monaco-workbench .close-editor-action {
|
||||
.vs-dark .monaco-workbench > .part.editor > .content .editor-group-container > .title .close-editor-action,
|
||||
.hc-black .monaco-workbench > .part.editor > .content .editor-group-container > .title .close-editor-action {
|
||||
background: url('close-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action {
|
||||
background: url('split-editor-vertical.svg') center center no-repeat;
|
||||
}
|
||||
/* Drag and Drop Feedback */
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action,
|
||||
.hc-black .monaco-workbench > .part.editor > .content.vertical-layout > .one-editor-silo > .container > .title .split-editor-action {
|
||||
background: url('split-editor-vertical-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action {
|
||||
background: url('split-editor-horizontal.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action,
|
||||
.hc-black .monaco-workbench > .part.editor > .content.horizontal-layout > .one-editor-silo > .container > .title .split-editor-action {
|
||||
background: url('split-editor-horizontal-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-workbench .show-group-editors-action {
|
||||
background: url('stackview.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-workbench .show-group-editors-action,
|
||||
.hc-black .monaco-workbench .show-group-editors-action {
|
||||
background: url('stackview-inverse.svg') center center no-repeat;
|
||||
}
|
||||
.monaco-editor-group-drag-image {
|
||||
display: inline-block;
|
||||
padding: 1px 7px;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
}
|
||||
@@ -5,147 +5,222 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/notabstitle';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { toResource } from 'vs/workbench/common/editor';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
|
||||
import 'vs/css!./media/notabstitlecontrol';
|
||||
import { toResource, Verbosity, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor/titleControl';
|
||||
import { ResourceLabel } from 'vs/workbench/browser/labels';
|
||||
import { Verbosity } from 'vs/platform/editor/common/editor';
|
||||
import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
|
||||
import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass } from 'vs/base/browser/dom';
|
||||
import { IEditorPartOptions, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
|
||||
export class NoTabsTitleControl extends TitleControl {
|
||||
private titleContainer: HTMLElement;
|
||||
private editorLabel: ResourceLabel;
|
||||
private lastRenderedActiveEditor: IEditorInput;
|
||||
|
||||
public create(parent: HTMLElement): void {
|
||||
super.create(parent);
|
||||
|
||||
protected create(parent: HTMLElement): void {
|
||||
this.titleContainer = parent;
|
||||
this.titleContainer.draggable = true;
|
||||
|
||||
//Container listeners
|
||||
this.registerContainerListeners();
|
||||
|
||||
// Gesture Support
|
||||
Gesture.addTarget(this.titleContainer);
|
||||
|
||||
// Pin on double click
|
||||
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e)));
|
||||
|
||||
// Detect mouse click
|
||||
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CLICK, (e: MouseEvent) => this.onTitleClick(e)));
|
||||
|
||||
// Detect touch
|
||||
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
|
||||
const labelContainer = document.createElement('div');
|
||||
addClass(labelContainer, 'label-container');
|
||||
this.titleContainer.appendChild(labelContainer);
|
||||
|
||||
// Editor Label
|
||||
this.editorLabel = this.instantiationService.createInstance(ResourceLabel, this.titleContainer, void 0);
|
||||
this.toUnbind.push(this.editorLabel);
|
||||
this.toUnbind.push(this.editorLabel.onClick(e => this.onTitleLabelClick(e)));
|
||||
this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, void 0));
|
||||
this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e)));
|
||||
|
||||
// Breadcrumbs
|
||||
this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, extraClasses: ['no-tabs-breadcrumbs'] });
|
||||
|
||||
// Right Actions Container
|
||||
const actionsContainer = document.createElement('div');
|
||||
DOM.addClass(actionsContainer, 'title-actions');
|
||||
addClass(actionsContainer, 'title-actions');
|
||||
this.titleContainer.appendChild(actionsContainer);
|
||||
|
||||
// Editor actions toolbar
|
||||
this.createEditorActionsToolBar(actionsContainer);
|
||||
}
|
||||
|
||||
private registerContainerListeners(): void {
|
||||
|
||||
// Group dragging
|
||||
this.enableGroupDragging(this.titleContainer);
|
||||
|
||||
// Pin on double click
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e)));
|
||||
|
||||
// Detect mouse click
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.CLICK, (e: MouseEvent) => this.onTitleClick(e)));
|
||||
|
||||
// Detect touch
|
||||
this._register(addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
|
||||
|
||||
// Context Menu
|
||||
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu({ group: this.context, editor: this.context.activeEditor }, e, this.titleContainer)));
|
||||
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu({ group: this.context, editor: this.context.activeEditor }, e, this.titleContainer)));
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
|
||||
this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
|
||||
}
|
||||
|
||||
private onTitleLabelClick(e: MouseEvent): void {
|
||||
DOM.EventHelper.stop(e, false);
|
||||
if (!this.dragged) {
|
||||
setTimeout(() => this.quickOpenService.show()); // delayed to let the onTitleClick() come first which can cause a focus change which can close quick open
|
||||
}
|
||||
EventHelper.stop(e, false);
|
||||
|
||||
// delayed to let the onTitleClick() come first which can cause a focus change which can close quick open
|
||||
setTimeout(() => this.quickOpenService.show());
|
||||
}
|
||||
|
||||
private onTitleDoubleClick(e: MouseEvent): void {
|
||||
DOM.EventHelper.stop(e);
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
EventHelper.stop(e);
|
||||
|
||||
const group = this.context;
|
||||
|
||||
this.editorGroupService.pinEditor(group, group.activeEditor);
|
||||
this.group.pinEditor();
|
||||
}
|
||||
|
||||
private onTitleClick(e: MouseEvent | GestureEvent): void {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const group = this.context;
|
||||
|
||||
// Close editor on middle mouse click
|
||||
if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) {
|
||||
this.closeOneEditorAction.run({ groupId: group.id, editorIndex: group.indexOf(group.activeEditor) }).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
// Focus editor group unless:
|
||||
// - click on toolbar: should trigger actions within
|
||||
// - mouse click: do not focus group if there are more than one as it otherwise makes group DND funky
|
||||
// - touch: always focus
|
||||
else if ((this.stacks.groups.length === 1 || !(e instanceof MouseEvent)) && !DOM.isAncestor(((e as GestureEvent).initialTarget || e.target || e.srcElement) as HTMLElement, this.editorActionsToolbar.getContainer())) {
|
||||
this.editorGroupService.focusGroup(group);
|
||||
this.group.closeEditor(this.group.activeEditor);
|
||||
}
|
||||
}
|
||||
|
||||
protected doRefresh(): void {
|
||||
const group = this.context;
|
||||
const editor = group && group.activeEditor;
|
||||
getPreferredHeight(): number {
|
||||
return EDITOR_TITLE_HEIGHT;
|
||||
}
|
||||
|
||||
openEditor(editor: IEditorInput): void {
|
||||
this.ifActiveEditorChanged(() => this.redraw());
|
||||
}
|
||||
|
||||
closeEditor(editor: IEditorInput): void {
|
||||
this.ifActiveEditorChanged(() => this.redraw());
|
||||
}
|
||||
|
||||
closeEditors(editors: IEditorInput[]): void {
|
||||
this.ifActiveEditorChanged(() => this.redraw());
|
||||
}
|
||||
|
||||
closeAllEditors(): void {
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
moveEditor(editor: IEditorInput, fromIndex: number, targetIndex: number): void {
|
||||
this.ifActiveEditorChanged(() => this.redraw());
|
||||
}
|
||||
|
||||
pinEditor(editor: IEditorInput): void {
|
||||
this.ifEditorIsActive(editor, () => this.redraw());
|
||||
}
|
||||
|
||||
setActive(isActive: boolean): void {
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
updateEditorLabel(editor: IEditorInput): void {
|
||||
this.ifEditorIsActive(editor, () => this.redraw());
|
||||
}
|
||||
|
||||
updateEditorDirty(editor: IEditorInput): void {
|
||||
this.ifEditorIsActive(editor, () => {
|
||||
if (editor.isDirty()) {
|
||||
addClass(this.titleContainer, 'dirty');
|
||||
} else {
|
||||
removeClass(this.titleContainer, 'dirty');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void {
|
||||
if (oldOptions.labelFormat !== newOptions.labelFormat) {
|
||||
this.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
updateStyles(): void {
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
private ifActiveEditorChanged(fn: () => void): void {
|
||||
if (
|
||||
!this.lastRenderedActiveEditor && this.group.activeEditor || // active editor changed from null => editor
|
||||
this.lastRenderedActiveEditor && !this.group.activeEditor || // active editor changed from editor => null
|
||||
!this.group.isActive(this.lastRenderedActiveEditor) // active editor changed from editorA => editorB
|
||||
) {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
|
||||
private ifEditorIsActive(editor: IEditorInput, fn: () => void): void {
|
||||
if (this.group.isActive(editor)) {
|
||||
fn(); // only run if editor is current active
|
||||
}
|
||||
}
|
||||
|
||||
private redraw(): void {
|
||||
const editor = this.group.activeEditor;
|
||||
this.lastRenderedActiveEditor = editor;
|
||||
|
||||
const isEditorPinned = this.group.isPinned(this.group.activeEditor);
|
||||
const isGroupActive = this.accessor.activeGroup === this.group;
|
||||
|
||||
// Update Breadcrumbs
|
||||
if (this.breadcrumbsControl) {
|
||||
if (isGroupActive) {
|
||||
this.breadcrumbsControl.update();
|
||||
toggleClass(this.breadcrumbsControl.domNode, 'preview', !isEditorPinned);
|
||||
} else {
|
||||
this.breadcrumbsControl.hide();
|
||||
}
|
||||
}
|
||||
|
||||
// Clear if there is no editor
|
||||
if (!editor) {
|
||||
removeClass(this.titleContainer, 'dirty');
|
||||
this.editorLabel.clear();
|
||||
this.clearEditorActionsToolbar();
|
||||
|
||||
return; // return early if we are being closed
|
||||
}
|
||||
|
||||
const isPinned = group.isPinned(group.activeEditor);
|
||||
const isActive = this.stacks.isActive(group);
|
||||
// Otherwise render it
|
||||
else {
|
||||
// Dirty state
|
||||
this.updateEditorDirty(editor);
|
||||
|
||||
// Activity state
|
||||
if (isActive) {
|
||||
DOM.addClass(this.titleContainer, 'active');
|
||||
} else {
|
||||
DOM.removeClass(this.titleContainer, 'active');
|
||||
// Editor Label
|
||||
const resource = toResource(editor, { supportSideBySide: true });
|
||||
const name = editor.getName() || '';
|
||||
|
||||
const { labelFormat } = this.accessor.partOptions;
|
||||
let description: string;
|
||||
if (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden()) {
|
||||
description = ''; // hide description when showing breadcrumbs
|
||||
} else if (labelFormat === 'default' && !isGroupActive) {
|
||||
description = ''; // hide description when group is not active and style is 'default'
|
||||
} else {
|
||||
description = editor.getDescription(this.getVerbosity(labelFormat)) || '';
|
||||
}
|
||||
|
||||
let title = editor.getTitle(Verbosity.LONG);
|
||||
if (description === title) {
|
||||
title = ''; // dont repeat what is already shown
|
||||
}
|
||||
|
||||
this.editorLabel.setLabel({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
|
||||
if (isGroupActive) {
|
||||
this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND);
|
||||
} else {
|
||||
this.editorLabel.element.style.color = this.getColor(TAB_UNFOCUSED_ACTIVE_FOREGROUND);
|
||||
}
|
||||
|
||||
// Update Editor Actions Toolbar
|
||||
this.updateEditorActionsToolbar();
|
||||
}
|
||||
|
||||
// Dirty state
|
||||
if (editor.isDirty()) {
|
||||
DOM.addClass(this.titleContainer, 'dirty');
|
||||
} else {
|
||||
DOM.removeClass(this.titleContainer, 'dirty');
|
||||
}
|
||||
|
||||
// Editor Label
|
||||
const resource = toResource(editor, { supportSideBySide: true });
|
||||
const name = editor.getName() || '';
|
||||
|
||||
const labelFormat = this.editorGroupService.getTabOptions().labelFormat;
|
||||
let description: string;
|
||||
if (labelFormat === 'default' && !isActive) {
|
||||
description = ''; // hide description when group is not active and style is 'default'
|
||||
} else {
|
||||
description = editor.getDescription(this.getVerbosity(labelFormat)) || '';
|
||||
}
|
||||
|
||||
let title = editor.getTitle(Verbosity.LONG);
|
||||
if (description === title) {
|
||||
title = ''; // dont repeat what is already shown
|
||||
}
|
||||
|
||||
this.editorLabel.setLabel({ name, description, resource }, { title, italic: !isPinned, extraClasses: ['title-label'] });
|
||||
if (isActive) {
|
||||
this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND);
|
||||
} else {
|
||||
this.editorLabel.element.style.color = this.getColor(TAB_UNFOCUSED_ACTIVE_FOREGROUND);
|
||||
}
|
||||
|
||||
// Update Editor Actions Toolbar
|
||||
this.updateEditorActionsToolbar();
|
||||
}
|
||||
|
||||
private getVerbosity(style: string): Verbosity {
|
||||
@@ -155,4 +230,16 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
default: return Verbosity.MEDIUM;
|
||||
}
|
||||
}
|
||||
|
||||
protected prepareEditorActions(editorActions: IToolbarActions): { primaryEditorActions: IAction[], secondaryEditorActions: IAction[] } {
|
||||
const isGroupActive = this.accessor.activeGroup === this.group;
|
||||
|
||||
// Group active: show all actions
|
||||
if (isGroupActive) {
|
||||
return super.prepareEditorActions(editorActions);
|
||||
}
|
||||
|
||||
// Group inactive: only show close action
|
||||
return { primaryEditorActions: editorActions.primary.filter(action => action.id === CLOSE_EDITOR_COMMAND_ID), secondaryEditorActions: [] };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
@@ -19,27 +19,29 @@ export interface IRangeHighlightDecoration {
|
||||
isWholeLine?: boolean;
|
||||
}
|
||||
|
||||
export class RangeHighlightDecorations implements IDisposable {
|
||||
export class RangeHighlightDecorations extends Disposable {
|
||||
|
||||
private rangeHighlightDecorationId: string = null;
|
||||
private editor: ICodeEditor = null;
|
||||
private editorDisposables: IDisposable[] = [];
|
||||
|
||||
private readonly _onHighlightRemoved: Emitter<void> = new Emitter<void>();
|
||||
public readonly onHighlghtRemoved: Event<void> = this._onHighlightRemoved.event;
|
||||
private readonly _onHighlightRemoved: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onHighlghtRemoved(): Event<void> { return this._onHighlightRemoved.event; }
|
||||
|
||||
constructor(@IWorkbenchEditorService private editorService: IWorkbenchEditorService) {
|
||||
constructor(@IEditorService private editorService: IEditorService) {
|
||||
super();
|
||||
}
|
||||
|
||||
public removeHighlightRange() {
|
||||
removeHighlightRange() {
|
||||
if (this.editor && this.editor.getModel() && this.rangeHighlightDecorationId) {
|
||||
this.editor.deltaDecorations([this.rangeHighlightDecorationId], []);
|
||||
this._onHighlightRemoved.fire();
|
||||
}
|
||||
|
||||
this.rangeHighlightDecorationId = null;
|
||||
}
|
||||
|
||||
public highlightRange(range: IRangeHighlightDecoration, editor?: ICodeEditor) {
|
||||
highlightRange(range: IRangeHighlightDecoration, editor?: ICodeEditor) {
|
||||
editor = editor ? editor : this.getEditor(range);
|
||||
if (editor) {
|
||||
this.doHighlightRange(editor, range);
|
||||
@@ -48,20 +50,23 @@ export class RangeHighlightDecorations implements IDisposable {
|
||||
|
||||
private doHighlightRange(editor: ICodeEditor, selectionRange: IRangeHighlightDecoration) {
|
||||
this.removeHighlightRange();
|
||||
|
||||
editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => {
|
||||
this.rangeHighlightDecorationId = changeAccessor.addDecoration(selectionRange.range, this.createRangeHighlightDecoration(selectionRange.isWholeLine));
|
||||
});
|
||||
|
||||
this.setEditor(editor);
|
||||
}
|
||||
|
||||
private getEditor(resourceRange: IRangeHighlightDecoration): ICodeEditor {
|
||||
const activeInput = this.editorService.getActiveEditorInput();
|
||||
const resource = activeInput && activeInput.getResource();
|
||||
const activeEditor = this.editorService.activeEditor;
|
||||
const resource = activeEditor && activeEditor.getResource();
|
||||
if (resource) {
|
||||
if (resource.toString() === resourceRange.resource.toString()) {
|
||||
return <ICodeEditor>this.editorService.getActiveEditor().getControl();
|
||||
return this.editorService.activeTextEditorWidget as ICodeEditor;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -107,7 +112,9 @@ export class RangeHighlightDecorations implements IDisposable {
|
||||
return (isWholeLine ? RangeHighlightDecorations._WHOLE_LINE_RANGE_HIGHLIGHT : RangeHighlightDecorations._RANGE_HIGHLIGHT);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
dispose() {
|
||||
super.dispose();
|
||||
|
||||
if (this.editor && this.editor.getModel()) {
|
||||
this.removeHighlightRange();
|
||||
this.disposeEditorListeners();
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/resourceviewer';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as mimes from 'vs/base/common/mime';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
@@ -24,79 +21,26 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
||||
interface MapExtToMediaMimes {
|
||||
[index: string]: string;
|
||||
}
|
||||
|
||||
// Known media mimes that we can handle
|
||||
const mapExtToMediaMimes: MapExtToMediaMimes = {
|
||||
'.bmp': 'image/bmp',
|
||||
'.gif': 'image/gif',
|
||||
'.jpg': 'image/jpg',
|
||||
'.jpeg': 'image/jpg',
|
||||
'.jpe': 'image/jpg',
|
||||
'.png': 'image/png',
|
||||
'.tiff': 'image/tiff',
|
||||
'.tif': 'image/tiff',
|
||||
'.ico': 'image/x-icon',
|
||||
'.tga': 'image/x-tga',
|
||||
'.psd': 'image/vnd.adobe.photoshop',
|
||||
'.webp': 'image/webp',
|
||||
'.mid': 'audio/midi',
|
||||
'.midi': 'audio/midi',
|
||||
'.mp4a': 'audio/mp4',
|
||||
'.mpga': 'audio/mpeg',
|
||||
'.mp2': 'audio/mpeg',
|
||||
'.mp2a': 'audio/mpeg',
|
||||
'.mp3': 'audio/mpeg',
|
||||
'.m2a': 'audio/mpeg',
|
||||
'.m3a': 'audio/mpeg',
|
||||
'.oga': 'audio/ogg',
|
||||
'.ogg': 'audio/ogg',
|
||||
'.spx': 'audio/ogg',
|
||||
'.aac': 'audio/x-aac',
|
||||
'.wav': 'audio/x-wav',
|
||||
'.wma': 'audio/x-ms-wma',
|
||||
'.mp4': 'video/mp4',
|
||||
'.mp4v': 'video/mp4',
|
||||
'.mpg4': 'video/mp4',
|
||||
'.mpeg': 'video/mpeg',
|
||||
'.mpg': 'video/mpeg',
|
||||
'.mpe': 'video/mpeg',
|
||||
'.m1v': 'video/mpeg',
|
||||
'.m2v': 'video/mpeg',
|
||||
'.ogv': 'video/ogg',
|
||||
'.qt': 'video/quicktime',
|
||||
'.mov': 'video/quicktime',
|
||||
'.webm': 'video/webm',
|
||||
'.mkv': 'video/x-matroska',
|
||||
'.mk3d': 'video/x-matroska',
|
||||
'.mks': 'video/x-matroska',
|
||||
'.wmv': 'video/x-ms-wmv',
|
||||
'.flv': 'video/x-flv',
|
||||
'.avi': 'video/x-msvideo',
|
||||
'.movie': 'video/x-sgi-movie'
|
||||
};
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
export interface IResourceDescriptor {
|
||||
resource: URI;
|
||||
name: string;
|
||||
size: number;
|
||||
etag: string;
|
||||
mime: string;
|
||||
readonly resource: URI;
|
||||
readonly name: string;
|
||||
readonly size: number;
|
||||
readonly etag: string;
|
||||
readonly mime: string;
|
||||
}
|
||||
|
||||
class BinarySize {
|
||||
public static readonly KB = 1024;
|
||||
public static readonly MB = BinarySize.KB * BinarySize.KB;
|
||||
public static readonly GB = BinarySize.MB * BinarySize.KB;
|
||||
public static readonly TB = BinarySize.GB * BinarySize.KB;
|
||||
static readonly KB = 1024;
|
||||
static readonly MB = BinarySize.KB * BinarySize.KB;
|
||||
static readonly GB = BinarySize.MB * BinarySize.KB;
|
||||
static readonly TB = BinarySize.GB * BinarySize.KB;
|
||||
|
||||
public static formatSize(size: number): string {
|
||||
static formatSize(size: number): string {
|
||||
if (size < BinarySize.KB) {
|
||||
return nls.localize('sizeB', "{0}B", size);
|
||||
}
|
||||
@@ -129,8 +73,9 @@ export class ResourceViewer {
|
||||
|
||||
private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally
|
||||
|
||||
public static show(
|
||||
static show(
|
||||
descriptor: IResourceDescriptor,
|
||||
fileService: IFileService,
|
||||
container: HTMLElement,
|
||||
scrollbar: DomScrollableElement,
|
||||
openInternalClb: (uri: URI) => void,
|
||||
@@ -143,7 +88,7 @@ export class ResourceViewer {
|
||||
|
||||
// Images
|
||||
if (ResourceViewer.isImageResource(descriptor)) {
|
||||
return ImageView.create(container, descriptor, scrollbar, openExternalClb, metadataClb);
|
||||
return ImageView.create(container, descriptor, fileService, scrollbar, openExternalClb, metadataClb);
|
||||
}
|
||||
|
||||
// Large Files
|
||||
@@ -160,37 +105,26 @@ export class ResourceViewer {
|
||||
}
|
||||
|
||||
private static isImageResource(descriptor: IResourceDescriptor) {
|
||||
const mime = ResourceViewer.getMime(descriptor);
|
||||
const mime = getMime(descriptor);
|
||||
|
||||
return mime.indexOf('image/') >= 0;
|
||||
}
|
||||
|
||||
private static getMime(descriptor: IResourceDescriptor): string {
|
||||
let mime = descriptor.mime;
|
||||
if (!mime && descriptor.resource.scheme !== Schemas.data) {
|
||||
const ext = paths.extname(descriptor.resource.toString());
|
||||
if (ext) {
|
||||
mime = mapExtToMediaMimes[ext.toLowerCase()];
|
||||
}
|
||||
}
|
||||
|
||||
return mime || mimes.MIME_BINARY;
|
||||
}
|
||||
}
|
||||
|
||||
class ImageView {
|
||||
private static readonly MAX_IMAGE_SIZE = BinarySize.MB; // showing images inline is memory intense, so we have a limit
|
||||
private static readonly BASE64_MARKER = 'base64,';
|
||||
|
||||
public static create(
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptor: IResourceDescriptor,
|
||||
fileService: IFileService,
|
||||
scrollbar: DomScrollableElement,
|
||||
openExternalClb: (uri: URI) => void,
|
||||
metadataClb: (meta: string) => void
|
||||
): ResourceViewerContext | null {
|
||||
if (ImageView.shouldShowImageInline(descriptor)) {
|
||||
return InlineImageView.create(container, descriptor, scrollbar, metadataClb);
|
||||
return InlineImageView.create(container, descriptor, fileService, scrollbar, metadataClb);
|
||||
}
|
||||
|
||||
LargeImageView.create(container, descriptor, openExternalClb);
|
||||
@@ -219,7 +153,7 @@ class ImageView {
|
||||
}
|
||||
|
||||
class LargeImageView {
|
||||
public static create(
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptor: IResourceDescriptor,
|
||||
openExternalClb: (uri: URI) => void
|
||||
@@ -245,7 +179,7 @@ class LargeImageView {
|
||||
}
|
||||
|
||||
class FileTooLargeFileView {
|
||||
public static create(
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptor: IResourceDescriptor,
|
||||
scrollbar: DomScrollableElement,
|
||||
@@ -268,7 +202,7 @@ class FileTooLargeFileView {
|
||||
}
|
||||
|
||||
class FileSeemsBinaryFileView {
|
||||
public static create(
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptor: IResourceDescriptor,
|
||||
scrollbar: DomScrollableElement,
|
||||
@@ -302,29 +236,32 @@ class FileSeemsBinaryFileView {
|
||||
type Scale = number | 'fit';
|
||||
|
||||
class ZoomStatusbarItem extends Themable implements IStatusbarItem {
|
||||
|
||||
static instance: ZoomStatusbarItem;
|
||||
|
||||
showTimeout: number;
|
||||
public static instance: ZoomStatusbarItem;
|
||||
|
||||
private statusBarItem: HTMLElement;
|
||||
|
||||
private onSelectScale?: (scale: Scale) => void;
|
||||
|
||||
constructor(
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IThemeService themeService: IThemeService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
ZoomStatusbarItem.instance = this;
|
||||
this.toUnbind.push(editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
|
||||
|
||||
this._register(editorService.onDidActiveEditorChange(() => this.onActiveEditorChanged()));
|
||||
}
|
||||
|
||||
private onEditorsChanged(): void {
|
||||
private onActiveEditorChanged(): void {
|
||||
this.hide();
|
||||
this.onSelectScale = void 0;
|
||||
}
|
||||
|
||||
public show(scale: Scale, onSelectScale: (scale: number) => void) {
|
||||
show(scale: Scale, onSelectScale: (scale: number) => void) {
|
||||
clearTimeout(this.showTimeout);
|
||||
this.showTimeout = setTimeout(() => {
|
||||
this.onSelectScale = onSelectScale;
|
||||
@@ -333,11 +270,11 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public hide() {
|
||||
hide() {
|
||||
this.statusBarItem.style.display = 'none';
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): IDisposable {
|
||||
render(container: HTMLElement): IDisposable {
|
||||
if (!this.statusBarItem && container) {
|
||||
this.statusBarItem = $(container).a()
|
||||
.addClass('.zoom-statusbar-item')
|
||||
@@ -419,22 +356,15 @@ class InlineImageView {
|
||||
*/
|
||||
private static readonly PIXELATION_THRESHOLD = 3;
|
||||
|
||||
/**
|
||||
* Chrome is caching images very aggressively and so we use the ETag information to find out if
|
||||
* we need to bypass the cache or not. We could always bypass the cache everytime we show the image
|
||||
* however that has very bad impact on memory consumption because each time the image gets shown,
|
||||
* memory grows (see also https://github.com/electron/electron/issues/6275)
|
||||
*/
|
||||
private static IMAGE_RESOURCE_ETAG_CACHE = new LRUCache<string, { etag: string, src: string }>(100);
|
||||
|
||||
/**
|
||||
* Store the scale and position of an image so it can be restored when changing editor tabs
|
||||
*/
|
||||
private static readonly imageStateCache = new LRUCache<string, ImageState>(100);
|
||||
|
||||
public static create(
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptor: IResourceDescriptor,
|
||||
fileService: IFileService,
|
||||
scrollbar: DomScrollableElement,
|
||||
metadataClb: (meta: string) => void
|
||||
) {
|
||||
@@ -601,7 +531,7 @@ class InlineImageView {
|
||||
$(container)
|
||||
.empty()
|
||||
.addClass('image', 'zoom-in')
|
||||
.img({ src: InlineImageView.imageSrc(descriptor) })
|
||||
.img({})
|
||||
.style('visibility', 'hidden')
|
||||
.addClass('scale-to-fit')
|
||||
.on(DOM.EventType.LOAD, (e, i) => {
|
||||
@@ -619,27 +549,33 @@ class InlineImageView {
|
||||
}
|
||||
});
|
||||
|
||||
InlineImageView.imageSrc(descriptor, fileService).then(dataUri => {
|
||||
const imgs = container.getElementsByTagName('img');
|
||||
if (imgs.length) {
|
||||
imgs[0].src = dataUri;
|
||||
}
|
||||
});
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private static imageSrc(descriptor: IResourceDescriptor): string {
|
||||
private static imageSrc(descriptor: IResourceDescriptor, fileService: IFileService): TPromise<string> {
|
||||
if (descriptor.resource.scheme === Schemas.data) {
|
||||
return descriptor.resource.toString(true /* skip encoding */);
|
||||
return TPromise.as(descriptor.resource.toString(true /* skip encoding */));
|
||||
}
|
||||
|
||||
const src = descriptor.resource.toString();
|
||||
return fileService.resolveContent(descriptor.resource, { encoding: 'base64' }).then(data => {
|
||||
const mime = getMime(descriptor);
|
||||
|
||||
let cached = InlineImageView.IMAGE_RESOURCE_ETAG_CACHE.get(src);
|
||||
if (!cached) {
|
||||
cached = { etag: descriptor.etag, src };
|
||||
InlineImageView.IMAGE_RESOURCE_ETAG_CACHE.set(src, cached);
|
||||
}
|
||||
|
||||
if (cached.etag !== descriptor.etag) {
|
||||
cached.etag = descriptor.etag;
|
||||
cached.src = `${src}?${Date.now()}`; // bypass cache with this trick
|
||||
}
|
||||
|
||||
return cached.src;
|
||||
return `data:${mime};base64,${data.value}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMime(descriptor: IResourceDescriptor) {
|
||||
let mime = descriptor.mime;
|
||||
if (!mime && descriptor.resource.scheme !== Schemas.data) {
|
||||
mime = mimes.getMediaMime(descriptor.resource.toString());
|
||||
}
|
||||
return mime || mimes.MIME_BINARY;
|
||||
}
|
||||
|
||||
@@ -6,29 +6,55 @@
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { EditorInput, EditorOptions, SideBySideEditorInput } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, EditorOptions, SideBySideEditorInput, IEditorControl, IEditor } from 'vs/workbench/common/editor';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { IEditorControl, Position, IEditor } from 'vs/platform/editor/common/editor';
|
||||
import { VSash } from 'vs/base/browser/ui/sash/sash';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { SplitView, Sizing, Orientation } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { Event, Relay, anyEvent, mapEvent, Emitter } from 'vs/base/common/event';
|
||||
|
||||
export class SideBySideEditor extends BaseEditor {
|
||||
|
||||
public static readonly ID: string = 'workbench.editor.sidebysideEditor';
|
||||
static readonly ID: string = 'workbench.editor.sidebysideEditor';
|
||||
|
||||
private dimension: DOM.Dimension;
|
||||
get minimumMasterWidth() { return this.masterEditor ? this.masterEditor.minimumWidth : 0; }
|
||||
get maximumMasterWidth() { return this.masterEditor ? this.masterEditor.maximumWidth : Number.POSITIVE_INFINITY; }
|
||||
get minimumMasterHeight() { return this.masterEditor ? this.masterEditor.minimumHeight : 0; }
|
||||
get maximumMasterHeight() { return this.masterEditor ? this.masterEditor.maximumHeight : Number.POSITIVE_INFINITY; }
|
||||
|
||||
get minimumDetailsWidth() { return this.detailsEditor ? this.detailsEditor.minimumWidth : 0; }
|
||||
get maximumDetailsWidth() { return this.detailsEditor ? this.detailsEditor.maximumWidth : Number.POSITIVE_INFINITY; }
|
||||
get minimumDetailsHeight() { return this.detailsEditor ? this.detailsEditor.minimumHeight : 0; }
|
||||
get maximumDetailsHeight() { return this.detailsEditor ? this.detailsEditor.maximumHeight : Number.POSITIVE_INFINITY; }
|
||||
|
||||
// these setters need to exist because this extends from BaseEditor
|
||||
set minimumWidth(value: number) { /* noop */ }
|
||||
set maximumWidth(value: number) { /* noop */ }
|
||||
set minimumHeight(value: number) { /* noop */ }
|
||||
set maximumHeight(value: number) { /* noop */ }
|
||||
|
||||
get minimumWidth() { return this.minimumMasterWidth + this.minimumDetailsWidth; }
|
||||
get maximumWidth() { return this.maximumMasterWidth + this.maximumDetailsWidth; }
|
||||
get minimumHeight() { return this.minimumMasterHeight + this.minimumDetailsHeight; }
|
||||
get maximumHeight() { return this.maximumMasterHeight + this.maximumDetailsHeight; }
|
||||
|
||||
protected masterEditor: BaseEditor;
|
||||
private masterEditorContainer: HTMLElement;
|
||||
|
||||
protected detailsEditor: BaseEditor;
|
||||
|
||||
private masterEditorContainer: HTMLElement;
|
||||
private detailsEditorContainer: HTMLElement;
|
||||
|
||||
private sash: VSash;
|
||||
private splitview: SplitView;
|
||||
private dimension: DOM.Dimension = new DOM.Dimension(0, 0);
|
||||
|
||||
private onDidCreateEditors = this._register(new Emitter<{ width: number; height: number; }>());
|
||||
private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; }>());
|
||||
readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; }> = anyEvent(this.onDidCreateEditors.event, this._onDidSizeConstraintsChange.event);
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@@ -40,36 +66,54 @@ export class SideBySideEditor extends BaseEditor {
|
||||
|
||||
protected createEditor(parent: HTMLElement): void {
|
||||
DOM.addClass(parent, 'side-by-side-editor');
|
||||
this.createSash(parent);
|
||||
|
||||
this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL }));
|
||||
this._register(this.splitview.onDidSashReset(() => this.splitview.distributeViewSizes()));
|
||||
|
||||
this.detailsEditorContainer = DOM.$('.details-editor-container');
|
||||
this.splitview.addView({
|
||||
element: this.detailsEditorContainer,
|
||||
layout: size => this.detailsEditor && this.detailsEditor.layout(new DOM.Dimension(size, this.dimension.height)),
|
||||
minimumSize: 220,
|
||||
maximumSize: Number.POSITIVE_INFINITY,
|
||||
onDidChange: Event.None
|
||||
}, Sizing.Distribute);
|
||||
|
||||
this.masterEditorContainer = DOM.$('.master-editor-container');
|
||||
this.splitview.addView({
|
||||
element: this.masterEditorContainer,
|
||||
layout: size => this.masterEditor && this.masterEditor.layout(new DOM.Dimension(size, this.dimension.height)),
|
||||
minimumSize: 220,
|
||||
maximumSize: Number.POSITIVE_INFINITY,
|
||||
onDidChange: Event.None
|
||||
}, Sizing.Distribute);
|
||||
|
||||
this.updateStyles();
|
||||
}
|
||||
|
||||
public setInput(newInput: SideBySideEditorInput, options?: EditorOptions): TPromise<void> {
|
||||
setInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
|
||||
const oldInput = <SideBySideEditorInput>this.input;
|
||||
return super.setInput(newInput, options)
|
||||
.then(() => this.updateInput(oldInput, newInput, options));
|
||||
return super.setInput(newInput, options, token)
|
||||
.then(() => this.updateInput(oldInput, newInput, options, token));
|
||||
}
|
||||
|
||||
protected setEditorVisible(visible: boolean, position: Position): void {
|
||||
setOptions(options: EditorOptions): void {
|
||||
if (this.masterEditor) {
|
||||
this.masterEditor.setVisible(visible, position);
|
||||
this.masterEditor.setOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
protected setEditorVisible(visible: boolean, group: IEditorGroup): void {
|
||||
if (this.masterEditor) {
|
||||
this.masterEditor.setVisible(visible, group);
|
||||
}
|
||||
if (this.detailsEditor) {
|
||||
this.detailsEditor.setVisible(visible, position);
|
||||
this.detailsEditor.setVisible(visible, group);
|
||||
}
|
||||
super.setEditorVisible(visible, position);
|
||||
super.setEditorVisible(visible, group);
|
||||
}
|
||||
|
||||
public changePosition(position: Position): void {
|
||||
if (this.masterEditor) {
|
||||
this.masterEditor.changePosition(position);
|
||||
}
|
||||
if (this.detailsEditor) {
|
||||
this.detailsEditor.changePosition(position);
|
||||
}
|
||||
super.changePosition(position);
|
||||
}
|
||||
|
||||
public clearInput(): void {
|
||||
clearInput(): void {
|
||||
if (this.masterEditor) {
|
||||
this.masterEditor.clearInput();
|
||||
}
|
||||
@@ -80,57 +124,49 @@ export class SideBySideEditor extends BaseEditor {
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
focus(): void {
|
||||
if (this.masterEditor) {
|
||||
this.masterEditor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
public layout(dimension: DOM.Dimension): void {
|
||||
layout(dimension: DOM.Dimension): void {
|
||||
this.dimension = dimension;
|
||||
this.sash.setDimenesion(this.dimension);
|
||||
this.splitview.layout(dimension.width);
|
||||
}
|
||||
|
||||
public getControl(): IEditorControl {
|
||||
getControl(): IEditorControl {
|
||||
if (this.masterEditor) {
|
||||
return this.masterEditor.getControl();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public getMasterEditor(): IEditor {
|
||||
getMasterEditor(): IEditor {
|
||||
return this.masterEditor;
|
||||
}
|
||||
|
||||
public getDetailsEditor(): IEditor {
|
||||
getDetailsEditor(): IEditor {
|
||||
return this.detailsEditor;
|
||||
}
|
||||
|
||||
public supportsCenteredLayout(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options?: EditorOptions): void {
|
||||
private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
|
||||
if (!newInput.matches(oldInput)) {
|
||||
if (oldInput) {
|
||||
this.disposeEditors();
|
||||
}
|
||||
this.createEditorContainers();
|
||||
|
||||
return this.setNewInput(newInput, options);
|
||||
} else {
|
||||
this.detailsEditor.setInput(newInput.details);
|
||||
this.masterEditor.setInput(newInput.master, options);
|
||||
|
||||
return void 0;
|
||||
return this.setNewInput(newInput, options, token);
|
||||
}
|
||||
|
||||
return TPromise.join([this.detailsEditor.setInput(newInput.details, null, token), this.masterEditor.setInput(newInput.master, options, token)]).then(() => void 0);
|
||||
}
|
||||
|
||||
private setNewInput(newInput: SideBySideEditorInput, options?: EditorOptions): void {
|
||||
private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
|
||||
const detailsEditor = this._createEditor(<EditorInput>newInput.details, this.detailsEditorContainer);
|
||||
const masterEditor = this._createEditor(<EditorInput>newInput.master, this.masterEditorContainer);
|
||||
|
||||
this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options);
|
||||
return this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options, token);
|
||||
}
|
||||
|
||||
private _createEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor {
|
||||
@@ -138,29 +174,26 @@ export class SideBySideEditor extends BaseEditor {
|
||||
|
||||
const editor = descriptor.instantiate(this.instantiationService);
|
||||
editor.create(container);
|
||||
editor.setVisible(this.isVisible(), this.position);
|
||||
editor.setVisible(this.isVisible(), this.group);
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions): TPromise<void> {
|
||||
private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions, token: CancellationToken): TPromise<void> {
|
||||
this.detailsEditor = details;
|
||||
this.masterEditor = master;
|
||||
this.dolayout(this.sash.getVerticalSashLeft());
|
||||
return TPromise.join([this.detailsEditor.setInput(detailsInput), this.masterEditor.setInput(masterInput, options)]).then(() => this.focus());
|
||||
|
||||
this._onDidSizeConstraintsChange.input = anyEvent(
|
||||
mapEvent(details.onDidSizeConstraintsChange, () => undefined),
|
||||
mapEvent(master.onDidSizeConstraintsChange, () => undefined)
|
||||
);
|
||||
|
||||
this.onDidCreateEditors.fire();
|
||||
|
||||
return TPromise.join([this.detailsEditor.setInput(detailsInput, null, token), this.masterEditor.setInput(masterInput, options, token)]).then(() => this.focus());
|
||||
}
|
||||
|
||||
private createEditorContainers(): void {
|
||||
const parentElement = this.getContainer();
|
||||
this.detailsEditorContainer = DOM.append(parentElement, DOM.$('.details-editor-container'));
|
||||
this.detailsEditorContainer.style.position = 'absolute';
|
||||
this.masterEditorContainer = DOM.append(parentElement, DOM.$('.master-editor-container'));
|
||||
this.masterEditorContainer.style.position = 'absolute';
|
||||
|
||||
this.updateStyles();
|
||||
}
|
||||
|
||||
public updateStyles(): void {
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
if (this.masterEditorContainer) {
|
||||
@@ -168,52 +201,24 @@ export class SideBySideEditor extends BaseEditor {
|
||||
}
|
||||
}
|
||||
|
||||
private createSash(parentElement: HTMLElement): void {
|
||||
this.sash = this._register(new VSash(parentElement, 220));
|
||||
this._register(this.sash.onPositionChange(position => this.dolayout(position)));
|
||||
}
|
||||
|
||||
private dolayout(splitPoint: number): void {
|
||||
if (!this.detailsEditor || !this.masterEditor || !this.dimension) {
|
||||
return;
|
||||
}
|
||||
const masterEditorWidth = this.dimension.width - splitPoint;
|
||||
const detailsEditorWidth = this.dimension.width - masterEditorWidth;
|
||||
|
||||
this.detailsEditorContainer.style.width = `${detailsEditorWidth}px`;
|
||||
this.detailsEditorContainer.style.height = `${this.dimension.height}px`;
|
||||
this.detailsEditorContainer.style.left = '0px';
|
||||
|
||||
this.masterEditorContainer.style.width = `${masterEditorWidth}px`;
|
||||
this.masterEditorContainer.style.height = `${this.dimension.height}px`;
|
||||
this.masterEditorContainer.style.left = `${splitPoint}px`;
|
||||
|
||||
this.detailsEditor.layout(new DOM.Dimension(detailsEditorWidth, this.dimension.height));
|
||||
this.masterEditor.layout(new DOM.Dimension(masterEditorWidth, this.dimension.height));
|
||||
}
|
||||
|
||||
private disposeEditors(): void {
|
||||
const parentContainer = this.getContainer();
|
||||
if (this.detailsEditor) {
|
||||
this.detailsEditor.dispose();
|
||||
this.detailsEditor = null;
|
||||
}
|
||||
|
||||
if (this.masterEditor) {
|
||||
this.masterEditor.dispose();
|
||||
this.masterEditor = null;
|
||||
}
|
||||
if (this.detailsEditorContainer) {
|
||||
parentContainer.removeChild(this.detailsEditorContainer);
|
||||
this.detailsEditorContainer = null;
|
||||
}
|
||||
if (this.masterEditorContainer) {
|
||||
parentContainer.removeChild(this.masterEditorContainer);
|
||||
this.masterEditorContainer = null;
|
||||
}
|
||||
|
||||
this.detailsEditorContainer.innerHTML = '';
|
||||
this.masterEditorContainer.innerHTML = '';
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
this.disposeEditors();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,11 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IDiffEditorOptions, IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
|
||||
import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
|
||||
import { TextEditorOptions, EditorInput, EditorOptions, TEXT_DIFF_EDITOR_ID, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, ITextDiffEditor, IEditorMemento } from 'vs/workbench/common/editor';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator';
|
||||
@@ -26,28 +25,28 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/common/editorCommon';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { getCodeOrDiffEditor } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
|
||||
/**
|
||||
* The text editor that leverages the diff text editor for the editing experience.
|
||||
*/
|
||||
export class TextDiffEditor extends BaseTextEditor {
|
||||
export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
|
||||
public static readonly ID = TEXT_DIFF_EDITOR_ID;
|
||||
static readonly ID = TEXT_DIFF_EDITOR_ID;
|
||||
|
||||
private diffNavigator: DiffNavigator;
|
||||
private diffNavigatorDisposables: IDisposable[];
|
||||
private diffNavigatorDisposables: IDisposable[] = [];
|
||||
private nextDiffAction: NavigateAction;
|
||||
private previousDiffAction: NavigateAction;
|
||||
private toggleIgnoreTrimWhitespaceAction: ToggleIgnoreTrimWhitespaceAction;
|
||||
@@ -58,22 +57,25 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
|
||||
@IConfigurationService private readonly _actualConfigurationService: IConfigurationService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@ITextFileService textFileService: ITextFileService
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
) {
|
||||
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
|
||||
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService);
|
||||
|
||||
this.diffNavigatorDisposables = [];
|
||||
this.toUnbind.push(this._actualConfigurationService.onDidChangeConfiguration((e) => {
|
||||
this._register(this._actualConfigurationService.onDidChangeConfiguration((e) => {
|
||||
if (e.affectsConfiguration('diffEditor.ignoreTrimWhitespace')) {
|
||||
this.updateIgnoreTrimWhitespaceAction();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
protected getEditorMemento<T>(storageService: IStorageService, editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento<T> {
|
||||
return new EditorMemento(this.getId(), key, Object.create(null), limit, editorGroupService); // do not persist in storage as diff editors are never persisted
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
if (this.input) {
|
||||
return this.input.getName();
|
||||
}
|
||||
@@ -81,7 +83,7 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
return nls.localize('textDiffEditor', "Text Diff Editor");
|
||||
}
|
||||
|
||||
public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IDiffEditor {
|
||||
createEditorControl(parent: HTMLElement, configuration: ICodeEditorOptions): IDiffEditor {
|
||||
|
||||
// Actions
|
||||
this.nextDiffAction = new NavigateAction(this, true);
|
||||
@@ -89,55 +91,10 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
this.toggleIgnoreTrimWhitespaceAction = new ToggleIgnoreTrimWhitespaceAction(this._actualConfigurationService);
|
||||
this.updateIgnoreTrimWhitespaceAction();
|
||||
|
||||
// Support navigation within the diff editor by overriding the editor service within
|
||||
const delegatingEditorService = this.instantiationService.createInstance(DelegatingWorkbenchEditorService);
|
||||
delegatingEditorService.setEditorOpenHandler((input: EditorInput, options?: EditorOptions, arg3?: any) => {
|
||||
|
||||
// Check if arg4 is a position argument that differs from this editors position
|
||||
if (types.isUndefinedOrNull(arg3) || arg3 === false || arg3 === this.position) {
|
||||
const activeDiffInput = <DiffEditorInput>this.input;
|
||||
if (input && options && activeDiffInput) {
|
||||
|
||||
// Input matches modified side of the diff editor: perform the action on modified side
|
||||
if (input.matches(activeDiffInput.modifiedInput)) {
|
||||
return this.setInput(this.input, options).then(() => this);
|
||||
}
|
||||
|
||||
// Input matches original side of the diff editor: perform the action on original side
|
||||
else if (input.matches(activeDiffInput.originalInput)) {
|
||||
const originalEditor = this.getControl().getOriginalEditor();
|
||||
if (options instanceof TextEditorOptions) {
|
||||
(<TextEditorOptions>options).apply(originalEditor, ScrollType.Smooth);
|
||||
|
||||
return TPromise.as(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
});
|
||||
|
||||
// Create a special child of instantiator that will delegate all calls to openEditor() to the same diff editor if the input matches with the modified one
|
||||
const diffEditorInstantiator = this.instantiationService.createChild(new ServiceCollection([IWorkbenchEditorService, delegatingEditorService]));
|
||||
|
||||
return diffEditorInstantiator.createInstance(DiffEditorWidget, parent, configuration);
|
||||
return this.instantiationService.createInstance(DiffEditorWidget, parent, configuration);
|
||||
}
|
||||
|
||||
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
|
||||
|
||||
// Return early for same input unless we force to open
|
||||
const forceOpen = options && options.forceOpen;
|
||||
if (!forceOpen && input.matches(this.input)) {
|
||||
|
||||
// Still apply options if any (avoiding instanceof here for a reason, do not change!)
|
||||
const textOptions = <TextEditorOptions>options;
|
||||
if (textOptions && types.isFunction(textOptions.apply)) {
|
||||
textOptions.apply(<IDiffEditor>this.getControl(), ScrollType.Smooth);
|
||||
}
|
||||
|
||||
return TPromise.wrap<void>(null);
|
||||
}
|
||||
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
|
||||
|
||||
// Dispose previous diff navigator
|
||||
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
|
||||
@@ -146,27 +103,28 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
this.saveTextDiffEditorViewState(this.input);
|
||||
|
||||
// Set input and resolve
|
||||
return super.setInput(input, options).then(() => {
|
||||
return input.resolve(true).then(resolvedModel => {
|
||||
return super.setInput(input, options, token).then(() => {
|
||||
return input.resolve().then(resolvedModel => {
|
||||
|
||||
// Check for cancellation
|
||||
if (token.isCancellationRequested) {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
// Assert Model Instance
|
||||
if (!(resolvedModel instanceof TextDiffEditorModel) && this.openAsBinary(input, options)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Assert that the current input is still the one we expect. This prevents a race condition when loading a diff takes long and another input was set meanwhile
|
||||
if (!this.input || this.input !== input) {
|
||||
return null;
|
||||
return void 0;
|
||||
}
|
||||
|
||||
// Set Editor Model
|
||||
const diffEditor = <IDiffEditor>this.getControl();
|
||||
diffEditor.setModel((<TextDiffEditorModel>resolvedModel).textDiffEditorModel);
|
||||
const diffEditor = this.getControl();
|
||||
const resolvedDiffEditorModel = <TextDiffEditorModel>resolvedModel;
|
||||
diffEditor.setModel(resolvedDiffEditorModel.textDiffEditorModel);
|
||||
|
||||
// Apply Options from TextOptions
|
||||
let optionsGotApplied = false;
|
||||
if (options && types.isFunction((<TextEditorOptions>options).apply)) {
|
||||
optionsGotApplied = (<TextEditorOptions>options).apply(<IDiffEditor>diffEditor, ScrollType.Immediate);
|
||||
optionsGotApplied = (<TextEditorOptions>options).apply(diffEditor, ScrollType.Immediate);
|
||||
}
|
||||
|
||||
// Otherwise restore View State
|
||||
@@ -175,6 +133,7 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
hasPreviousViewState = this.restoreTextDiffEditorViewState(input);
|
||||
}
|
||||
|
||||
// Diff navigator
|
||||
this.diffNavigator = new DiffNavigator(diffEditor, {
|
||||
alwaysRevealFirst: !optionsGotApplied && !hasPreviousViewState // only reveal first change if we had no options or viewstate
|
||||
});
|
||||
@@ -185,7 +144,11 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
this.previousDiffAction.updateEnablement();
|
||||
}));
|
||||
|
||||
// Enablement of actions
|
||||
this.updateIgnoreTrimWhitespaceAction();
|
||||
|
||||
// Readonly flag
|
||||
diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() });
|
||||
}, error => {
|
||||
|
||||
// In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff.
|
||||
@@ -199,8 +162,11 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
});
|
||||
}
|
||||
|
||||
public supportsCenteredLayout(): boolean {
|
||||
return false;
|
||||
setOptions(options: EditorOptions): void {
|
||||
const textOptions = <TextEditorOptions>options;
|
||||
if (textOptions && types.isFunction(textOptions.apply)) {
|
||||
textOptions.apply(this.getControl(), ScrollType.Smooth);
|
||||
}
|
||||
}
|
||||
|
||||
private restoreTextDiffEditorViewState(input: EditorInput): boolean {
|
||||
@@ -243,7 +209,7 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
modifiedInput.setForceOpenAsBinary();
|
||||
}
|
||||
|
||||
this.editorService.openEditor(binaryDiffInput, options, this.position).done(null, onUnexpectedError);
|
||||
this.editorService.openEditor(binaryDiffInput, options, this.group);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -251,7 +217,7 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected computeConfiguration(configuration: IEditorConfiguration): IEditorOptions {
|
||||
protected computeConfiguration(configuration: IEditorConfiguration): ICodeEditorOptions {
|
||||
const editorConfiguration = super.computeConfiguration(configuration);
|
||||
|
||||
// Handle diff editor specially by merging in diffEditor configuration
|
||||
@@ -262,7 +228,7 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
return editorConfiguration;
|
||||
}
|
||||
|
||||
protected getConfigurationOverrides(): IEditorOptions {
|
||||
protected getConfigurationOverrides(): ICodeEditorOptions {
|
||||
const options: IDiffEditorOptions = super.getConfigurationOverrides();
|
||||
|
||||
options.readOnly = this.isReadOnly();
|
||||
@@ -304,7 +270,7 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
return (<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_BINARY;
|
||||
}
|
||||
|
||||
public clearInput(): void {
|
||||
clearInput(): void {
|
||||
|
||||
// Dispose previous diff navigator
|
||||
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
|
||||
@@ -319,11 +285,11 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
public getDiffNavigator(): DiffNavigator {
|
||||
getDiffNavigator(): DiffNavigator {
|
||||
return this.diffNavigator;
|
||||
}
|
||||
|
||||
public getActions(): IAction[] {
|
||||
getActions(): IAction[] {
|
||||
return [
|
||||
this.toggleIgnoreTrimWhitespaceAction,
|
||||
this.previousDiffAction,
|
||||
@@ -331,7 +297,7 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
];
|
||||
}
|
||||
|
||||
public getControl(): IDiffEditor {
|
||||
getControl(): IDiffEditor {
|
||||
return super.getControl() as IDiffEditor;
|
||||
}
|
||||
|
||||
@@ -370,12 +336,8 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
}
|
||||
|
||||
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState {
|
||||
const editor = getCodeOrDiffEditor(this).diffEditor;
|
||||
if (!editor) {
|
||||
return null; // not supported for non-diff editors
|
||||
}
|
||||
|
||||
const model = editor.getModel();
|
||||
const control = this.getControl();
|
||||
const model = control.getModel();
|
||||
if (!model || !model.modified || !model.original) {
|
||||
return null; // view state always needs a model
|
||||
}
|
||||
@@ -389,7 +351,7 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
return null; // prevent saving view state for a model that is not the expected one
|
||||
}
|
||||
|
||||
return editor.saveViewState();
|
||||
return control.saveViewState();
|
||||
}
|
||||
|
||||
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI {
|
||||
@@ -412,7 +374,7 @@ export class TextDiffEditor extends BaseTextEditor {
|
||||
return URI.from({ scheme: 'diff', path: `${btoa(original.toString())}${btoa(modified.toString())}` });
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
|
||||
|
||||
super.dispose();
|
||||
@@ -437,7 +399,7 @@ class NavigateAction extends Action {
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
run(): TPromise<any> {
|
||||
if (this.next) {
|
||||
this.editor.getDiffNavigator().next();
|
||||
} else {
|
||||
@@ -447,7 +409,7 @@ class NavigateAction extends Action {
|
||||
return null;
|
||||
}
|
||||
|
||||
public updateEnablement(): void {
|
||||
updateEnablement(): void {
|
||||
this.enabled = this.editor.getDiffNavigator().canNavigate();
|
||||
}
|
||||
}
|
||||
@@ -464,12 +426,12 @@ class ToggleIgnoreTrimWhitespaceAction extends Action {
|
||||
this.label = nls.localize('toggleIgnoreTrimWhitespace.label', "Ignore Trim Whitespace");
|
||||
}
|
||||
|
||||
public updateClassName(ignoreTrimWhitespace: boolean): void {
|
||||
updateClassName(ignoreTrimWhitespace: boolean): void {
|
||||
this._isChecked = ignoreTrimWhitespace;
|
||||
this.class = `textdiff-editor-action toggleIgnoreTrimWhitespace${this._isChecked ? ' is-checked' : ''}`;
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
run(): TPromise<any> {
|
||||
this._configurationService.updateValue(`diffEditor.ignoreTrimWhitespace`, !this._isChecked);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6,28 +6,26 @@
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { CodeEditor } from 'vs/editor/browser/codeEditor';
|
||||
import { EditorInput, EditorOptions, EditorViewStateMemento } from 'vs/workbench/common/editor';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { EditorInput, EditorOptions, IEditorMemento, ITextEditor } from 'vs/workbench/common/editor';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { IEditorViewState, IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { Position } from 'vs/platform/editor/common/editor';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Scope } from 'vs/workbench/common/memento';
|
||||
import { getCodeEditor, getCodeOrDiffEditor } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ITextFileService, SaveReason, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { isDiffEditor, isCodeEditor, ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
const TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'textEditorViewState';
|
||||
|
||||
@@ -40,12 +38,12 @@ export interface IEditorConfiguration {
|
||||
* The base class of editors that leverage the text editor for the editing experience. This class is only intended to
|
||||
* be subclassed and not instantiated.
|
||||
*/
|
||||
export abstract class BaseTextEditor extends BaseEditor {
|
||||
export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
private editorControl: IEditor;
|
||||
private _editorContainer: HTMLElement;
|
||||
private hasPendingConfigurationChange: boolean;
|
||||
private lastAppliedEditorOptions: IEditorOptions;
|
||||
private editorViewStateMemento: EditorViewStateMemento<IEditorViewState>;
|
||||
private editorMemento: IEditorMemento<IEditorViewState>;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -55,13 +53,14 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
@ITextResourceConfigurationService private readonly _configurationService: ITextResourceConfigurationService,
|
||||
@IThemeService protected themeService: IThemeService,
|
||||
@ITextFileService private readonly _textFileService: ITextFileService,
|
||||
@IEditorGroupService protected editorGroupService: IEditorGroupService
|
||||
@IEditorService protected editorService: IEditorService,
|
||||
@IEditorGroupsService protected editorGroupService: IEditorGroupsService,
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
|
||||
this.editorViewStateMemento = new EditorViewStateMemento<IEditorViewState>(this.getMemento(storageService, Scope.WORKSPACE), TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
|
||||
this.editorMemento = this.getEditorMemento<IEditorViewState>(storageService, editorGroupService, TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
|
||||
|
||||
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
|
||||
}
|
||||
|
||||
protected get instantiationService(): IInstantiationService {
|
||||
@@ -107,8 +106,10 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
let ariaLabel = this.getAriaLabel();
|
||||
|
||||
// Apply group information to help identify in which group we are
|
||||
if (ariaLabel && typeof this.position === 'number') {
|
||||
ariaLabel = nls.localize('editorLabelWithGroup', "{0}, Group {1}.", ariaLabel, this.position + 1);
|
||||
if (ariaLabel) {
|
||||
if (this.group) {
|
||||
ariaLabel = nls.localize('editorLabelWithGroup', "{0}, {1}.", ariaLabel, this.group.label);
|
||||
}
|
||||
}
|
||||
|
||||
return ariaLabel;
|
||||
@@ -129,25 +130,25 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
|
||||
// Editor for Text
|
||||
this._editorContainer = parent;
|
||||
this.editorControl = this.createEditorControl(parent, this.computeConfiguration(this.configurationService.getValue<IEditorConfiguration>(this.getResource())));
|
||||
this.editorControl = this._register(this.createEditorControl(parent, this.computeConfiguration(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
|
||||
|
||||
// Model & Language changes
|
||||
const codeEditor = getCodeEditor(this);
|
||||
const codeEditor = getCodeEditor(this.editorControl);
|
||||
if (codeEditor) {
|
||||
this.toUnbind.push(codeEditor.onDidChangeModelLanguage(e => this.updateEditorConfiguration()));
|
||||
this.toUnbind.push(codeEditor.onDidChangeModel(e => this.updateEditorConfiguration()));
|
||||
this._register(codeEditor.onDidChangeModelLanguage(e => this.updateEditorConfiguration()));
|
||||
this._register(codeEditor.onDidChangeModel(e => this.updateEditorConfiguration()));
|
||||
}
|
||||
|
||||
// Application & Editor focus change to respect auto save settings
|
||||
if (isCodeEditor(this.editorControl)) {
|
||||
this.toUnbind.push(this.editorControl.onDidBlurEditor(() => this.onEditorFocusLost()));
|
||||
this._register(this.editorControl.onDidBlurEditorWidget(() => this.onEditorFocusLost()));
|
||||
} else if (isDiffEditor(this.editorControl)) {
|
||||
this.toUnbind.push(this.editorControl.getOriginalEditor().onDidBlurEditor(() => this.onEditorFocusLost()));
|
||||
this.toUnbind.push(this.editorControl.getModifiedEditor().onDidBlurEditor(() => this.onEditorFocusLost()));
|
||||
this._register(this.editorControl.getOriginalEditor().onDidBlurEditorWidget(() => this.onEditorFocusLost()));
|
||||
this._register(this.editorControl.getModifiedEditor().onDidBlurEditorWidget(() => this.onEditorFocusLost()));
|
||||
}
|
||||
|
||||
this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorFocusLost()));
|
||||
this.toUnbind.push(DOM.addDisposableListener(window, DOM.EventType.BLUR, () => this.onWindowFocusLost()));
|
||||
this._register(this.editorService.onDidActiveEditorChange(() => this.onEditorFocusLost()));
|
||||
this._register(DOM.addDisposableListener(window, DOM.EventType.BLUR, () => this.onWindowFocusLost()));
|
||||
}
|
||||
|
||||
private onEditorFocusLost(): void {
|
||||
@@ -182,11 +183,11 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
protected createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IEditor {
|
||||
|
||||
// Use a getter for the instantiation service since some subclasses might use scoped instantiation services
|
||||
return this.instantiationService.createInstance(CodeEditor, parent, configuration);
|
||||
return this.instantiationService.createInstance(CodeEditorWidget, parent, configuration, {});
|
||||
}
|
||||
|
||||
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
|
||||
return super.setInput(input, options).then(() => {
|
||||
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
|
||||
return super.setInput(input, options, token).then(() => {
|
||||
|
||||
// Update editor options after having set the input. We do this because there can be
|
||||
// editor input specific options (e.g. an ARIA label depending on the input showing)
|
||||
@@ -195,16 +196,7 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
});
|
||||
}
|
||||
|
||||
public changePosition(position: Position): void {
|
||||
super.changePosition(position);
|
||||
|
||||
// Make sure to update ARIA label if the position of this editor changed
|
||||
if (this.editorControl) {
|
||||
this.editorControl.updateOptions({ ariaLabel: this.computeAriaLabel() });
|
||||
}
|
||||
}
|
||||
|
||||
protected setEditorVisible(visible: boolean, position: Position = null): void {
|
||||
protected setEditorVisible(visible: boolean, group: IEditorGroup): void {
|
||||
|
||||
// Pass on to Editor
|
||||
if (visible) {
|
||||
@@ -214,20 +206,20 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
this.editorControl.onHide();
|
||||
}
|
||||
|
||||
super.setEditorVisible(visible, position);
|
||||
super.setEditorVisible(visible, group);
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
focus(): void {
|
||||
this.editorControl.focus();
|
||||
}
|
||||
|
||||
public layout(dimension: DOM.Dimension): void {
|
||||
layout(dimension: DOM.Dimension): void {
|
||||
|
||||
// Pass on to Editor
|
||||
this.editorControl.layout(dimension);
|
||||
}
|
||||
|
||||
public getControl(): IEditor {
|
||||
getControl(): IEditor {
|
||||
return this.editorControl;
|
||||
}
|
||||
|
||||
@@ -240,16 +232,12 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
return;
|
||||
}
|
||||
|
||||
this.editorViewStateMemento.saveState(resource, this.position, editorViewState);
|
||||
this.editorMemento.saveState(this.group, resource, editorViewState);
|
||||
}
|
||||
|
||||
protected retrieveTextEditorViewState(resource: URI): IEditorViewState {
|
||||
const editor = getCodeOrDiffEditor(this).codeEditor;
|
||||
if (!editor) {
|
||||
return null; // not supported for diff editors
|
||||
}
|
||||
|
||||
const model = editor.getModel();
|
||||
const control = this.getControl() as ICodeEditor;
|
||||
const model = control.getModel();
|
||||
if (!model) {
|
||||
return null; // view state always needs a model
|
||||
}
|
||||
@@ -263,7 +251,7 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
return null; // prevent saving view state for a model that is not the expected one
|
||||
}
|
||||
|
||||
return editor.saveViewState();
|
||||
return control.saveViewState();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,7 +259,7 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
*/
|
||||
protected clearTextEditorViewState(resources: URI[]): void {
|
||||
resources.forEach(resource => {
|
||||
this.editorViewStateMemento.clearState(resource);
|
||||
this.editorMemento.clearState(resource);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -279,7 +267,7 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
* Loads the text editor view state for the given resource and returns it.
|
||||
*/
|
||||
protected loadTextEditorViewState(resource: URI): IEditorViewState {
|
||||
return this.editorViewStateMemento.loadState(resource, this.position);
|
||||
return this.editorMemento.loadState(this.group, resource);
|
||||
}
|
||||
|
||||
private updateEditorConfiguration(configuration = this.configurationService.getValue<IEditorConfiguration>(this.getResource())): void {
|
||||
@@ -304,7 +292,7 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
}
|
||||
|
||||
protected getResource(): URI {
|
||||
const codeEditor = getCodeEditor(this);
|
||||
const codeEditor = getCodeEditor(this.editorControl);
|
||||
if (codeEditor) {
|
||||
const model = codeEditor.getModel();
|
||||
if (model) {
|
||||
@@ -321,17 +309,8 @@ export abstract class BaseTextEditor extends BaseEditor {
|
||||
|
||||
protected abstract getAriaLabel(): string;
|
||||
|
||||
protected saveMemento(): void {
|
||||
|
||||
// ensure to first save our view state memento
|
||||
this.editorViewStateMemento.save();
|
||||
|
||||
super.saveMemento();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
this.lastAppliedEditorOptions = void 0;
|
||||
this.editorControl.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
/**
|
||||
* An editor implementation that is capable of showing the contents of resource inputs. Uses
|
||||
@@ -37,13 +39,14 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@ITextFileService textFileService: ITextFileService
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IEditorService editorService: IEditorService
|
||||
) {
|
||||
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
|
||||
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
getTitle(): string {
|
||||
if (this.input) {
|
||||
return this.input.getName();
|
||||
}
|
||||
@@ -51,38 +54,25 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
return nls.localize('textEditor', "Text Editor");
|
||||
}
|
||||
|
||||
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
|
||||
|
||||
// Return early for same input unless we force to open
|
||||
const forceOpen = options && options.forceOpen;
|
||||
if (!forceOpen && input.matches(this.input)) {
|
||||
|
||||
// Still apply options if any (avoiding instanceof here for a reason, do not change!)
|
||||
const textOptions = <TextEditorOptions>options;
|
||||
if (textOptions && types.isFunction(textOptions.apply)) {
|
||||
textOptions.apply(this.getControl(), ScrollType.Smooth);
|
||||
}
|
||||
|
||||
return TPromise.wrap<void>(null);
|
||||
}
|
||||
setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable<void> {
|
||||
|
||||
// Remember view settings if input changes
|
||||
this.saveTextResourceEditorViewState(this.input);
|
||||
|
||||
// Set input and resolve
|
||||
return super.setInput(input, options).then(() => {
|
||||
return input.resolve(true).then((resolvedModel: EditorModel) => {
|
||||
return super.setInput(input, options, token).then(() => {
|
||||
return input.resolve().then((resolvedModel: EditorModel) => {
|
||||
|
||||
// Check for cancellation
|
||||
if (token.isCancellationRequested) {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
// Assert Model instance
|
||||
if (!(resolvedModel instanceof BaseTextEditorModel)) {
|
||||
return TPromise.wrapError<void>(new Error('Unable to open file as text'));
|
||||
}
|
||||
|
||||
// Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile
|
||||
if (!this.input || this.input !== input) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set Editor Model
|
||||
const textEditor = this.getControl();
|
||||
const textEditorModel = resolvedModel.textEditorModel;
|
||||
@@ -114,6 +104,13 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
}
|
||||
}
|
||||
|
||||
setOptions(options: EditorOptions): void {
|
||||
const textOptions = <TextEditorOptions>options;
|
||||
if (textOptions && types.isFunction(textOptions.apply)) {
|
||||
textOptions.apply(this.getControl(), ScrollType.Smooth);
|
||||
}
|
||||
}
|
||||
|
||||
protected getConfigurationOverrides(): IEditorOptions {
|
||||
const options = super.getConfigurationOverrides();
|
||||
|
||||
@@ -139,18 +136,23 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
|
||||
/**
|
||||
* Reveals the last line of this editor if it has a model set.
|
||||
* When smart is true only scroll if the cursor is currently on the last line of the output panel.
|
||||
* This allows users to click on the output panel to stop scrolling when they see something of interest.
|
||||
* To resume, they should scroll to the end of the output panel again.
|
||||
*/
|
||||
public revealLastLine(): void {
|
||||
revealLastLine(smart: boolean): void {
|
||||
const codeEditor = <ICodeEditor>this.getControl();
|
||||
const model = codeEditor.getModel();
|
||||
|
||||
if (model) {
|
||||
const lastLine = model.getLineCount();
|
||||
codeEditor.revealPosition({ lineNumber: lastLine, column: model.getLineMaxColumn(lastLine) }, ScrollType.Smooth);
|
||||
if (!smart || codeEditor.getPosition().lineNumber === lastLine) {
|
||||
codeEditor.revealPosition({ lineNumber: lastLine, column: model.getLineMaxColumn(lastLine) }, ScrollType.Smooth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public clearInput(): void {
|
||||
clearInput(): void {
|
||||
|
||||
// Keep editor view state in settings to restore when coming back
|
||||
this.saveTextResourceEditorViewState(this.input);
|
||||
@@ -161,7 +163,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
shutdown(): void {
|
||||
|
||||
// Save View State (only for untitled)
|
||||
if (this.input instanceof UntitledEditorInput) {
|
||||
@@ -198,7 +200,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
|
||||
export class TextResourceEditor extends AbstractTextResourceEditor {
|
||||
|
||||
public static readonly ID = 'workbench.editors.textResourceEditor';
|
||||
static readonly ID = 'workbench.editors.textResourceEditor';
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@@ -206,9 +208,10 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorGroupService editorGroupService: IEditorGroupService,
|
||||
@ITextFileService textFileService: ITextFileService
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, textFileService);
|
||||
super(TextResourceEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorGroupService, textFileService, editorService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,234 +5,133 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./media/titlecontrol';
|
||||
import * as nls from 'vs/nls';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { IAction, Action, IRunEvent } from 'vs/base/common/actions';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { IEditorStacksModel, IEditorGroup, IEditorIdentifier, EditorInput, IStacksModelChangeEvent, toResource, IEditorCommandsContext } from 'vs/workbench/common/editor';
|
||||
import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { applyDragImage } from 'vs/base/browser/dnd';
|
||||
import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ActionsOrientation, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
import { Action, IAction, IRunEvent } from 'vs/base/common/actions';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import 'vs/css!./media/titlecontrol';
|
||||
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { localize } from 'vs/nls';
|
||||
import { createActionItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { ExecuteCommandAction, IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { SplitEditorAction, CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { createActionItem, fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { IMenuService, MenuId, IMenu, ExecuteCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Themable } from 'vs/workbench/common/theme';
|
||||
import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Dimension, findParentWithClass } from 'vs/base/browser/dom';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { listActiveSelectionBackground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillResourceDataTransfers, LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
|
||||
import { BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
|
||||
import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptions } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource } from 'vs/workbench/common/editor';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
import { Themable } from 'vs/workbench/common/theme';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
export interface IToolbarActions {
|
||||
primary: IAction[];
|
||||
secondary: IAction[];
|
||||
}
|
||||
|
||||
export interface ITitleAreaControl {
|
||||
setContext(group: IEditorGroup): void;
|
||||
hasContext(): boolean;
|
||||
allowDragging(element: HTMLElement): boolean;
|
||||
setDragged(dragged: boolean): void;
|
||||
create(parent: HTMLElement): void;
|
||||
getContainer(): HTMLElement;
|
||||
refresh(instant?: boolean): void;
|
||||
update(instant?: boolean): void;
|
||||
updateEditorActionsToolbar(): void;
|
||||
layout(dimension: Dimension): void;
|
||||
dispose(): void;
|
||||
}
|
||||
export abstract class TitleControl extends Themable {
|
||||
|
||||
export abstract class TitleControl extends Themable implements ITitleAreaControl {
|
||||
protected readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
|
||||
protected readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
|
||||
|
||||
protected stacks: IEditorStacksModel;
|
||||
protected context: IEditorGroup;
|
||||
|
||||
protected dragged: boolean;
|
||||
|
||||
protected closeOneEditorAction: CloseOneEditorAction;
|
||||
protected splitEditorAction: SplitEditorAction;
|
||||
|
||||
private parent: HTMLElement;
|
||||
protected breadcrumbsControl: BreadcrumbsControl;
|
||||
|
||||
private currentPrimaryEditorActionIds: string[] = [];
|
||||
private currentSecondaryEditorActionIds: string[] = [];
|
||||
protected editorActionsToolbar: ToolBar;
|
||||
|
||||
private mapActionsToEditors: { [editorId: string]: IToolbarActions; };
|
||||
private titleAreaUpdateScheduler: RunOnceScheduler;
|
||||
private titleAreaToolbarUpdateScheduler: RunOnceScheduler;
|
||||
private refreshScheduled: boolean;
|
||||
private mapEditorToActions: Map<string, IToolbarActions> = new Map();
|
||||
|
||||
private resourceContext: ResourceContextKey;
|
||||
private disposeOnEditorActions: IDisposable[] = [];
|
||||
private editorToolBarMenuDisposables: IDisposable[] = [];
|
||||
|
||||
private contextMenu: IMenu;
|
||||
|
||||
constructor(
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
parent: HTMLElement,
|
||||
protected accessor: IEditorGroupsAccessor,
|
||||
protected group: IEditorGroupView,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupService protected editorGroupService: IEditorGroupService,
|
||||
@IContextKeyService protected contextKeyService: IContextKeyService,
|
||||
@IKeybindingService protected keybindingService: IKeybindingService,
|
||||
@ITelemetryService protected telemetryService: ITelemetryService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IMenuService protected menuService: IMenuService,
|
||||
@IMenuService private menuService: IMenuService,
|
||||
@IQuickOpenService protected quickOpenService: IQuickOpenService,
|
||||
@IThemeService protected themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IExtensionService private extensionService: IExtensionService,
|
||||
@IConfigurationService protected configurationService: IConfigurationService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this.stacks = editorGroupService.getStacksModel();
|
||||
this.mapActionsToEditors = Object.create(null);
|
||||
|
||||
this.titleAreaUpdateScheduler = new RunOnceScheduler(() => this.onSchedule(), 0);
|
||||
this.toUnbind.push(this.titleAreaUpdateScheduler);
|
||||
|
||||
this.titleAreaToolbarUpdateScheduler = new RunOnceScheduler(() => this.updateEditorActionsToolbar(), 0);
|
||||
this.toUnbind.push(this.titleAreaToolbarUpdateScheduler);
|
||||
|
||||
this.resourceContext = instantiationService.createInstance(ResourceContextKey);
|
||||
this.contextMenu = this._register(this.menuService.createMenu(MenuId.EditorTitleContext, this.contextKeyService));
|
||||
|
||||
this.contextMenu = this.menuService.createMenu(MenuId.EditorTitleContext, this.contextKeyService);
|
||||
this.toUnbind.push(this.contextMenu);
|
||||
|
||||
this.initActions(this.instantiationService);
|
||||
this.create(parent);
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
public setDragged(dragged: boolean): void {
|
||||
this.dragged = dragged;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.stacks.onModelChanged(e => this.onStacksChanged(e)));
|
||||
this._register(this.extensionService.onDidRegisterExtensions(() => this.updateEditorActionsToolbar()));
|
||||
}
|
||||
|
||||
private onStacksChanged(e: IStacksModelChangeEvent): void {
|
||||
if (e.structural) {
|
||||
this.updateSplitActionEnablement();
|
||||
protected abstract create(parent: HTMLElement): void;
|
||||
|
||||
protected createBreadcrumbsControl(container: HTMLElement, options: IBreadcrumbsControlOptions): void {
|
||||
const config = this._register(BreadcrumbsConfig.IsEnabled.bindTo(this.configurationService));
|
||||
config.onDidChange(value => {
|
||||
if (!value && this.breadcrumbsControl) {
|
||||
this.breadcrumbsControl.dispose();
|
||||
this.breadcrumbsControl = undefined;
|
||||
this.group.relayout();
|
||||
} else if (value && !this.breadcrumbsControl) {
|
||||
this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group);
|
||||
this.breadcrumbsControl.update();
|
||||
this.group.relayout();
|
||||
}
|
||||
});
|
||||
if (config.value) {
|
||||
this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group);
|
||||
}
|
||||
}
|
||||
|
||||
private updateSplitActionEnablement(): void {
|
||||
if (!this.context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const groupCount = this.stacks.groups.length;
|
||||
|
||||
// Split editor
|
||||
this.splitEditorAction.enabled = groupCount < 3;
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
this.update(true); // run an update when the theme changes to new styles
|
||||
}
|
||||
|
||||
private onSchedule(): void {
|
||||
if (this.refreshScheduled) {
|
||||
this.doRefresh();
|
||||
} else {
|
||||
this.doUpdate();
|
||||
}
|
||||
|
||||
this.refreshScheduled = false;
|
||||
}
|
||||
|
||||
public setContext(group: IEditorGroup): void {
|
||||
this.context = group;
|
||||
|
||||
this.editorActionsToolbar.context = { groupId: group ? group.id : void 0 } as IEditorCommandsContext;
|
||||
}
|
||||
|
||||
public hasContext(): boolean {
|
||||
return !!this.context;
|
||||
}
|
||||
|
||||
public update(instant?: boolean): void {
|
||||
if (instant) {
|
||||
this.titleAreaUpdateScheduler.cancel();
|
||||
this.onSchedule();
|
||||
} else {
|
||||
this.titleAreaUpdateScheduler.schedule();
|
||||
}
|
||||
|
||||
this.titleAreaToolbarUpdateScheduler.cancel(); // a title area update will always refresh the toolbar too
|
||||
}
|
||||
|
||||
public refresh(instant?: boolean) {
|
||||
this.refreshScheduled = true;
|
||||
|
||||
if (instant) {
|
||||
this.titleAreaUpdateScheduler.cancel();
|
||||
this.onSchedule();
|
||||
} else {
|
||||
this.titleAreaUpdateScheduler.schedule();
|
||||
}
|
||||
|
||||
this.titleAreaToolbarUpdateScheduler.cancel(); // a title area update will always refresh the toolbar too
|
||||
}
|
||||
|
||||
public create(parent: HTMLElement): void {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public getContainer(): HTMLElement {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
protected abstract doRefresh(): void;
|
||||
|
||||
protected doUpdate(): void {
|
||||
this.doRefresh();
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
// Subclasses can opt in to react on layout
|
||||
}
|
||||
|
||||
public allowDragging(element: HTMLElement): boolean {
|
||||
return !findParentWithClass(element, 'monaco-action-bar', 'one-editor-silo');
|
||||
}
|
||||
|
||||
protected initActions(services: IInstantiationService): void {
|
||||
this.closeOneEditorAction = services.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL);
|
||||
this.splitEditorAction = services.createInstance(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL);
|
||||
}
|
||||
|
||||
protected createEditorActionsToolBar(container: HTMLElement): void {
|
||||
this.editorActionsToolbar = new ToolBar(container, this.contextMenuService, {
|
||||
actionItemProvider: (action: Action) => this.actionItemProvider(action),
|
||||
const context = { groupId: this.group.id } as IEditorCommandsContext;
|
||||
|
||||
this.editorActionsToolbar = this._register(new ToolBar(container, this.contextMenuService, {
|
||||
actionItemProvider: action => this.actionItemProvider(action as Action),
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
ariaLabel: nls.localize('araLabelEditorActions', "Editor actions"),
|
||||
getKeyBinding: (action) => this.getKeybinding(action)
|
||||
});
|
||||
ariaLabel: localize('araLabelEditorActions', "Editor actions"),
|
||||
getKeyBinding: action => this.getKeybinding(action),
|
||||
actionRunner: this._register(new EditorCommandsContextActionRunner(context))
|
||||
}));
|
||||
|
||||
// Context
|
||||
this.editorActionsToolbar.context = context;
|
||||
|
||||
// Action Run Handling
|
||||
this.toUnbind.push(this.editorActionsToolbar.actionRunner.onDidRun((e: IRunEvent) => {
|
||||
this._register(this.editorActionsToolbar.actionRunner.onDidRun((e: IRunEvent) => {
|
||||
|
||||
// Check for Error
|
||||
if (e.error && !errors.isPromiseCanceledError(e.error)) {
|
||||
this.notificationService.error(e.error);
|
||||
}
|
||||
// Notify for Error
|
||||
this.notificationService.error(e.error);
|
||||
|
||||
// Log in telemetry
|
||||
if (this.telemetryService) {
|
||||
@@ -247,20 +146,13 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
}));
|
||||
}
|
||||
|
||||
protected actionItemProvider(action: Action): IActionItem {
|
||||
if (!this.context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const group = this.context;
|
||||
const position = this.stacks.positionOfGroup(group);
|
||||
const editor = this.editorService.getVisibleEditors()[position];
|
||||
|
||||
let actionItem: IActionItem;
|
||||
private actionItemProvider(action: Action): IActionItem {
|
||||
const activeControl = this.group.activeControl;
|
||||
|
||||
// Check Active Editor
|
||||
if (editor instanceof BaseEditor) {
|
||||
actionItem = editor.getActionItem(action);
|
||||
let actionItem: IActionItem;
|
||||
if (activeControl instanceof BaseEditor) {
|
||||
actionItem = activeControl.getActionItem(action);
|
||||
}
|
||||
|
||||
// Check extensions
|
||||
@@ -271,86 +163,13 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
return actionItem;
|
||||
}
|
||||
|
||||
protected getEditorActions(identifier: IEditorIdentifier): IToolbarActions {
|
||||
const primary: IAction[] = [];
|
||||
const secondary: IAction[] = [];
|
||||
|
||||
const { group } = identifier;
|
||||
const position = this.stacks.positionOfGroup(group);
|
||||
|
||||
// Update the resource context
|
||||
this.resourceContext.set(group && toResource(group.activeEditor, { supportSideBySide: true }));
|
||||
|
||||
// Editor actions require the editor control to be there, so we retrieve it via service
|
||||
const control = this.editorService.getVisibleEditors()[position];
|
||||
if (control instanceof BaseEditor && control.input && typeof control.position === 'number') {
|
||||
|
||||
// Editor Control Actions
|
||||
let editorActions = this.mapActionsToEditors[control.getId()];
|
||||
if (!editorActions) {
|
||||
editorActions = { primary: control.getActions(), secondary: control.getSecondaryActions() };
|
||||
this.mapActionsToEditors[control.getId()] = editorActions;
|
||||
}
|
||||
primary.push(...editorActions.primary);
|
||||
secondary.push(...editorActions.secondary);
|
||||
|
||||
// MenuItems
|
||||
// TODO This isn't very proper but needed as we have failed to
|
||||
// use the correct context key service per editor only once. Don't
|
||||
// take this code as sample of how to work with menus
|
||||
this.disposeOnEditorActions = dispose(this.disposeOnEditorActions);
|
||||
const widget = control.getControl();
|
||||
const codeEditor = isCodeEditor(widget) && widget || isDiffEditor(widget) && widget.getModifiedEditor();
|
||||
const scopedContextKeyService = codeEditor && codeEditor.invokeWithinContext(accessor => accessor.get(IContextKeyService)) || this.contextKeyService;
|
||||
const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService);
|
||||
this.disposeOnEditorActions.push(titleBarMenu, titleBarMenu.onDidChange(_ => {
|
||||
// schedule the update for the title area toolbar only if no other
|
||||
// update to the title area is scheduled which will always also
|
||||
// update the toolbar
|
||||
if (!this.titleAreaUpdateScheduler.isScheduled()) {
|
||||
this.titleAreaToolbarUpdateScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
|
||||
fillInActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary }, this.contextMenuService);
|
||||
}
|
||||
|
||||
return { primary, secondary };
|
||||
}
|
||||
|
||||
public updateEditorActionsToolbar(): void {
|
||||
const group = this.context;
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = group && group.activeEditor;
|
||||
const isActive = this.stacks.isActive(group);
|
||||
protected updateEditorActionsToolbar(): void {
|
||||
|
||||
// Update Editor Actions Toolbar
|
||||
let primaryEditorActions: IAction[] = [];
|
||||
let secondaryEditorActions: IAction[] = [];
|
||||
|
||||
const editorActions = this.getEditorActions({ group, editor });
|
||||
|
||||
// Primary actions only for the active group
|
||||
if (isActive) {
|
||||
primaryEditorActions = prepareActions(editorActions.primary);
|
||||
if (editor instanceof EditorInput && editor.supportsSplitEditor()) {
|
||||
this.updateSplitActionEnablement();
|
||||
primaryEditorActions.push(this.splitEditorAction);
|
||||
}
|
||||
}
|
||||
|
||||
secondaryEditorActions = prepareActions(editorActions.secondary);
|
||||
|
||||
const tabOptions = this.editorGroupService.getTabOptions();
|
||||
const { primaryEditorActions, secondaryEditorActions } = this.prepareEditorActions(this.getEditorActions());
|
||||
|
||||
// Only update if something actually has changed
|
||||
const primaryEditorActionIds = primaryEditorActions.map(a => a.id);
|
||||
if (!tabOptions.showTabs) {
|
||||
primaryEditorActionIds.push(this.closeOneEditorAction.id); // always show "Close" when tabs are disabled
|
||||
}
|
||||
|
||||
const secondaryEditorActionIds = secondaryEditorActions.map(a => a.id);
|
||||
if (
|
||||
!arrays.equals(primaryEditorActionIds, this.currentPrimaryEditorActionIds) ||
|
||||
@@ -360,15 +179,66 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
) {
|
||||
this.editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions)();
|
||||
|
||||
if (!tabOptions.showTabs) {
|
||||
this.editorActionsToolbar.addPrimaryAction(this.closeOneEditorAction)();
|
||||
}
|
||||
|
||||
this.currentPrimaryEditorActionIds = primaryEditorActionIds;
|
||||
this.currentSecondaryEditorActionIds = secondaryEditorActionIds;
|
||||
}
|
||||
}
|
||||
|
||||
protected prepareEditorActions(editorActions: IToolbarActions): { primaryEditorActions: IAction[]; secondaryEditorActions: IAction[]; } {
|
||||
let primaryEditorActions: IAction[];
|
||||
let secondaryEditorActions: IAction[];
|
||||
|
||||
// Primary actions only for the active group
|
||||
if (this.accessor.activeGroup === this.group) {
|
||||
primaryEditorActions = prepareActions(editorActions.primary);
|
||||
} else {
|
||||
primaryEditorActions = [];
|
||||
}
|
||||
|
||||
// Secondary actions for all groups
|
||||
secondaryEditorActions = prepareActions(editorActions.secondary);
|
||||
|
||||
return { primaryEditorActions, secondaryEditorActions };
|
||||
}
|
||||
|
||||
private getEditorActions(): IToolbarActions {
|
||||
const primary: IAction[] = [];
|
||||
const secondary: IAction[] = [];
|
||||
|
||||
// Dispose previous listeners
|
||||
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
|
||||
|
||||
// Update the resource context
|
||||
this.resourceContext.set(toResource(this.group.activeEditor, { supportSideBySide: true }));
|
||||
|
||||
// Editor actions require the editor control to be there, so we retrieve it via service
|
||||
const activeControl = this.group.activeControl;
|
||||
if (activeControl instanceof BaseEditor) {
|
||||
|
||||
// Editor Control Actions
|
||||
let editorActions = this.mapEditorToActions.get(activeControl.getId());
|
||||
if (!editorActions) {
|
||||
editorActions = { primary: activeControl.getActions(), secondary: activeControl.getSecondaryActions() };
|
||||
this.mapEditorToActions.set(activeControl.getId(), editorActions);
|
||||
}
|
||||
primary.push(...editorActions.primary);
|
||||
secondary.push(...editorActions.secondary);
|
||||
|
||||
// Contributed Actions
|
||||
const codeEditor = getCodeEditor(activeControl.getControl());
|
||||
const scopedContextKeyService = codeEditor && codeEditor.invokeWithinContext(accessor => accessor.get(IContextKeyService)) || this.contextKeyService;
|
||||
const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService);
|
||||
this.editorToolBarMenuDisposables.push(titleBarMenu);
|
||||
this.editorToolBarMenuDisposables.push(titleBarMenu.onDidChange(() => {
|
||||
this.updateEditorActionsToolbar(); // Update editor toolbar whenever contributed actions change
|
||||
}));
|
||||
|
||||
fillInActionBarActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary });
|
||||
}
|
||||
|
||||
return { primary, secondary };
|
||||
}
|
||||
|
||||
protected clearEditorActionsToolbar(): void {
|
||||
this.editorActionsToolbar.setActions([], [])();
|
||||
|
||||
@@ -376,11 +246,46 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
this.currentSecondaryEditorActionIds = [];
|
||||
}
|
||||
|
||||
protected onContextMenu(identifier: IEditorIdentifier, e: Event, node: HTMLElement): void {
|
||||
protected enableGroupDragging(element: HTMLElement): void {
|
||||
|
||||
// Drag start
|
||||
this._register(addDisposableListener(element, EventType.DRAG_START, (e: DragEvent) => {
|
||||
if (e.target !== element) {
|
||||
return; // only if originating from tabs container
|
||||
}
|
||||
|
||||
// Set editor group as transfer
|
||||
this.groupTransfer.setData([new DraggedEditorGroupIdentifier(this.group.id)], DraggedEditorGroupIdentifier.prototype);
|
||||
e.dataTransfer.effectAllowed = 'copyMove';
|
||||
|
||||
// If tabs are disabled, treat dragging as if an editor tab was dragged
|
||||
if (!this.accessor.partOptions.showTabs) {
|
||||
const resource = toResource(this.group.activeEditor, { supportSideBySide: true });
|
||||
if (resource) {
|
||||
this.instantiationService.invokeFunction(fillResourceDataTransfers, [resource], e);
|
||||
}
|
||||
}
|
||||
|
||||
// Drag Image
|
||||
let label = this.group.activeEditor.getName();
|
||||
if (this.accessor.partOptions.showTabs && this.group.count > 1) {
|
||||
label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1);
|
||||
}
|
||||
|
||||
applyDragImage(e, label, 'monaco-editor-group-drag-image');
|
||||
}));
|
||||
|
||||
// Drag end
|
||||
this._register(addDisposableListener(element, EventType.DRAG_END, () => {
|
||||
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
|
||||
}));
|
||||
}
|
||||
|
||||
protected onContextMenu(editor: IEditorInput, e: Event, node: HTMLElement): void {
|
||||
|
||||
// Update the resource context
|
||||
const currentContext = this.resourceContext.get();
|
||||
this.resourceContext.set(toResource(identifier.editor, { supportSideBySide: true }));
|
||||
this.resourceContext.set(toResource(editor, { supportSideBySide: true }));
|
||||
|
||||
// Find target anchor
|
||||
let anchor: HTMLElement | { x: number, y: number } = node;
|
||||
@@ -391,24 +296,21 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
|
||||
// Fill in contributed actions
|
||||
const actions: IAction[] = [];
|
||||
fillInActions(this.contextMenu, { shouldForwardArgs: true, arg: this.resourceContext.get() }, actions, this.contextMenuService);
|
||||
fillInContextMenuActions(this.contextMenu, { shouldForwardArgs: true, arg: this.resourceContext.get() }, actions, this.contextMenuService);
|
||||
|
||||
// Show it
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => TPromise.as(actions),
|
||||
getActionsContext: () => ({ groupId: identifier.group.id, editorIndex: identifier.group.indexOf(identifier.editor) } as IEditorCommandsContext),
|
||||
getActionsContext: () => ({ groupId: this.group.id, editorIndex: this.group.getIndexOfEditor(editor) } as IEditorCommandsContext),
|
||||
getKeyBinding: (action) => this.getKeybinding(action),
|
||||
onHide: (cancel) => {
|
||||
onHide: () => {
|
||||
|
||||
// restore previous context
|
||||
this.resourceContext.set(currentContext);
|
||||
|
||||
// restore focus to active editor if any
|
||||
const editor = this.editorService.getActiveEditor();
|
||||
if (editor) {
|
||||
editor.focus();
|
||||
}
|
||||
// restore focus to active group
|
||||
this.accessor.activeGroup.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -423,18 +325,61 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
|
||||
return keybinding ? keybinding.getLabel() : void 0;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
//#region ITitleAreaControl
|
||||
|
||||
// Actions
|
||||
[
|
||||
this.splitEditorAction,
|
||||
this.closeOneEditorAction
|
||||
].forEach((action) => {
|
||||
action.dispose();
|
||||
});
|
||||
abstract openEditor(editor: IEditorInput): void;
|
||||
|
||||
// Toolbar
|
||||
this.editorActionsToolbar.dispose();
|
||||
abstract closeEditor(editor: IEditorInput): void;
|
||||
|
||||
abstract closeEditors(editors: IEditorInput[]): void;
|
||||
|
||||
abstract closeAllEditors(): void;
|
||||
|
||||
abstract moveEditor(editor: IEditorInput, fromIndex: number, targetIndex: number): void;
|
||||
|
||||
abstract pinEditor(editor: IEditorInput): void;
|
||||
|
||||
abstract setActive(isActive: boolean): void;
|
||||
|
||||
abstract updateEditorLabel(editor: IEditorInput): void;
|
||||
|
||||
abstract updateEditorDirty(editor: IEditorInput): void;
|
||||
|
||||
abstract updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void;
|
||||
|
||||
abstract updateStyles(): void;
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
// Optionally implemented in subclasses
|
||||
|
||||
if (this.breadcrumbsControl) {
|
||||
this.breadcrumbsControl.layout(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
getPreferredHeight(): number {
|
||||
return EDITOR_TITLE_HEIGHT + (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden() ? BreadcrumbsControl.HEIGHT : 0);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.breadcrumbsControl = dispose(this.breadcrumbsControl);
|
||||
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
|
||||
// Drag Feedback
|
||||
const dragImageBackground = theme.getColor(listActiveSelectionBackground);
|
||||
const dragImageForeground = theme.getColor(listActiveSelectionForeground);
|
||||
collector.addRule(`
|
||||
.monaco-editor-group-drag-image {
|
||||
background: ${dragImageBackground};
|
||||
color: ${dragImageForeground};
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
50
src/vs/workbench/browser/parts/menubar/media/menubarpart.css
Normal file
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .part.menubar {
|
||||
display: flex;
|
||||
flex-shrink: 1;
|
||||
box-sizing: border-box;
|
||||
height: 30px;
|
||||
-webkit-app-region: no-drag;
|
||||
overflow: hidden;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.monaco-workbench.fullscreen .part.menubar {
|
||||
margin: 0px;
|
||||
padding: 0px 5px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.menubar > .menubar-menu-button {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 0px 8px;
|
||||
cursor: default;
|
||||
-webkit-app-region: no-drag;
|
||||
zoom: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.menubar .menubar-menu-items-holder {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
opacity: 1;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.menubar .menubar-menu-items-holder.monaco-menu-container {
|
||||
font-family: "Segoe WPC", "Segoe UI", ".SFNSDisplay-Light", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback";
|
||||
outline: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.menubar .menubar-menu-items-holder.monaco-menu-container :focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.hc-black .monaco-workbench .part.menubar .menubar-menu-items-holder.monaco-menu-container {
|
||||
border: 2px solid #6FC3DF;
|
||||
}
|
||||
402
src/vs/workbench/browser/parts/menubar/menubar.contribution.ts
Normal file
@@ -0,0 +1,402 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
|
||||
goMenuRegistration();
|
||||
|
||||
if (isMacintosh) {
|
||||
windowMenuRegistration();
|
||||
}
|
||||
|
||||
helpMenuRegistration();
|
||||
|
||||
// Menu registration
|
||||
function goMenuRegistration() {
|
||||
// Forward/Back
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: '1_fwd_back',
|
||||
command: {
|
||||
id: 'workbench.action.navigateBack',
|
||||
title: nls.localize({ key: 'miBack', comment: ['&& denotes a mnemonic'] }, "&&Back")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: '1_fwd_back',
|
||||
command: {
|
||||
id: 'workbench.action.navigateForward',
|
||||
title: nls.localize({ key: 'miForward', comment: ['&& denotes a mnemonic'] }, "&&Forward")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
// Switch Editor
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
|
||||
group: '1_any',
|
||||
command: {
|
||||
id: 'workbench.action.nextEditor',
|
||||
title: nls.localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
|
||||
group: '1_any',
|
||||
command: {
|
||||
id: 'workbench.action.previousEditor',
|
||||
title: nls.localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
|
||||
group: '2_used',
|
||||
command: {
|
||||
id: 'workbench.action.openNextRecentlyUsedEditorInGroup',
|
||||
title: nls.localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
|
||||
group: '2_used',
|
||||
command: {
|
||||
id: 'workbench.action.openPreviousRecentlyUsedEditorInGroup',
|
||||
title: nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: '2_switch',
|
||||
title: nls.localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor"),
|
||||
submenu: MenuId.MenubarSwitchEditorMenu,
|
||||
order: 1
|
||||
});
|
||||
|
||||
// Switch Group
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '1_focus_index',
|
||||
command: {
|
||||
id: 'workbench.action.focusFirstEditorGroup',
|
||||
title: nls.localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "Group &&1")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '1_focus_index',
|
||||
command: {
|
||||
id: 'workbench.action.focusSecondEditorGroup',
|
||||
title: nls.localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "Group &&2")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '1_focus_index',
|
||||
command: {
|
||||
id: 'workbench.action.focusThirdEditorGroup',
|
||||
title: nls.localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "Group &&3")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '1_focus_index',
|
||||
command: {
|
||||
id: 'workbench.action.focusFourthEditorGroup',
|
||||
title: nls.localize({ key: 'miFocusFourthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&4")
|
||||
},
|
||||
order: 4
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '1_focus_index',
|
||||
command: {
|
||||
id: 'workbench.action.focusFifthEditorGroup',
|
||||
title: nls.localize({ key: 'miFocusFifthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&5")
|
||||
},
|
||||
order: 5
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '2_next_prev',
|
||||
command: {
|
||||
id: 'workbench.action.focusNextGroup',
|
||||
title: nls.localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '2_next_prev',
|
||||
command: {
|
||||
id: 'workbench.action.focusPreviousGroup',
|
||||
title: nls.localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '3_directional',
|
||||
command: {
|
||||
id: 'workbench.action.focusLeftGroup',
|
||||
title: nls.localize({ key: 'miFocusLeftGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Left")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '3_directional',
|
||||
command: {
|
||||
id: 'workbench.action.focusRightGroup',
|
||||
title: nls.localize({ key: 'miFocusRightGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Right")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '3_directional',
|
||||
command: {
|
||||
id: 'workbench.action.focusAboveGroup',
|
||||
title: nls.localize({ key: 'miFocusAboveGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Above")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
|
||||
group: '3_directional',
|
||||
command: {
|
||||
id: 'workbench.action.focusBelowGroup',
|
||||
title: nls.localize({ key: 'miFocusBelowGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Below")
|
||||
},
|
||||
order: 4
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: '2_switch',
|
||||
title: nls.localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group"),
|
||||
submenu: MenuId.MenubarSwitchGroupMenu,
|
||||
order: 2
|
||||
});
|
||||
|
||||
// Go to
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: 'z_go_to',
|
||||
command: {
|
||||
id: 'workbench.action.quickOpen',
|
||||
title: nls.localize({ key: 'miGotoFile', comment: ['&& denotes a mnemonic'] }, "Go to &&File...")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: 'z_go_to',
|
||||
command: {
|
||||
id: 'workbench.action.gotoSymbol',
|
||||
title: nls.localize({ key: 'miGotoSymbolInFile', comment: ['&& denotes a mnemonic'] }, "Go to &&Symbol in File...")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: 'z_go_to',
|
||||
command: {
|
||||
id: 'workbench.action.showAllSymbols',
|
||||
title: nls.localize({ key: 'miGotoSymbolInWorkspace', comment: ['&& denotes a mnemonic'] }, "Go to Symbol in &&Workspace...")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: 'z_go_to',
|
||||
command: {
|
||||
id: 'editor.action.goToDeclaration',
|
||||
title: nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition")
|
||||
},
|
||||
order: 4
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: 'z_go_to',
|
||||
command: {
|
||||
id: 'editor.action.goToTypeDefinition',
|
||||
title: nls.localize({ key: 'miGotoTypeDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Type Definition")
|
||||
},
|
||||
order: 5
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: 'z_go_to',
|
||||
command: {
|
||||
id: 'editor.action.goToImplementation',
|
||||
title: nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementation")
|
||||
},
|
||||
order: 6
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
|
||||
group: 'z_go_to',
|
||||
command: {
|
||||
id: 'workbench.action.gotoLine',
|
||||
title: nls.localize({ key: 'miGotoLine', comment: ['&& denotes a mnemonic'] }, "Go to &&Line...")
|
||||
},
|
||||
order: 7
|
||||
});
|
||||
}
|
||||
|
||||
function windowMenuRegistration() {
|
||||
|
||||
}
|
||||
|
||||
function helpMenuRegistration() {
|
||||
// Welcome
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '1_welcome',
|
||||
command: {
|
||||
id: 'workbench.action.showWelcomePage',
|
||||
title: nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '1_welcome',
|
||||
command: {
|
||||
id: 'workbench.action.showInteractivePlayground',
|
||||
title: nls.localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "&&Interactive Playground")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '1_welcome',
|
||||
command: {
|
||||
id: 'workbench.action.openDocumentationUrl',
|
||||
title: nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '1_welcome',
|
||||
command: {
|
||||
id: 'update.showCurrentReleaseNotes',
|
||||
title: nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes")
|
||||
},
|
||||
order: 4
|
||||
});
|
||||
|
||||
// Reference
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '2_reference',
|
||||
command: {
|
||||
id: 'workbench.action.keybindingsReference',
|
||||
title: nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '2_reference',
|
||||
command: {
|
||||
id: 'workbench.action.openIntroductoryVideosUrl',
|
||||
title: nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '2_reference',
|
||||
command: {
|
||||
id: 'workbench.action.openTipsAndTricksUrl',
|
||||
title: nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "&&Tips and Tricks")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
// Feedback
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '3_feedback',
|
||||
command: {
|
||||
id: 'workbench.action.openTwitterUrl',
|
||||
title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join us on Twitter")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '3_feedback',
|
||||
command: {
|
||||
id: 'workbench.action.openRequestFeatureUrl',
|
||||
title: nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '3_feedback',
|
||||
command: {
|
||||
id: 'workbench.action.openIssueReporter',
|
||||
title: nls.localize({ key: 'miReportIssue', comment: ['&& denotes a mnemonic', 'Translate this to "Report Issue in English" in all languages please!'] }, "Report &&Issue")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
// Legal
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '4_legal',
|
||||
command: {
|
||||
id: 'workbench.action.openLicenseUrl',
|
||||
title: nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '4_legal',
|
||||
command: {
|
||||
id: 'workbench.action.openPrivacyStatementUrl',
|
||||
title: nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "&&Privacy Statement")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
// Tools
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '5_tools',
|
||||
command: {
|
||||
id: 'workbench.action.toggleDevTools',
|
||||
title: nls.localize({ key: 'miToggleDevTools', comment: ['&& denotes a mnemonic'] }, "&&Toggle Developer Tools")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '5_tools',
|
||||
command: {
|
||||
id: 'workbench.action.openProcessExplorer',
|
||||
title: nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
if (!isMacintosh) {
|
||||
// About
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: 'z_about',
|
||||
command: {
|
||||
id: 'workbench.action.showAboutDialog',
|
||||
title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
}
|
||||
}
|
||||
1246
src/vs/workbench/browser/parts/menubar/menubarPart.ts
Normal file
@@ -115,7 +115,6 @@
|
||||
/** Notification: Source */
|
||||
|
||||
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-source {
|
||||
opacity: 0.7;
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
overflow: hidden; /* always give away space to buttons container */
|
||||
|
||||
@@ -18,8 +18,8 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
|
||||
|
||||
export class ClearNotificationAction extends Action {
|
||||
|
||||
public static readonly ID = CLEAR_NOTIFICATION;
|
||||
public static readonly LABEL = localize('clearNotification', "Clear Notification");
|
||||
static readonly ID = CLEAR_NOTIFICATION;
|
||||
static readonly LABEL = localize('clearNotification', "Clear Notification");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -29,7 +29,7 @@ export class ClearNotificationAction extends Action {
|
||||
super(id, label, 'clear-notification-action');
|
||||
}
|
||||
|
||||
public run(notification: INotificationViewItem): TPromise<any> {
|
||||
run(notification: INotificationViewItem): TPromise<any> {
|
||||
this.commandService.executeCommand(CLEAR_NOTIFICATION, notification);
|
||||
|
||||
return TPromise.as(void 0);
|
||||
@@ -38,8 +38,8 @@ export class ClearNotificationAction extends Action {
|
||||
|
||||
export class ClearAllNotificationsAction extends Action {
|
||||
|
||||
public static readonly ID = CLEAR_ALL_NOTIFICATIONS;
|
||||
public static readonly LABEL = localize('clearNotifications', "Clear All Notifications");
|
||||
static readonly ID = CLEAR_ALL_NOTIFICATIONS;
|
||||
static readonly LABEL = localize('clearNotifications', "Clear All Notifications");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -49,7 +49,7 @@ export class ClearAllNotificationsAction extends Action {
|
||||
super(id, label, 'clear-all-notifications-action');
|
||||
}
|
||||
|
||||
public run(notification: INotificationViewItem): TPromise<any> {
|
||||
run(notification: INotificationViewItem): TPromise<any> {
|
||||
this.commandService.executeCommand(CLEAR_ALL_NOTIFICATIONS);
|
||||
|
||||
return TPromise.as(void 0);
|
||||
@@ -58,8 +58,8 @@ export class ClearAllNotificationsAction extends Action {
|
||||
|
||||
export class HideNotificationsCenterAction extends Action {
|
||||
|
||||
public static readonly ID = HIDE_NOTIFICATIONS_CENTER;
|
||||
public static readonly LABEL = localize('hideNotificationsCenter', "Hide Notifications");
|
||||
static readonly ID = HIDE_NOTIFICATIONS_CENTER;
|
||||
static readonly LABEL = localize('hideNotificationsCenter', "Hide Notifications");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -69,7 +69,7 @@ export class HideNotificationsCenterAction extends Action {
|
||||
super(id, label, 'hide-all-notifications-action');
|
||||
}
|
||||
|
||||
public run(notification: INotificationViewItem): TPromise<any> {
|
||||
run(notification: INotificationViewItem): TPromise<any> {
|
||||
this.commandService.executeCommand(HIDE_NOTIFICATIONS_CENTER);
|
||||
|
||||
return TPromise.as(void 0);
|
||||
@@ -78,8 +78,8 @@ export class HideNotificationsCenterAction extends Action {
|
||||
|
||||
export class ExpandNotificationAction extends Action {
|
||||
|
||||
public static readonly ID = EXPAND_NOTIFICATION;
|
||||
public static readonly LABEL = localize('expandNotification', "Expand Notification");
|
||||
static readonly ID = EXPAND_NOTIFICATION;
|
||||
static readonly LABEL = localize('expandNotification', "Expand Notification");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -89,7 +89,7 @@ export class ExpandNotificationAction extends Action {
|
||||
super(id, label, 'expand-notification-action');
|
||||
}
|
||||
|
||||
public run(notification: INotificationViewItem): TPromise<any> {
|
||||
run(notification: INotificationViewItem): TPromise<any> {
|
||||
this.commandService.executeCommand(EXPAND_NOTIFICATION, notification);
|
||||
|
||||
return TPromise.as(void 0);
|
||||
@@ -98,8 +98,8 @@ export class ExpandNotificationAction extends Action {
|
||||
|
||||
export class CollapseNotificationAction extends Action {
|
||||
|
||||
public static readonly ID = COLLAPSE_NOTIFICATION;
|
||||
public static readonly LABEL = localize('collapseNotification', "Collapse Notification");
|
||||
static readonly ID = COLLAPSE_NOTIFICATION;
|
||||
static readonly LABEL = localize('collapseNotification', "Collapse Notification");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -109,7 +109,7 @@ export class CollapseNotificationAction extends Action {
|
||||
super(id, label, 'collapse-notification-action');
|
||||
}
|
||||
|
||||
public run(notification: INotificationViewItem): TPromise<any> {
|
||||
run(notification: INotificationViewItem): TPromise<any> {
|
||||
this.commandService.executeCommand(COLLAPSE_NOTIFICATION, notification);
|
||||
|
||||
return TPromise.as(void 0);
|
||||
@@ -118,8 +118,8 @@ export class CollapseNotificationAction extends Action {
|
||||
|
||||
export class ConfigureNotificationAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.configureNotification';
|
||||
public static readonly LABEL = localize('configureNotification', "Configure Notification");
|
||||
static readonly ID = 'workbench.action.configureNotification';
|
||||
static readonly LABEL = localize('configureNotification', "Configure Notification");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -129,15 +129,15 @@ export class ConfigureNotificationAction extends Action {
|
||||
super(id, label, 'configure-notification-action');
|
||||
}
|
||||
|
||||
public get configurationActions(): IAction[] {
|
||||
get configurationActions(): IAction[] {
|
||||
return this._configurationActions;
|
||||
}
|
||||
}
|
||||
|
||||
export class CopyNotificationMessageAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.copyNotificationMessage';
|
||||
public static readonly LABEL = localize('copyNotification', "Copy Text");
|
||||
static readonly ID = 'workbench.action.copyNotificationMessage';
|
||||
static readonly LABEL = localize('copyNotification', "Copy Text");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -147,7 +147,7 @@ export class CopyNotificationMessageAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(notification: INotificationViewItem): TPromise<any> {
|
||||
run(notification: INotificationViewItem): TPromise<any> {
|
||||
this.clipboardService.writeText(notification.message.raw);
|
||||
|
||||
return TPromise.as(void 0);
|
||||
|
||||
@@ -8,15 +8,14 @@
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { localize } from 'vs/nls';
|
||||
import { INotificationViewItem, INotificationsModel, NotificationChangeType, INotificationChangeEvent } from 'vs/workbench/common/notifications';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { Severity } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export class NotificationsAlerts {
|
||||
private toDispose: IDisposable[];
|
||||
export class NotificationsAlerts extends Disposable {
|
||||
|
||||
constructor(private model: INotificationsModel) {
|
||||
this.toDispose = [];
|
||||
super();
|
||||
|
||||
// Alert initial notifications if any
|
||||
model.notifications.forEach(n => this.ariaAlert(n));
|
||||
@@ -25,7 +24,7 @@ export class NotificationsAlerts {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
}
|
||||
|
||||
private onDidNotificationChange(e: INotificationChangeEvent): void {
|
||||
@@ -36,7 +35,11 @@ export class NotificationsAlerts {
|
||||
|
||||
// Always log errors to console with full details
|
||||
if (e.item.severity === Severity.Error) {
|
||||
console.error(toErrorMessage(e.item.message.value, true));
|
||||
if (e.item.message.original instanceof Error) {
|
||||
console.error(e.item.message.original);
|
||||
} else {
|
||||
console.error(toErrorMessage(e.item.message.value, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,8 +56,4 @@ export class NotificationsAlerts {
|
||||
|
||||
alert(alertText);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import { NotificationsList } from 'vs/workbench/browser/parts/notifications/noti
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { addClass, removeClass, isAncestor, Dimension } from 'vs/base/browser/dom';
|
||||
import { widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ClearAllNotificationsAction, HideNotificationsCenterAction, NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsActions';
|
||||
@@ -29,13 +29,15 @@ export class NotificationsCenter extends Themable {
|
||||
|
||||
private static MAX_DIMENSIONS = new Dimension(450, 400);
|
||||
|
||||
private readonly _onDidChangeVisibility: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onDidChangeVisibility(): Event<void> { return this._onDidChangeVisibility.event; }
|
||||
|
||||
private notificationsCenterContainer: HTMLElement;
|
||||
private notificationsCenterHeader: HTMLElement;
|
||||
private notificationsCenterTitle: HTMLSpanElement;
|
||||
private notificationsList: NotificationsList;
|
||||
private _isVisible: boolean;
|
||||
private workbenchDimensions: Dimension;
|
||||
private readonly _onDidChangeVisibility: Emitter<void>;
|
||||
private notificationsCenterVisibleContextKey: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
@@ -45,32 +47,25 @@ export class NotificationsCenter extends Themable {
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IPartService private partService: IPartService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupsService private editorGroupService: IEditorGroupsService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
this._onDidChangeVisibility = new Emitter<void>();
|
||||
this.toUnbind.push(this._onDidChangeVisibility);
|
||||
|
||||
this.notificationsCenterVisibleContextKey = NotificationsCenterVisibleContext.bindTo(contextKeyService);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
}
|
||||
|
||||
public get onDidChangeVisibility(): Event<void> {
|
||||
return this._onDidChangeVisibility.event;
|
||||
}
|
||||
|
||||
public get isVisible(): boolean {
|
||||
get isVisible(): boolean {
|
||||
return this._isVisible;
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
show(): void {
|
||||
if (this._isVisible) {
|
||||
this.notificationsList.show(true /* focus */);
|
||||
|
||||
@@ -138,21 +133,17 @@ export class NotificationsCenter extends Themable {
|
||||
addClass(toolbarContainer, 'notifications-center-header-toolbar');
|
||||
this.notificationsCenterHeader.appendChild(toolbarContainer);
|
||||
|
||||
const actionRunner = this.instantiationService.createInstance(NotificationActionRunner);
|
||||
this.toUnbind.push(actionRunner);
|
||||
const actionRunner = this._register(this.instantiationService.createInstance(NotificationActionRunner));
|
||||
|
||||
const notificationsToolBar = new ActionBar(toolbarContainer, {
|
||||
const notificationsToolBar = this._register(new ActionBar(toolbarContainer, {
|
||||
ariaLabel: localize('notificationsToolbar', "Notification Center Actions"),
|
||||
actionRunner
|
||||
});
|
||||
this.toUnbind.push(notificationsToolBar);
|
||||
}));
|
||||
|
||||
const hideAllAction = this.instantiationService.createInstance(HideNotificationsCenterAction, HideNotificationsCenterAction.ID, HideNotificationsCenterAction.LABEL);
|
||||
this.toUnbind.push(hideAllAction);
|
||||
const hideAllAction = this._register(this.instantiationService.createInstance(HideNotificationsCenterAction, HideNotificationsCenterAction.ID, HideNotificationsCenterAction.LABEL));
|
||||
notificationsToolBar.push(hideAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(hideAllAction) });
|
||||
|
||||
const clearAllAction = this.instantiationService.createInstance(ClearAllNotificationsAction, ClearAllNotificationsAction.ID, ClearAllNotificationsAction.LABEL);
|
||||
this.toUnbind.push(clearAllAction);
|
||||
const clearAllAction = this._register(this.instantiationService.createInstance(ClearAllNotificationsAction, ClearAllNotificationsAction.ID, ClearAllNotificationsAction.LABEL));
|
||||
notificationsToolBar.push(clearAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(clearAllAction) });
|
||||
|
||||
// Notifications List
|
||||
@@ -174,7 +165,7 @@ export class NotificationsCenter extends Themable {
|
||||
return; // only if visible
|
||||
}
|
||||
|
||||
let focusEditor = false;
|
||||
let focusGroup = false;
|
||||
|
||||
// Update notifications list based on event
|
||||
switch (e.kind) {
|
||||
@@ -185,7 +176,7 @@ export class NotificationsCenter extends Themable {
|
||||
this.notificationsList.updateNotificationsList(e.index, 1, [e.item]);
|
||||
break;
|
||||
case NotificationChangeType.REMOVE:
|
||||
focusEditor = isAncestor(document.activeElement, this.notificationsCenterContainer);
|
||||
focusGroup = isAncestor(document.activeElement, this.notificationsCenterContainer);
|
||||
this.notificationsList.updateNotificationsList(e.index, 1);
|
||||
break;
|
||||
}
|
||||
@@ -197,26 +188,19 @@ export class NotificationsCenter extends Themable {
|
||||
if (this.model.notifications.length === 0) {
|
||||
this.hide();
|
||||
|
||||
// Restore focus to editor if we had focus
|
||||
if (focusEditor) {
|
||||
this.focusEditor();
|
||||
// Restore focus to editor group if we had focus
|
||||
if (focusGroup) {
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private focusEditor(): void {
|
||||
const editor = this.editorService.getActiveEditor();
|
||||
if (editor) {
|
||||
editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
hide(): void {
|
||||
if (!this._isVisible || !this.notificationsCenterContainer) {
|
||||
return; // already hidden
|
||||
}
|
||||
|
||||
const focusEditor = isAncestor(document.activeElement, this.notificationsCenterContainer);
|
||||
const focusGroup = isAncestor(document.activeElement, this.notificationsCenterContainer);
|
||||
|
||||
// Hide
|
||||
this._isVisible = false;
|
||||
@@ -229,8 +213,9 @@ export class NotificationsCenter extends Themable {
|
||||
// Event
|
||||
this._onDidChangeVisibility.fire();
|
||||
|
||||
if (focusEditor) {
|
||||
this.focusEditor();
|
||||
// Restore focus to editor group if we had focus
|
||||
if (focusGroup) {
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +235,7 @@ export class NotificationsCenter extends Themable {
|
||||
}
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
layout(dimension: Dimension): void {
|
||||
this.workbenchDimensions = dimension;
|
||||
|
||||
if (this._isVisible && this.notificationsCenterContainer) {
|
||||
@@ -284,7 +269,7 @@ export class NotificationsCenter extends Themable {
|
||||
}
|
||||
}
|
||||
|
||||
public clearAll(): void {
|
||||
clearAll(): void {
|
||||
|
||||
// Hide notifications center first
|
||||
this.hide();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { INotificationViewItem, isNotificationViewItem } from 'vs/workbench/common/notifications';
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
@@ -88,7 +88,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
// Hide Notifications Center
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: HIDE_NOTIFICATIONS_CENTER,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
when: NotificationsCenterVisibleContext,
|
||||
primary: KeyCode.Escape,
|
||||
handler: accessor => center.hide()
|
||||
@@ -106,7 +106,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
// Clear Notification
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CLEAR_NOTIFICATION,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: NotificationFocusedContext,
|
||||
primary: KeyCode.Delete,
|
||||
mac: {
|
||||
@@ -123,7 +123,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
// Expand Notification
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: EXPAND_NOTIFICATION,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: NotificationFocusedContext,
|
||||
primary: KeyCode.RightArrow,
|
||||
handler: (accessor, args?: any) => {
|
||||
@@ -137,7 +137,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
// Collapse Notification
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: COLLAPSE_NOTIFICATION,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: NotificationFocusedContext,
|
||||
primary: KeyCode.LeftArrow,
|
||||
handler: (accessor, args?: any) => {
|
||||
@@ -151,7 +151,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
// Toggle Notification
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: TOGGLE_NOTIFICATION,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: NotificationFocusedContext,
|
||||
primary: KeyCode.Space,
|
||||
secondary: [KeyCode.Enter],
|
||||
@@ -166,7 +166,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
// Hide Toasts
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: HIDE_NOTIFICATION_TOAST,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
when: NotificationsToastsVisibleContext,
|
||||
primary: KeyCode.Escape,
|
||||
handler: accessor => toasts.hide()
|
||||
@@ -178,7 +178,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
// Focus Next Toast
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: FOCUS_NEXT_NOTIFICATION_TOAST,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(NotificationFocusedContext, NotificationsToastsVisibleContext),
|
||||
primary: KeyCode.DownArrow,
|
||||
handler: (accessor) => {
|
||||
@@ -189,7 +189,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
// Focus Previous Toast
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: FOCUS_PREVIOUS_NOTIFICATION_TOAST,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(NotificationFocusedContext, NotificationsToastsVisibleContext),
|
||||
primary: KeyCode.UpArrow,
|
||||
handler: (accessor) => {
|
||||
@@ -200,7 +200,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
// Focus First Toast
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: FOCUS_FIRST_NOTIFICATION_TOAST,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(NotificationFocusedContext, NotificationsToastsVisibleContext),
|
||||
primary: KeyCode.PageUp,
|
||||
secondary: [KeyCode.Home],
|
||||
@@ -212,7 +212,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
|
||||
// Focus Last Toast
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: FOCUS_LAST_NOTIFICATION_TOAST,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(NotificationFocusedContext, NotificationsToastsVisibleContext),
|
||||
primary: KeyCode.PageDown,
|
||||
secondary: [KeyCode.End],
|
||||
|
||||
@@ -38,7 +38,7 @@ export class NotificationsList extends Themable {
|
||||
this.viewModel = [];
|
||||
}
|
||||
|
||||
public show(focus?: boolean): void {
|
||||
show(focus?: boolean): void {
|
||||
if (this.isVisible) {
|
||||
if (focus) {
|
||||
this.list.domFocus();
|
||||
@@ -67,47 +67,43 @@ export class NotificationsList extends Themable {
|
||||
this.listContainer = document.createElement('div');
|
||||
addClass(this.listContainer, 'notifications-list-container');
|
||||
|
||||
const actionRunner = this.instantiationService.createInstance(NotificationActionRunner);
|
||||
this.toUnbind.push(actionRunner);
|
||||
const actionRunner = this._register(this.instantiationService.createInstance(NotificationActionRunner));
|
||||
|
||||
// Notification Renderer
|
||||
const renderer = this.instantiationService.createInstance(NotificationRenderer, actionRunner);
|
||||
|
||||
// List
|
||||
this.list = <WorkbenchList<INotificationViewItem>>this.instantiationService.createInstance(
|
||||
this.list = this._register(<WorkbenchList<INotificationViewItem>>this.instantiationService.createInstance(
|
||||
WorkbenchList,
|
||||
this.listContainer,
|
||||
new NotificationsListDelegate(this.listContainer),
|
||||
[renderer],
|
||||
this.options
|
||||
);
|
||||
this.toUnbind.push(this.list);
|
||||
));
|
||||
|
||||
// Context menu to copy message
|
||||
const copyAction = this.instantiationService.createInstance(CopyNotificationMessageAction, CopyNotificationMessageAction.ID, CopyNotificationMessageAction.LABEL);
|
||||
this.toUnbind.push(copyAction);
|
||||
this.toUnbind.push(this.list.onContextMenu(e => {
|
||||
const copyAction = this._register(this.instantiationService.createInstance(CopyNotificationMessageAction, CopyNotificationMessageAction.ID, CopyNotificationMessageAction.LABEL));
|
||||
this._register((this.list.onContextMenu(e => {
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => TPromise.as([copyAction]),
|
||||
getActionsContext: () => e.element,
|
||||
actionRunner
|
||||
});
|
||||
}));
|
||||
})));
|
||||
|
||||
// Toggle on double click
|
||||
this.toUnbind.push(this.list.onMouseDblClick(event => (event.element as INotificationViewItem).toggle()));
|
||||
this._register((this.list.onMouseDblClick(event => (event.element as INotificationViewItem).toggle())));
|
||||
|
||||
// Clear focus when DOM focus moves out
|
||||
// Use document.hasFocus() to not clear the focus when the entire window lost focus
|
||||
// This ensures that when the focus comes back, the notifciation is still focused
|
||||
const listFocusTracker = trackFocus(this.list.getHTMLElement());
|
||||
listFocusTracker.onDidBlur(() => {
|
||||
const listFocusTracker = this._register(trackFocus(this.list.getHTMLElement()));
|
||||
this._register(listFocusTracker.onDidBlur(() => {
|
||||
if (document.hasFocus()) {
|
||||
this.list.setFocus([]);
|
||||
}
|
||||
});
|
||||
this.toUnbind.push(listFocusTracker);
|
||||
}));
|
||||
|
||||
// Context key
|
||||
NotificationFocusedContext.bindTo(this.list.contextKeyService);
|
||||
@@ -115,7 +111,7 @@ export class NotificationsList extends Themable {
|
||||
// Only allow for focus in notifications, as the
|
||||
// selection is too strong over the contents of
|
||||
// the notification
|
||||
this.toUnbind.push(this.list.onSelectionChange(e => {
|
||||
this._register(this.list.onSelectionChange(e => {
|
||||
if (e.indexes.length > 0) {
|
||||
this.list.setSelection([]);
|
||||
}
|
||||
@@ -126,7 +122,7 @@ export class NotificationsList extends Themable {
|
||||
this.updateStyles();
|
||||
}
|
||||
|
||||
public updateNotificationsList(start: number, deleteCount: number, items: INotificationViewItem[] = []) {
|
||||
updateNotificationsList(start: number, deleteCount: number, items: INotificationViewItem[] = []) {
|
||||
const listHasDOMFocus = isAncestor(document.activeElement, this.listContainer);
|
||||
|
||||
// Remember focus and relative top of that item
|
||||
@@ -177,7 +173,7 @@ export class NotificationsList extends Themable {
|
||||
}
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
hide(): void {
|
||||
if (!this.isVisible || !this.list) {
|
||||
return; // already hidden
|
||||
}
|
||||
@@ -192,7 +188,7 @@ export class NotificationsList extends Themable {
|
||||
this.viewModel = [];
|
||||
}
|
||||
|
||||
public focusFirst(): void {
|
||||
focusFirst(): void {
|
||||
if (!this.isVisible || !this.list) {
|
||||
return; // hidden
|
||||
}
|
||||
@@ -201,7 +197,7 @@ export class NotificationsList extends Themable {
|
||||
this.list.domFocus();
|
||||
}
|
||||
|
||||
public hasFocus(): boolean {
|
||||
hasFocus(): boolean {
|
||||
if (!this.isVisible || !this.list) {
|
||||
return false; // hidden
|
||||
}
|
||||
@@ -222,7 +218,7 @@ export class NotificationsList extends Themable {
|
||||
}
|
||||
}
|
||||
|
||||
public layout(width: number, maxHeight?: number): void {
|
||||
layout(width: number, maxHeight?: number): void {
|
||||
if (this.list) {
|
||||
this.listContainer.style.width = `${width}px`;
|
||||
|
||||
@@ -234,7 +230,7 @@ export class NotificationsList extends Themable {
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
this.hide();
|
||||
|
||||
super.dispose();
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
|
||||
import { INotificationsModel, INotificationChangeEvent, NotificationChangeType, INotificationViewItem } from 'vs/workbench/common/notifications';
|
||||
import { IStatusbarService, StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { HIDE_NOTIFICATIONS_CENTER, SHOW_NOTIFICATIONS_CENTER } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export class NotificationsStatus {
|
||||
export class NotificationsStatus extends Disposable {
|
||||
private statusItem: IDisposable;
|
||||
private toDispose: IDisposable[];
|
||||
private isNotificationsCenterVisible: boolean;
|
||||
private _counter: Set<INotificationViewItem>;
|
||||
|
||||
@@ -21,7 +20,8 @@ export class NotificationsStatus {
|
||||
private model: INotificationsModel,
|
||||
@IStatusbarService private statusbarService: IStatusbarService
|
||||
) {
|
||||
this.toDispose = [];
|
||||
super();
|
||||
|
||||
this._counter = new Set<INotificationViewItem>();
|
||||
|
||||
this.updateNotificationsStatusItem();
|
||||
@@ -33,7 +33,7 @@ export class NotificationsStatus {
|
||||
return this._counter.size;
|
||||
}
|
||||
|
||||
public update(isCenterVisible: boolean): void {
|
||||
update(isCenterVisible: boolean): void {
|
||||
if (this.isNotificationsCenterVisible !== isCenterVisible) {
|
||||
this.isNotificationsCenterVisible = isCenterVisible;
|
||||
|
||||
@@ -44,7 +44,7 @@ export class NotificationsStatus {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
}
|
||||
|
||||
private onDidNotificationChange(e: INotificationChangeEvent): void {
|
||||
@@ -101,8 +101,8 @@ export class NotificationsStatus {
|
||||
return localize('notifications', "{0} New Notifications", this.count);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
dispose() {
|
||||
super.dispose();
|
||||
|
||||
if (this.statusItem) {
|
||||
this.statusItem.dispose();
|
||||
|
||||
@@ -16,7 +16,7 @@ import { IPartService, Parts } from 'vs/workbench/services/part/common/partServi
|
||||
import { Themable, NOTIFICATIONS_TOAST_BORDER } from 'vs/workbench/common/theme';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { NotificationsToastsVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -64,7 +64,7 @@ export class NotificationsToasts extends Themable {
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IPartService private partService: IPartService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupsService private editorGroupService: IEditorGroupsService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ILifecycleService private lifecycleService: ILifecycleService
|
||||
) {
|
||||
@@ -80,7 +80,7 @@ export class NotificationsToasts extends Themable {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
}
|
||||
|
||||
private onDidNotificationChange(e: INotificationChangeEvent): void {
|
||||
@@ -185,7 +185,8 @@ export class NotificationsToasts extends Themable {
|
||||
let timeoutHandle: number;
|
||||
const hideAfterTimeout = () => {
|
||||
timeoutHandle = setTimeout(() => {
|
||||
if (!notificationList.hasFocus() && !item.expanded && !isMouseOverToast) {
|
||||
const showsProgress = item.hasProgress() && !item.progress.state.done;
|
||||
if (!notificationList.hasFocus() && !item.expanded && !isMouseOverToast && !showsProgress) {
|
||||
this.removeToast(item);
|
||||
} else {
|
||||
hideAfterTimeout(); // push out disposal if item has focus or is expanded
|
||||
@@ -218,11 +219,11 @@ export class NotificationsToasts extends Themable {
|
||||
|
||||
private removeToast(item: INotificationViewItem): void {
|
||||
const notificationToast = this.mapNotificationToToast.get(item);
|
||||
let focusEditor = false;
|
||||
let focusGroup = false;
|
||||
if (notificationToast) {
|
||||
const toastHasDOMFocus = isAncestor(document.activeElement, notificationToast.container);
|
||||
if (toastHasDOMFocus) {
|
||||
focusEditor = !(this.focusNext() || this.focusPrevious()); // focus next if any, otherwise focus editor
|
||||
focusGroup = !(this.focusNext() || this.focusPrevious()); // focus next if any, otherwise focus editor
|
||||
}
|
||||
|
||||
// Listeners
|
||||
@@ -241,20 +242,13 @@ export class NotificationsToasts extends Themable {
|
||||
else {
|
||||
this.doHide();
|
||||
|
||||
// Move focus to editor as needed
|
||||
if (focusEditor) {
|
||||
this.focusEditor();
|
||||
// Move focus back to editor group as needed
|
||||
if (focusGroup) {
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private focusEditor(): void {
|
||||
const editor = this.editorService.getActiveEditor();
|
||||
if (editor) {
|
||||
editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private removeToasts(): void {
|
||||
this.mapNotificationToToast.forEach(toast => dispose(toast.disposeables));
|
||||
this.mapNotificationToToast.clear();
|
||||
@@ -271,17 +265,17 @@ export class NotificationsToasts extends Themable {
|
||||
this.notificationsToastsVisibleContextKey.set(false);
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
const focusEditor = isAncestor(document.activeElement, this.notificationsToastsContainer);
|
||||
hide(): void {
|
||||
const focusGroup = isAncestor(document.activeElement, this.notificationsToastsContainer);
|
||||
|
||||
this.removeToasts();
|
||||
|
||||
if (focusEditor) {
|
||||
this.focusEditor();
|
||||
if (focusGroup) {
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
}
|
||||
}
|
||||
|
||||
public focus(): boolean {
|
||||
focus(): boolean {
|
||||
const toasts = this.getToasts(ToastVisibility.VISIBLE);
|
||||
if (toasts.length > 0) {
|
||||
toasts[0].list.focusFirst();
|
||||
@@ -292,7 +286,7 @@ export class NotificationsToasts extends Themable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public focusNext(): boolean {
|
||||
focusNext(): boolean {
|
||||
const toasts = this.getToasts(ToastVisibility.VISIBLE);
|
||||
for (let i = 0; i < toasts.length; i++) {
|
||||
const toast = toasts[i];
|
||||
@@ -311,7 +305,7 @@ export class NotificationsToasts extends Themable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public focusPrevious(): boolean {
|
||||
focusPrevious(): boolean {
|
||||
const toasts = this.getToasts(ToastVisibility.VISIBLE);
|
||||
for (let i = 0; i < toasts.length; i++) {
|
||||
const toast = toasts[i];
|
||||
@@ -330,7 +324,7 @@ export class NotificationsToasts extends Themable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public focusFirst(): boolean {
|
||||
focusFirst(): boolean {
|
||||
const toast = this.getToasts(ToastVisibility.VISIBLE)[0];
|
||||
if (toast) {
|
||||
toast.list.focusFirst();
|
||||
@@ -341,7 +335,7 @@ export class NotificationsToasts extends Themable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public focusLast(): boolean {
|
||||
focusLast(): boolean {
|
||||
const toasts = this.getToasts(ToastVisibility.VISIBLE);
|
||||
if (toasts.length > 0) {
|
||||
toasts[toasts.length - 1].list.focusFirst();
|
||||
@@ -352,7 +346,7 @@ export class NotificationsToasts extends Themable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public update(isCenterVisible: boolean): void {
|
||||
update(isCenterVisible: boolean): void {
|
||||
if (this.isNotificationsCenterVisible !== isCenterVisible) {
|
||||
this.isNotificationsCenterVisible = isCenterVisible;
|
||||
|
||||
@@ -397,7 +391,7 @@ export class NotificationsToasts extends Themable {
|
||||
return notificationToasts.reverse(); // from newest to oldest
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
layout(dimension: Dimension): void {
|
||||
this.workbenchDimensions = dimension;
|
||||
|
||||
const maxDimensions = this.computeMaxDimensions();
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { clearNode, addClass, removeClass, toggleClass, addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { clearNode, addClass, removeClass, toggleClass, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
@@ -26,7 +26,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
|
||||
import { Severity } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export class NotificationsListDelegate implements IDelegate<INotificationViewItem> {
|
||||
export class NotificationsListDelegate implements IVirtualDelegate<INotificationViewItem> {
|
||||
|
||||
private static readonly ROW_HEIGHT = 42;
|
||||
private static readonly LINE_HEIGHT = 22;
|
||||
@@ -46,7 +46,7 @@ export class NotificationsListDelegate implements IDelegate<INotificationViewIte
|
||||
return offsetHelper;
|
||||
}
|
||||
|
||||
public getHeight(notification: INotificationViewItem): number {
|
||||
getHeight(notification: INotificationViewItem): number {
|
||||
|
||||
// First row: message and actions
|
||||
let expandedHeight = NotificationsListDelegate.ROW_HEIGHT;
|
||||
@@ -102,7 +102,7 @@ export class NotificationsListDelegate implements IDelegate<INotificationViewIte
|
||||
return preferredHeight;
|
||||
}
|
||||
|
||||
public getTemplateId(element: INotificationViewItem): string {
|
||||
getTemplateId(element: INotificationViewItem): string {
|
||||
if (element instanceof NotificationViewItem) {
|
||||
return NotificationRenderer.TEMPLATE_ID;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ interface IMessageActionHandler {
|
||||
|
||||
class NotificationMessageRenderer {
|
||||
|
||||
public static render(message: INotificationMessage, actionHandler?: IMessageActionHandler): HTMLElement {
|
||||
static render(message: INotificationMessage, actionHandler?: IMessageActionHandler): HTMLElement {
|
||||
const messageContainer = document.createElement('span');
|
||||
|
||||
// Message has no links
|
||||
@@ -181,7 +181,7 @@ class NotificationMessageRenderer {
|
||||
|
||||
export class NotificationRenderer implements IRenderer<INotificationViewItem, INotificationTemplateData> {
|
||||
|
||||
public static readonly TEMPLATE_ID = 'notification';
|
||||
static readonly TEMPLATE_ID = 'notification';
|
||||
|
||||
constructor(
|
||||
private actionRunner: IActionRunner,
|
||||
@@ -191,11 +191,11 @@ export class NotificationRenderer implements IRenderer<INotificationViewItem, IN
|
||||
) {
|
||||
}
|
||||
|
||||
public get templateId() {
|
||||
get templateId() {
|
||||
return NotificationRenderer.TEMPLATE_ID;
|
||||
}
|
||||
|
||||
public renderTemplate(container: HTMLElement): INotificationTemplateData {
|
||||
renderTemplate(container: HTMLElement): INotificationTemplateData {
|
||||
const data: INotificationTemplateData = Object.create(null);
|
||||
data.toDispose = [];
|
||||
|
||||
@@ -274,11 +274,15 @@ export class NotificationRenderer implements IRenderer<INotificationViewItem, IN
|
||||
return data;
|
||||
}
|
||||
|
||||
public renderElement(notification: INotificationViewItem, index: number, data: INotificationTemplateData): void {
|
||||
renderElement(notification: INotificationViewItem, index: number, data: INotificationTemplateData): void {
|
||||
data.renderer.setInput(notification);
|
||||
}
|
||||
|
||||
public disposeTemplate(templateData: INotificationTemplateData): void {
|
||||
disposeElement(): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: INotificationTemplateData): void {
|
||||
templateData.toDispose = dispose(templateData.toDispose);
|
||||
}
|
||||
}
|
||||
@@ -291,7 +295,7 @@ export class NotificationTemplateRenderer {
|
||||
|
||||
private static readonly SEVERITIES: ('info' | 'warning' | 'error')[] = ['info', 'warning', 'error'];
|
||||
|
||||
private inputDisposeables: IDisposable[];
|
||||
private inputDisposeables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private template: INotificationTemplateData,
|
||||
@@ -301,8 +305,6 @@ export class NotificationTemplateRenderer {
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService
|
||||
) {
|
||||
this.inputDisposeables = [];
|
||||
|
||||
if (!NotificationTemplateRenderer.closeNotificationAction) {
|
||||
NotificationTemplateRenderer.closeNotificationAction = instantiationService.createInstance(ClearNotificationAction, ClearNotificationAction.ID, ClearNotificationAction.LABEL);
|
||||
NotificationTemplateRenderer.expandNotificationAction = instantiationService.createInstance(ExpandNotificationAction, ExpandNotificationAction.ID, ExpandNotificationAction.LABEL);
|
||||
@@ -310,7 +312,7 @@ export class NotificationTemplateRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
public setInput(notification: INotificationViewItem): void {
|
||||
setInput(notification: INotificationViewItem): void {
|
||||
this.inputDisposeables = dispose(this.inputDisposeables);
|
||||
|
||||
this.render(notification);
|
||||
@@ -320,6 +322,13 @@ export class NotificationTemplateRenderer {
|
||||
|
||||
// Container
|
||||
toggleClass(this.template.container, 'expanded', notification.expanded);
|
||||
this.inputDisposeables.push(addDisposableListener(this.template.container, EventType.MOUSE_UP, e => {
|
||||
if (e.button === 1 /* Middle Button */) {
|
||||
EventHelper.stop(e);
|
||||
|
||||
notification.close();
|
||||
}
|
||||
}));
|
||||
|
||||
// Severity Icon
|
||||
this.renderSeverity(notification);
|
||||
@@ -435,8 +444,7 @@ export class NotificationTemplateRenderer {
|
||||
button.label = action.label;
|
||||
|
||||
this.inputDisposeables.push(button.onDidClick(e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
// Run action
|
||||
this.actionRunner.run(action, notification);
|
||||
@@ -474,7 +482,7 @@ export class NotificationTemplateRenderer {
|
||||
}
|
||||
|
||||
if (typeof state.worked === 'number') {
|
||||
this.template.progress.worked(state.worked).show();
|
||||
this.template.progress.setWorked(state.worked).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,7 +509,7 @@ export class NotificationTemplateRenderer {
|
||||
return keybinding ? keybinding.getLabel() : void 0;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
this.inputDisposeables = dispose(this.inputDisposeables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#e8e8e8" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#C5C5C5"/></svg>
|
||||
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 253 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#424242"/></svg>
|
||||
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 253 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#2d2d30;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>CollapseChevronDown_md_16x</title><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/><path class="icon-vs-out" d="M15.444,6.061,8,13.5.556,6.061,3.03,3.586,8,8.556l4.97-4.97Z" style="display: none;"/><path class="icon-vs-bg" d="M14.03,6.061,8,12.091,1.97,6.061,3.03,5,8,9.97,12.97,5Z"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 5.232l-7 7-7-7L2.23 4 8 9.77 13.77 4 15 5.232z" fill="#C5C5C5"/></svg>
|
||||
|
Before Width: | Height: | Size: 507 B After Width: | Height: | Size: 178 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>CollapseChevronDown_md_16x</title><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/><path class="icon-vs-out" d="M15.444,6.061,8,13.5.556,6.061,3.03,3.586,8,8.556l4.97-4.97Z" style="display: none;"/><path class="icon-vs-bg" d="M14.03,6.061,8,12.091,1.97,6.061,3.03,5,8,9.97,12.97,5Z"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 5.232l-7 7-7-7L2.23 4 8 9.77 13.77 4 15 5.232z" fill="#424242"/></svg>
|
||||
|
Before Width: | Height: | Size: 507 B After Width: | Height: | Size: 178 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#262626"><circle cx="3.5" cy="7.5" r="2.5"/><circle cx="8.5" cy="7.5" r="2.5"/><circle cx="13.5" cy="7.5" r="2.5"/></g><g fill="#C5C5C5"><circle cx="3.5" cy="7.5" r="1.5"/><circle cx="8.5" cy="7.5" r="1.5"/><circle cx="13.5" cy="7.5" r="1.5"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>Ellipsis_bold_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M6,7.5A2.5,2.5,0,1,1,3.5,5,2.5,2.5,0,0,1,6,7.5ZM8.5,5A2.5,2.5,0,1,0,11,7.5,2.5,2.5,0,0,0,8.5,5Zm5,0A2.5,2.5,0,1,0,16,7.5,2.5,2.5,0,0,0,13.5,5Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M5,7.5A1.5,1.5,0,1,1,3.5,6,1.5,1.5,0,0,1,5,7.5ZM8.5,6A1.5,1.5,0,1,0,10,7.5,1.5,1.5,0,0,0,8.5,6Zm5,0A1.5,1.5,0,1,0,15,7.5,1.5,1.5,0,0,0,13.5,6Z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 325 B After Width: | Height: | Size: 748 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#F6F6F6"><circle cx="3.5" cy="7.5" r="2.5"/><circle cx="8.5" cy="7.5" r="2.5"/><circle cx="13.5" cy="7.5" r="2.5"/></g><g fill="#424242"><circle cx="3.5" cy="7.5" r="1.5"/><circle cx="8.5" cy="7.5" r="1.5"/><circle cx="13.5" cy="7.5" r="1.5"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>Ellipsis_bold_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M6,7.5A2.5,2.5,0,1,1,3.5,5,2.5,2.5,0,0,1,6,7.5ZM8.5,5A2.5,2.5,0,1,0,11,7.5,2.5,2.5,0,0,0,8.5,5Zm5,0A2.5,2.5,0,1,0,16,7.5,2.5,2.5,0,0,0,13.5,5Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M5,7.5A1.5,1.5,0,1,1,3.5,6,1.5,1.5,0,0,1,5,7.5ZM8.5,6A1.5,1.5,0,1,0,10,7.5,1.5,1.5,0,0,0,8.5,6Zm5,0A1.5,1.5,0,1,0,15,7.5,1.5,1.5,0,0,0,13.5,6Z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 325 B After Width: | Height: | Size: 748 B |
@@ -37,48 +37,63 @@
|
||||
/** Panel Switcher */
|
||||
|
||||
.monaco-workbench > .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more {
|
||||
background: url('ellipsis.svg') center center no-repeat;
|
||||
background-image: url('ellipsis.svg');
|
||||
display: block;
|
||||
height: 31px;
|
||||
height: 28px;
|
||||
min-width: 28px;
|
||||
margin-left: 0px;
|
||||
margin-right: 0px;
|
||||
background-size: 16px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar {
|
||||
line-height: 30px;
|
||||
line-height: 27px; /* matches panel titles in settings */
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:first-child {
|
||||
margin-left: 12px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item {
|
||||
text-transform: uppercase;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
font-size: 11px;
|
||||
padding-bottom: 3px; /* puts the bottom border down */
|
||||
padding-top: 2px;
|
||||
padding-top: 4px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label{
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:last-child {
|
||||
padding-right: 6px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked .action-label {
|
||||
border-bottom: 1px solid;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .badge {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .badge .badge-content {
|
||||
padding: 0.2em 0.5em;
|
||||
padding: 0.3em 0.5em;
|
||||
border-radius: 1em;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
display: inline;
|
||||
}
|
||||
display: inline-block;
|
||||
min-width: 1.6em;
|
||||
line-height: 1em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/** Actions */
|
||||
|
||||
@@ -89,6 +104,7 @@
|
||||
.monaco-workbench .panel .monaco-action-bar .action-item .monaco-select-box {
|
||||
cursor: pointer;
|
||||
min-width: 110px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.monaco-workbench .hide-panel-action {
|
||||
@@ -156,5 +172,5 @@
|
||||
|
||||
.vs-dark .monaco-workbench > .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more,
|
||||
.hc-black .monaco-workbench > .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more {
|
||||
background: url('ellipsis-inverse.svg') center center no-repeat;
|
||||
background-image: url('ellipsis-inverse.svg');
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#2d2d30;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>CollapseChevronUp_md_16x</title><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/><path class="icon-vs-out" d="M15.444,9.939,12.97,12.414,8,7.444l-4.97,4.97L.556,9.939,8,2.5Z" style="display: none;"/><path class="icon-vs-bg" d="M14.03,9.939,12.97,11,8,6.03,3.03,11,1.97,9.939,8,3.909Z"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 11l-1.23 1.232L8 6.462l-5.77 5.77L1 11l7-7 7 7z" fill="#C5C5C5"/></svg>
|
||||
|
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 179 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>CollapseChevronUp_md_16x</title><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/><path class="icon-vs-out" d="M15.444,9.939,12.97,12.414,8,7.444l-4.97,4.97L.556,9.939,8,2.5Z" style="display: none;"/><path class="icon-vs-bg" d="M14.03,9.939,12.97,11,8,6.03,3.03,11,1.97,9.939,8,3.909Z"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 11l-1.23 1.232L8 6.462l-5.77 5.77L1 11l7-7 7 7z" fill="#424242"/></svg>
|
||||
|
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 179 B |
@@ -10,7 +10,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/actions';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService';
|
||||
@@ -18,6 +18,7 @@ import { ActivityAction } from 'vs/workbench/browser/parts/compositebar/composit
|
||||
import { IActivity } from 'vs/workbench/common/activity';
|
||||
|
||||
export class ClosePanelAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.closePanel';
|
||||
static LABEL = nls.localize('closePanel', "Close Panel");
|
||||
|
||||
@@ -29,12 +30,13 @@ export class ClosePanelAction extends Action {
|
||||
super(id, name, 'hide-panel-action');
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
run(): TPromise<any> {
|
||||
return this.partService.setPanelHidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class TogglePanelAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.togglePanel';
|
||||
static LABEL = nls.localize('togglePanel', "Toggle Panel");
|
||||
|
||||
@@ -46,15 +48,15 @@ export class TogglePanelAction extends Action {
|
||||
super(id, name, partService.isVisible(Parts.PANEL_PART) ? 'panel expanded' : 'panel');
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
run(): TPromise<any> {
|
||||
return this.partService.setPanelHidden(this.partService.isVisible(Parts.PANEL_PART));
|
||||
}
|
||||
}
|
||||
|
||||
class FocusPanelAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.focusPanel';
|
||||
public static readonly LABEL = nls.localize('focusPanel', "Focus into Panel");
|
||||
static readonly ID = 'workbench.action.focusPanel';
|
||||
static readonly LABEL = nls.localize('focusPanel', "Focus into Panel");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -65,7 +67,7 @@ class FocusPanelAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
run(): TPromise<any> {
|
||||
|
||||
// Show panel
|
||||
if (!this.partService.isVisible(Parts.PANEL_PART)) {
|
||||
@@ -83,10 +85,12 @@ class FocusPanelAction extends Action {
|
||||
|
||||
export class TogglePanelPositionAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.togglePanelPosition';
|
||||
public static readonly LABEL = nls.localize('toggledPanelPosition', "Toggle Panel Position");
|
||||
static readonly ID = 'workbench.action.togglePanelPosition';
|
||||
static readonly LABEL = nls.localize('toggledPanelPosition', "Toggle Panel Position");
|
||||
|
||||
private static readonly MOVE_TO_RIGHT_LABEL = nls.localize('moveToRight', "Move to Right");
|
||||
private static readonly MOVE_TO_BOTTOM_LABEL = nls.localize('moveToBottom', "Move to Bottom");
|
||||
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
constructor(
|
||||
@@ -106,12 +110,12 @@ export class TogglePanelPositionAction extends Action {
|
||||
setClassAndLabel();
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
run(): TPromise<any> {
|
||||
const position = this.partService.getPanelPosition();
|
||||
return this.partService.setPanelPosition(position === Position.BOTTOM ? Position.RIGHT : Position.BOTTOM);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
@@ -119,10 +123,12 @@ export class TogglePanelPositionAction extends Action {
|
||||
|
||||
export class ToggleMaximizedPanelAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.toggleMaximizedPanel';
|
||||
public static readonly LABEL = nls.localize('toggleMaximizedPanel', "Toggle Maximized Panel");
|
||||
static readonly ID = 'workbench.action.toggleMaximizedPanel';
|
||||
static readonly LABEL = nls.localize('toggleMaximizedPanel', "Toggle Maximized Panel");
|
||||
|
||||
private static readonly MAXIMIZE_LABEL = nls.localize('maximizePanel', "Maximize Panel Size");
|
||||
private static readonly RESTORE_LABEL = nls.localize('minimizePanel', "Restore Panel Size");
|
||||
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
constructor(
|
||||
@@ -139,13 +145,12 @@ export class ToggleMaximizedPanelAction extends Action {
|
||||
}));
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
// Show panel
|
||||
run(): TPromise<any> {
|
||||
return (!this.partService.isVisible(Parts.PANEL_PART) ? this.partService.setPanelHidden(false) : TPromise.as(null))
|
||||
.then(() => this.partService.toggleMaximizedPanel());
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
@@ -160,7 +165,7 @@ export class PanelActivityAction extends ActivityAction {
|
||||
super(activity);
|
||||
}
|
||||
|
||||
public run(event: any): TPromise<any> {
|
||||
run(event: any): TPromise<any> {
|
||||
return this.panelService.openPanel(this.activity.id, true).then(() => this.activate());
|
||||
}
|
||||
}
|
||||
@@ -172,3 +177,12 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedP
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View"));
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL), 'View: Toggle Panel Position', nls.localize('view', "View"));
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, undefined), 'View: Toggle Panel Position', nls.localize('view', "View"));
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '2_workbench_layout',
|
||||
command: {
|
||||
id: TogglePanelAction.ID,
|
||||
title: nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel")
|
||||
},
|
||||
order: 5
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import 'vs/css!./media/panelpart';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { $ } from 'vs/base/browser/builder';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
@@ -20,28 +20,35 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ClosePanelAction, TogglePanelPositionAction, PanelActivityAction, ToggleMaximizedPanelAction } from 'vs/workbench/browser/parts/panel/panelActions';
|
||||
import { ClosePanelAction, TogglePanelPositionAction, PanelActivityAction, ToggleMaximizedPanelAction, TogglePanelAction } from 'vs/workbench/browser/parts/panel/panelActions';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { activeContrastBorder, focusBorder, contrastBorder, editorBackground, badgeBackground, badgeForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar';
|
||||
import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
const ActivePanleContextId = 'activePanel';
|
||||
export const ActivePanelContext = new RawContextKey<string>(ActivePanleContextId, '');
|
||||
|
||||
export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
|
||||
public static readonly activePanelSettingsKey = 'workbench.panelpart.activepanelid';
|
||||
static readonly activePanelSettingsKey = 'workbench.panelpart.activepanelid';
|
||||
|
||||
private static readonly PINNED_PANELS = 'workbench.panel.pinnedPanels';
|
||||
private static readonly MIN_COMPOSITE_BAR_WIDTH = 50;
|
||||
|
||||
public _serviceBrand: any;
|
||||
_serviceBrand: any;
|
||||
|
||||
private activePanelContextKey: IContextKey<string>;
|
||||
private blockOpeningPanel: boolean;
|
||||
private compositeBar: CompositeBar;
|
||||
private compositeActions: { [compositeId: string]: { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null);
|
||||
private dimension: Dimension;
|
||||
|
||||
constructor(
|
||||
@@ -53,7 +60,8 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
@IPartService partService: IPartService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
) {
|
||||
super(
|
||||
notificationService,
|
||||
@@ -74,17 +82,18 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
{ hasTitle: true }
|
||||
);
|
||||
|
||||
this.compositeBar = this.instantiationService.createInstance(CompositeBar, {
|
||||
this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, {
|
||||
icon: false,
|
||||
storageId: PanelPart.PINNED_PANELS,
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
composites: this.getPanels(),
|
||||
openComposite: (compositeId: string) => this.openPanel(compositeId, true),
|
||||
getActivityAction: (compositeId: string) => this.instantiationService.createInstance(PanelActivityAction, this.getPanel(compositeId)),
|
||||
getCompositePinnedAction: (compositeId: string) => new ToggleCompositePinnedAction(this.getPanel(compositeId), this.compositeBar),
|
||||
getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction,
|
||||
getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction,
|
||||
getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(PanelActivityAction, this.getPanel(compositeId)),
|
||||
getContextMenuActions: () => [this.instantiationService.createInstance(TogglePanelAction, TogglePanelAction.ID, localize('hidePanel', "Hide Panel"))],
|
||||
getDefaultCompositeId: () => Registry.as<PanelRegistry>(PanelExtensions.Panels).getDefaultPanelId(),
|
||||
hidePart: () => this.partService.setPanelHidden(true),
|
||||
compositeSize: 0,
|
||||
overflowActionSize: 44,
|
||||
colors: {
|
||||
backgroundColor: PANEL_BACKGROUND,
|
||||
@@ -92,37 +101,54 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
badgeForeground,
|
||||
dragAndDropBackground: PANEL_DRAG_AND_DROP_BACKGROUND
|
||||
}
|
||||
});
|
||||
this.toUnbind.push(this.compositeBar);
|
||||
}));
|
||||
|
||||
for (const panel of this.getPanels()) {
|
||||
this.compositeBar.addComposite(panel);
|
||||
}
|
||||
|
||||
this.activePanelContextKey = ActivePanelContext.bindTo(contextKeyService);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.onDidPanelOpen(this._onDidPanelOpen, this));
|
||||
this._register(this.onDidPanelClose(this._onDidPanelClose, this));
|
||||
|
||||
this.toUnbind.push(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor, false)));
|
||||
this._register(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor)));
|
||||
|
||||
// Activate panel action on opening of a panel
|
||||
this.toUnbind.push(this.onDidPanelOpen(panel => {
|
||||
this._register(this.onDidPanelOpen(panel => {
|
||||
this.compositeBar.activateComposite(panel.getId());
|
||||
// Need to relayout composite bar since different panels have different action bar width
|
||||
this.layoutCompositeBar();
|
||||
this.layoutCompositeBar(); // Need to relayout composite bar since different panels have different action bar width
|
||||
}));
|
||||
this.toUnbind.push(this.compositeBar.onDidContextMenu(e => this.showContextMenu(e)));
|
||||
|
||||
// Deactivate panel action on close
|
||||
this.toUnbind.push(this.onDidPanelClose(panel => this.compositeBar.deactivateComposite(panel.getId())));
|
||||
this._register(this.onDidPanelClose(panel => this.compositeBar.deactivateComposite(panel.getId())));
|
||||
}
|
||||
|
||||
public get onDidPanelOpen(): Event<IPanel> {
|
||||
private _onDidPanelOpen(panel: IPanel): void {
|
||||
this.activePanelContextKey.set(panel.getId());
|
||||
}
|
||||
|
||||
private _onDidPanelClose(panel: IPanel): void {
|
||||
const id = panel.getId();
|
||||
|
||||
if (this.activePanelContextKey.get() === id) {
|
||||
this.activePanelContextKey.reset();
|
||||
}
|
||||
}
|
||||
|
||||
get onDidPanelOpen(): Event<IPanel> {
|
||||
return this._onDidCompositeOpen.event;
|
||||
}
|
||||
|
||||
public get onDidPanelClose(): Event<IPanel> {
|
||||
get onDidPanelClose(): Event<IPanel> {
|
||||
return this._onDidCompositeClose.event;
|
||||
}
|
||||
|
||||
public updateStyles(): void {
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
const container = $(this.getContainer());
|
||||
@@ -133,7 +159,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
title.style('border-top-color', this.getColor(PANEL_BORDER) || this.getColor(contrastBorder));
|
||||
}
|
||||
|
||||
public openPanel(id: string, focus?: boolean): TPromise<Panel> {
|
||||
openPanel(id: string, focus?: boolean): TPromise<Panel> {
|
||||
if (this.blockOpeningPanel) {
|
||||
return TPromise.as(null); // Workaround against a potential race condition
|
||||
}
|
||||
@@ -152,7 +178,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
return promise.then(() => this.openComposite(id, focus));
|
||||
}
|
||||
|
||||
public showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable {
|
||||
showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable {
|
||||
return this.compositeBar.showActivity(panelId, badge, clazz);
|
||||
}
|
||||
|
||||
@@ -160,31 +186,20 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
return this.getPanels().filter(p => p.id === panelId).pop();
|
||||
}
|
||||
|
||||
private showContextMenu(e: MouseEvent): void {
|
||||
const event = new StandardMouseEvent(e);
|
||||
const actions: Action[] = this.getPanels().map(panel => this.instantiationService.createInstance(ToggleCompositePinnedAction, panel, this.compositeBar));
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => { return { x: event.posx, y: event.posy }; },
|
||||
getActions: () => TPromise.as(actions),
|
||||
onHide: () => dispose(actions)
|
||||
});
|
||||
}
|
||||
|
||||
public getPanels(): PanelDescriptor[] {
|
||||
getPanels(): PanelDescriptor[] {
|
||||
return Registry.as<PanelRegistry>(PanelExtensions.Panels).getPanels()
|
||||
.filter(p => p.enabled)
|
||||
.sort((v1, v2) => v1.order - v2.order);
|
||||
}
|
||||
|
||||
public setPanelEnablement(id: string, enabled: boolean): void {
|
||||
setPanelEnablement(id: string, enabled: boolean): void {
|
||||
const descriptor = Registry.as<PanelRegistry>(PanelExtensions.Panels).getPanels().filter(p => p.id === id).pop();
|
||||
if (descriptor && descriptor.enabled !== enabled) {
|
||||
descriptor.enabled = enabled;
|
||||
if (enabled) {
|
||||
this.compositeBar.addComposite(descriptor, true);
|
||||
this.compositeBar.addComposite(descriptor);
|
||||
} else {
|
||||
this.compositeBar.removeComposite(id);
|
||||
this.removeComposite(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,15 +212,15 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
];
|
||||
}
|
||||
|
||||
public getActivePanel(): IPanel {
|
||||
getActivePanel(): IPanel {
|
||||
return this.getActiveComposite();
|
||||
}
|
||||
|
||||
public getLastActivePanelId(): string {
|
||||
getLastActivePanelId(): string {
|
||||
return this.getLastActiveCompositetId();
|
||||
}
|
||||
|
||||
public hideActivePanel(): TPromise<void> {
|
||||
hideActivePanel(): TPromise<void> {
|
||||
return this.hideActiveComposite().then(composite => void 0);
|
||||
}
|
||||
|
||||
@@ -226,7 +241,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
};
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): Dimension[] {
|
||||
layout(dimension: Dimension): Dimension[] {
|
||||
if (!this.partService.isVisible(Parts.PANEL_PART)) {
|
||||
return [dimension];
|
||||
}
|
||||
@@ -243,11 +258,6 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
return sizes;
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.compositeBar.shutdown();
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
private layoutCompositeBar(): void {
|
||||
if (this.dimension) {
|
||||
let availableWidth = this.dimension.width - 40; // take padding into account
|
||||
@@ -259,6 +269,28 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
}
|
||||
}
|
||||
|
||||
private getCompositeActions(compositeId: string): { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } {
|
||||
let compositeActions = this.compositeActions[compositeId];
|
||||
if (!compositeActions) {
|
||||
compositeActions = {
|
||||
activityAction: this.instantiationService.createInstance(PanelActivityAction, this.getPanel(compositeId)),
|
||||
pinnedAction: new ToggleCompositePinnedAction(this.getPanel(compositeId), this.compositeBar)
|
||||
};
|
||||
this.compositeActions[compositeId] = compositeActions;
|
||||
}
|
||||
return compositeActions;
|
||||
}
|
||||
|
||||
private removeComposite(compositeId: string): void {
|
||||
this.compositeBar.removeComposite(compositeId);
|
||||
const compositeActions = this.compositeActions[compositeId];
|
||||
if (compositeActions) {
|
||||
compositeActions.activityAction.dispose();
|
||||
compositeActions.pinnedAction.dispose();
|
||||
delete this.compositeActions[compositeId];
|
||||
}
|
||||
}
|
||||
|
||||
private getToolbarWidth(): number {
|
||||
const activePanel = this.getActivePanel();
|
||||
if (!activePanel) {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>arrow-left</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="arrow-left" fill="#c5c5c5">
|
||||
<polygon id="Shape" points="6 3 0 8 6 13 6 10 10 10 10 6 6 6"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 594 B |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>arrow-left</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="arrow-left" fill="#424242">
|
||||
<polygon id="Shape" points="6 3 0 8 6 13 6 10 10 10 10 6 6 6"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 594 B |
@@ -0,0 +1,18 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { QuickPickManyToggle, BackAction } from 'vs/workbench/browser/parts/quickinput/quickInput';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen';
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule(QuickPickManyToggle);
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(BackAction, BackAction.ID, BackAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Back');
|
||||
@@ -12,6 +12,39 @@
|
||||
margin-left: -300px;
|
||||
}
|
||||
|
||||
.quick-input-titlebar {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.quick-input-left-action-bar {
|
||||
display: flex;
|
||||
margin-left: 4px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-left-action-bar.monaco-action-bar .actions-container {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.quick-input-title {
|
||||
padding: 3px 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.quick-input-right-action-bar {
|
||||
display: flex;
|
||||
margin-right: 4px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-titlebar .monaco-action-bar .action-label.icon {
|
||||
margin: 0;
|
||||
width: 19px;
|
||||
height: 100%;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.quick-input-header {
|
||||
display: flex;
|
||||
padding: 6px 6px 4px 6px;
|
||||
@@ -32,10 +65,15 @@
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.quick-input-widget[data-type=selectMany] .quick-input-box {
|
||||
.quick-input-widget.show-checkboxes .quick-input-box {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.quick-input-visible-count {
|
||||
position: absolute;
|
||||
left: -10000px;
|
||||
}
|
||||
|
||||
.quick-input-count {
|
||||
align-self: center;
|
||||
position: absolute;
|
||||
@@ -65,56 +103,69 @@
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.quick-input-checkbox-list {
|
||||
.quick-input-list {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.quick-input-checkbox-list .monaco-list {
|
||||
.quick-input-list .monaco-list {
|
||||
overflow: hidden;
|
||||
max-height: calc(20 * 22px);
|
||||
}
|
||||
|
||||
.quick-input-checkbox-list .quick-input-checkbox-list-entry {
|
||||
.quick-input-list .quick-input-list-entry {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.quick-input-checkbox-list .quick-input-checkbox-list-label {
|
||||
.quick-input-list .quick-input-list-label {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-input-checkbox-list .quick-input-checkbox-list-checkbox {
|
||||
.quick-input-list .quick-input-list-checkbox {
|
||||
align-self: center;
|
||||
margin: 0;
|
||||
/* TODO */
|
||||
/* margin-top: 5px; */
|
||||
}
|
||||
|
||||
.quick-input-checkbox-list .quick-input-checkbox-list-rows {
|
||||
.quick-input-list .quick-input-list-rows {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.quick-input-widget.show-checkboxes .quick-input-list .quick-input-list-rows {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.quick-input-checkbox-list .quick-input-checkbox-list-rows > .quick-input-checkbox-list-row {
|
||||
.quick-input-widget .quick-input-list .quick-input-list-checkbox {
|
||||
display: none;
|
||||
}
|
||||
.quick-input-widget.show-checkboxes .quick-input-list .quick-input-list-checkbox {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-rows > .quick-input-list-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-input-checkbox-list .quick-input-checkbox-list-rows .monaco-highlighted-label span {
|
||||
.quick-input-list .quick-input-list-rows .monaco-highlighted-label span {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.quick-input-checkbox-list .quick-input-checkbox-list-label-meta {
|
||||
.quick-input-list .quick-input-list-label-meta {
|
||||
opacity: 0.7;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.quick-input-list .monaco-highlighted-label .highlight {
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -67,8 +67,24 @@ export class QuickInputBox {
|
||||
this.inputBox.setPlaceHolder(placeholder);
|
||||
}
|
||||
|
||||
setPassword(isPassword: boolean): void {
|
||||
this.inputBox.inputElement.type = isPassword ? 'password' : 'text';
|
||||
get placeholder() {
|
||||
return this.inputBox.inputElement.getAttribute('placeholder');
|
||||
}
|
||||
|
||||
set placeholder(placeholder: string) {
|
||||
this.inputBox.setPlaceHolder(placeholder);
|
||||
}
|
||||
|
||||
get password() {
|
||||
return this.inputBox.inputElement.type === 'password';
|
||||
}
|
||||
|
||||
set password(password: boolean) {
|
||||
this.inputBox.inputElement.type = password ? 'password' : 'text';
|
||||
}
|
||||
|
||||
set enabled(enabled: boolean) {
|
||||
this.inputBox.setEnabled(enabled);
|
||||
}
|
||||
|
||||
showDecoration(decoration: Severity): void {
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./quickInput';
|
||||
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { WorkbenchList } from 'vs/platform/list/browser/listService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IMatch } from 'vs/base/common/filters';
|
||||
import { matchesFuzzyOcticonAware, parseOcticons } from 'vs/base/common/octicon';
|
||||
import { compareAnything } from 'vs/base/common/comparers';
|
||||
@@ -24,23 +24,25 @@ import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlighte
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { listFocusBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
interface ICheckableElement {
|
||||
interface IListElement {
|
||||
index: number;
|
||||
item: IPickOpenEntry;
|
||||
item: IQuickPickItem;
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
class CheckableElement implements ICheckableElement {
|
||||
class ListElement implements IListElement {
|
||||
index: number;
|
||||
item: IPickOpenEntry;
|
||||
item: IQuickPickItem;
|
||||
shouldAlwaysShow = false;
|
||||
hidden = false;
|
||||
private _onChecked = new Emitter<boolean>();
|
||||
onChecked = this._onChecked.event;
|
||||
_checked: boolean;
|
||||
_checked?: boolean;
|
||||
get checked() {
|
||||
return this._checked;
|
||||
}
|
||||
@@ -54,58 +56,59 @@ class CheckableElement implements ICheckableElement {
|
||||
descriptionHighlights?: IMatch[];
|
||||
detailHighlights?: IMatch[];
|
||||
|
||||
constructor(init: ICheckableElement) {
|
||||
constructor(init: IListElement) {
|
||||
assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
interface ICheckableElementTemplateData {
|
||||
interface IListElementTemplateData {
|
||||
checkbox: HTMLInputElement;
|
||||
label: IconLabel;
|
||||
detail: HighlightedLabel;
|
||||
element: CheckableElement;
|
||||
element: ListElement;
|
||||
toDisposeElement: IDisposable[];
|
||||
toDisposeTemplate: IDisposable[];
|
||||
}
|
||||
|
||||
class CheckableElementRenderer implements IRenderer<CheckableElement, ICheckableElementTemplateData> {
|
||||
class ListElementRenderer implements IRenderer<ListElement, IListElementTemplateData> {
|
||||
|
||||
static readonly ID = 'checkableelement';
|
||||
static readonly ID = 'listelement';
|
||||
|
||||
get templateId() {
|
||||
return CheckableElementRenderer.ID;
|
||||
return ListElementRenderer.ID;
|
||||
}
|
||||
|
||||
renderTemplate(container: HTMLElement): ICheckableElementTemplateData {
|
||||
const data: ICheckableElementTemplateData = Object.create(null);
|
||||
|
||||
const entry = dom.append(container, $('.quick-input-checkbox-list-entry'));
|
||||
const label = dom.append(entry, $('label.quick-input-checkbox-list-label'));
|
||||
|
||||
// Entry
|
||||
data.checkbox = <HTMLInputElement>dom.append(label, $('input.quick-input-checkbox-list-checkbox'));
|
||||
data.checkbox.type = 'checkbox';
|
||||
renderTemplate(container: HTMLElement): IListElementTemplateData {
|
||||
const data: IListElementTemplateData = Object.create(null);
|
||||
data.toDisposeElement = [];
|
||||
data.toDisposeTemplate = [];
|
||||
|
||||
const entry = dom.append(container, $('.quick-input-list-entry'));
|
||||
|
||||
// Checkbox
|
||||
const label = dom.append(entry, $('label.quick-input-list-label'));
|
||||
data.checkbox = <HTMLInputElement>dom.append(label, $('input.quick-input-list-checkbox'));
|
||||
data.checkbox.type = 'checkbox';
|
||||
data.toDisposeTemplate.push(dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, e => {
|
||||
data.element.checked = data.checkbox.checked;
|
||||
}));
|
||||
|
||||
const rows = dom.append(label, $('.quick-input-checkbox-list-rows'));
|
||||
const row1 = dom.append(rows, $('.quick-input-checkbox-list-row'));
|
||||
const row2 = dom.append(rows, $('.quick-input-checkbox-list-row'));
|
||||
// Rows
|
||||
const rows = dom.append(label, $('.quick-input-list-rows'));
|
||||
const row1 = dom.append(rows, $('.quick-input-list-row'));
|
||||
const row2 = dom.append(rows, $('.quick-input-list-row'));
|
||||
|
||||
// Label
|
||||
data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true });
|
||||
|
||||
// Detail
|
||||
const detailContainer = dom.append(row2, $('.quick-input-checkbox-list-label-meta'));
|
||||
const detailContainer = dom.append(row2, $('.quick-input-list-label-meta'));
|
||||
data.detail = new HighlightedLabel(detailContainer);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
renderElement(element: CheckableElement, index: number, data: ICheckableElementTemplateData): void {
|
||||
renderElement(element: ListElement, index: number, data: IListElementTemplateData): void {
|
||||
data.toDisposeElement = dispose(data.toDisposeElement);
|
||||
data.element = element;
|
||||
data.checkbox.checked = element.checked;
|
||||
@@ -124,34 +127,43 @@ class CheckableElementRenderer implements IRenderer<CheckableElement, ICheckable
|
||||
data.detail.set(element.item.detail, detailHighlights);
|
||||
}
|
||||
|
||||
disposeTemplate(data: ICheckableElementTemplateData): void {
|
||||
disposeElement(): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
disposeTemplate(data: IListElementTemplateData): void {
|
||||
data.toDisposeElement = dispose(data.toDisposeElement);
|
||||
data.toDisposeTemplate = dispose(data.toDisposeTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
class CheckableElementDelegate implements IDelegate<CheckableElement> {
|
||||
class ListElementDelegate implements IVirtualDelegate<ListElement> {
|
||||
|
||||
getHeight(element: CheckableElement): number {
|
||||
getHeight(element: ListElement): number {
|
||||
return element.item.detail ? 44 : 22;
|
||||
}
|
||||
|
||||
getTemplateId(element: CheckableElement): string {
|
||||
return CheckableElementRenderer.ID;
|
||||
getTemplateId(element: ListElement): string {
|
||||
return ListElementRenderer.ID;
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickInputCheckboxList {
|
||||
export class QuickInputList {
|
||||
|
||||
private container: HTMLElement;
|
||||
private list: WorkbenchList<CheckableElement>;
|
||||
private elements: CheckableElement[] = [];
|
||||
private list: WorkbenchList<ListElement>;
|
||||
private elements: ListElement[] = [];
|
||||
private elementsToIndexes = new Map<IQuickPickItem, number>();
|
||||
matchOnDescription = false;
|
||||
matchOnDetail = false;
|
||||
private _onAllVisibleCheckedChanged = new Emitter<boolean>(); // TODO: Debounce
|
||||
onAllVisibleCheckedChanged: Event<boolean> = this._onAllVisibleCheckedChanged.event;
|
||||
private _onCheckedCountChanged = new Emitter<number>(); // TODO: Debounce
|
||||
onCheckedCountChanged: Event<number> = this._onCheckedCountChanged.event;
|
||||
private _onChangedAllVisibleChecked = new Emitter<boolean>();
|
||||
onChangedAllVisibleChecked: Event<boolean> = this._onChangedAllVisibleChecked.event;
|
||||
private _onChangedCheckedCount = new Emitter<number>();
|
||||
onChangedCheckedCount: Event<number> = this._onChangedCheckedCount.event;
|
||||
private _onChangedVisibleCount = new Emitter<number>();
|
||||
onChangedVisibleCount: Event<number> = this._onChangedVisibleCount.event;
|
||||
private _onChangedCheckedElements = new Emitter<IQuickPickItem[]>();
|
||||
onChangedCheckedElements: Event<IQuickPickItem[]> = this._onChangedCheckedElements.event;
|
||||
private _onLeave = new Emitter<void>();
|
||||
onLeave: Event<void> = this._onLeave.event;
|
||||
private _fireCheckedEvents = true;
|
||||
@@ -162,12 +174,12 @@ export class QuickInputCheckboxList {
|
||||
private parent: HTMLElement,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
this.container = dom.append(this.parent, $('.quick-input-checkbox-list'));
|
||||
const delegate = new CheckableElementDelegate();
|
||||
this.list = this.instantiationService.createInstance(WorkbenchList, this.container, delegate, [new CheckableElementRenderer()], {
|
||||
this.container = dom.append(this.parent, $('.quick-input-list'));
|
||||
const delegate = new ListElementDelegate();
|
||||
this.list = this.instantiationService.createInstance(WorkbenchList, this.container, delegate, [new ListElementRenderer()], {
|
||||
identityProvider: element => element.label,
|
||||
multipleSelectionSupport: false
|
||||
}) as WorkbenchList<CheckableElement>;
|
||||
}) as WorkbenchList<ListElement>;
|
||||
this.disposables.push(this.list);
|
||||
this.disposables.push(this.list.onKeyDown(e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
@@ -181,12 +193,14 @@ export class QuickInputCheckboxList {
|
||||
}
|
||||
break;
|
||||
case KeyCode.UpArrow:
|
||||
case KeyCode.PageUp:
|
||||
const focus1 = this.list.getFocus();
|
||||
if (focus1.length === 1 && focus1[0] === 0) {
|
||||
this._onLeave.fire();
|
||||
}
|
||||
break;
|
||||
case KeyCode.DownArrow:
|
||||
case KeyCode.PageDown:
|
||||
const focus2 = this.list.getFocus();
|
||||
if (focus2.length === 1 && focus2[0] === this.list.length - 1) {
|
||||
this._onLeave.fire();
|
||||
@@ -207,15 +221,20 @@ export class QuickInputCheckboxList {
|
||||
}
|
||||
|
||||
@memoize
|
||||
get onFocusChange() {
|
||||
get onDidChangeFocus() {
|
||||
return mapEvent(this.list.onFocusChange, e => e.elements.map(e => e.item));
|
||||
}
|
||||
|
||||
@memoize
|
||||
get onDidChangeSelection() {
|
||||
return mapEvent(this.list.onSelectionChange, e => e.elements.map(e => e.item));
|
||||
}
|
||||
|
||||
getAllVisibleChecked() {
|
||||
return this.allVisibleChecked(this.elements, false);
|
||||
}
|
||||
|
||||
private allVisibleChecked(elements: CheckableElement[], whenNoneVisible = true) {
|
||||
private allVisibleChecked(elements: ListElement[], whenNoneVisible = true) {
|
||||
for (let i = 0, n = elements.length; i < n; i++) {
|
||||
const element = elements[i];
|
||||
if (!element.hidden) {
|
||||
@@ -240,6 +259,17 @@ export class QuickInputCheckboxList {
|
||||
return count;
|
||||
}
|
||||
|
||||
getVisibleCount() {
|
||||
let count = 0;
|
||||
const elements = this.elements;
|
||||
for (let i = 0, n = elements.length; i < n; i++) {
|
||||
if (!elements[i].hidden) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
setAllVisibleChecked(checked: boolean) {
|
||||
try {
|
||||
this._fireCheckedEvents = false;
|
||||
@@ -254,16 +284,44 @@ export class QuickInputCheckboxList {
|
||||
}
|
||||
}
|
||||
|
||||
setElements(elements: IPickOpenEntry[]): void {
|
||||
setElements(elements: IQuickPickItem[]): void {
|
||||
this.elementDisposables = dispose(this.elementDisposables);
|
||||
this.elements = elements.map((item, index) => new CheckableElement({
|
||||
this.elements = elements.map((item, index) => new ListElement({
|
||||
index,
|
||||
item,
|
||||
checked: !!item.picked
|
||||
checked: false
|
||||
}));
|
||||
this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents())));
|
||||
|
||||
this.elementsToIndexes = this.elements.reduce((map, element, index) => {
|
||||
map.set(element.item, index);
|
||||
return map;
|
||||
}, new Map<IQuickPickItem, number>());
|
||||
this.list.splice(0, this.list.length, this.elements);
|
||||
this.list.setFocus([]);
|
||||
this._onChangedVisibleCount.fire(this.elements.length);
|
||||
}
|
||||
|
||||
getFocusedElements() {
|
||||
return this.list.getFocusedElements()
|
||||
.map(e => e.item);
|
||||
}
|
||||
|
||||
setFocusedElements(items: IQuickPickItem[]) {
|
||||
this.list.setFocus(items
|
||||
.filter(item => this.elementsToIndexes.has(item))
|
||||
.map(item => this.elementsToIndexes.get(item)));
|
||||
}
|
||||
|
||||
getSelectedElements() {
|
||||
return this.list.getSelectedElements()
|
||||
.map(e => e.item);
|
||||
}
|
||||
|
||||
setSelectedElements(items: IQuickPickItem[]) {
|
||||
this.list.setSelection(items
|
||||
.filter(item => this.elementsToIndexes.has(item))
|
||||
.map(item => this.elementsToIndexes.get(item)));
|
||||
}
|
||||
|
||||
getCheckedElements() {
|
||||
@@ -271,7 +329,38 @@ export class QuickInputCheckboxList {
|
||||
.map(e => e.item);
|
||||
}
|
||||
|
||||
setCheckedElements(items: IQuickPickItem[]) {
|
||||
try {
|
||||
this._fireCheckedEvents = false;
|
||||
const checked = new Set();
|
||||
for (const item of items) {
|
||||
checked.add(item);
|
||||
}
|
||||
for (const element of this.elements) {
|
||||
element.checked = checked.has(element.item);
|
||||
}
|
||||
} finally {
|
||||
this._fireCheckedEvents = true;
|
||||
this.fireCheckedEvents();
|
||||
}
|
||||
}
|
||||
|
||||
set enabled(value: boolean) {
|
||||
this.list.getHTMLElement().style.pointerEvents = value ? null : 'none';
|
||||
}
|
||||
|
||||
focus(what: 'First' | 'Last' | 'Next' | 'Previous' | 'NextPage' | 'PreviousPage'): void {
|
||||
if (!this.list.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((what === 'Next' || what === 'NextPage') && this.list.getFocus()[0] === this.list.length - 1) {
|
||||
what = 'First';
|
||||
}
|
||||
if ((what === 'Previous' || what === 'PreviousPage') && this.list.getFocus()[0] === 0) {
|
||||
what = 'Last';
|
||||
}
|
||||
|
||||
this.list['focus' + what]();
|
||||
this.list.reveal(this.list.getFocus()[0]);
|
||||
}
|
||||
@@ -322,20 +411,27 @@ export class QuickInputCheckboxList {
|
||||
});
|
||||
}
|
||||
|
||||
const shownElements = this.elements.filter(element => !element.hidden);
|
||||
|
||||
// Sort by value
|
||||
const normalizedSearchValue = query.toLowerCase();
|
||||
this.elements.sort((a, b) => {
|
||||
shownElements.sort((a, b) => {
|
||||
if (!query) {
|
||||
return a.index - b.index; // restore natural order
|
||||
}
|
||||
return compareEntries(a, b, normalizedSearchValue);
|
||||
});
|
||||
|
||||
this.list.splice(0, this.list.length, this.elements.filter(element => !element.hidden));
|
||||
this.elementsToIndexes = shownElements.reduce((map, element, index) => {
|
||||
map.set(element.item, index);
|
||||
return map;
|
||||
}, new Map<IQuickPickItem, number>());
|
||||
this.list.splice(0, this.list.length, shownElements);
|
||||
this.list.setFocus([]);
|
||||
this.list.layout();
|
||||
|
||||
this._onAllVisibleCheckedChanged.fire(this.getAllVisibleChecked());
|
||||
this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked());
|
||||
this._onChangedVisibleCount.fire(shownElements.length);
|
||||
}
|
||||
|
||||
toggleCheckbox() {
|
||||
@@ -353,7 +449,7 @@ export class QuickInputCheckboxList {
|
||||
}
|
||||
|
||||
display(display: boolean) {
|
||||
this.container.style.display = display ? null : 'none';
|
||||
this.container.style.display = display ? '' : 'none';
|
||||
}
|
||||
|
||||
isDisplayed() {
|
||||
@@ -367,13 +463,14 @@ export class QuickInputCheckboxList {
|
||||
|
||||
private fireCheckedEvents() {
|
||||
if (this._fireCheckedEvents) {
|
||||
this._onAllVisibleCheckedChanged.fire(this.getAllVisibleChecked());
|
||||
this._onCheckedCountChanged.fire(this.getCheckedCount());
|
||||
this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked());
|
||||
this._onChangedCheckedCount.fire(this.getCheckedCount());
|
||||
this._onChangedCheckedElements.fire(this.getCheckedElements());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function compareEntries(elementA: CheckableElement, elementB: CheckableElement, lookFor: string): number {
|
||||
function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: string): number {
|
||||
|
||||
const labelHighlightsA = elementA.labelHighlights || [];
|
||||
const labelHighlightsB = elementB.labelHighlights || [];
|
||||
@@ -386,4 +483,13 @@ function compareEntries(elementA: CheckableElement, elementB: CheckableElement,
|
||||
}
|
||||
|
||||
return compareAnything(elementA.item.label, elementB.item.label, lookFor);
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
// Override inactive focus background with active focus background for single-pick case.
|
||||
const listInactiveFocusBackground = theme.getColor(listFocusBackground);
|
||||
if (listInactiveFocusBackground) {
|
||||
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { background-color: ${listInactiveFocusBackground}; }`);
|
||||
collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused:hover { background-color: ${listInactiveFocusBackground}; }`);
|
||||
}
|
||||
});
|
||||
@@ -21,24 +21,21 @@ import { Mode, IEntryRunContext, IAutoFocus, IQuickNavigateConfiguration, IModel
|
||||
import { QuickOpenEntry, QuickOpenModel, QuickOpenEntryGroup, compareEntries, QuickOpenItemAccessorClass } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { QuickOpenWidget, HideReason } from 'vs/base/parts/quickopen/browser/quickOpenWidget';
|
||||
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
|
||||
import * as labels from 'vs/base/common/labels';
|
||||
import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IResourceInput, IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { IResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { getIconClasses } from 'vs/workbench/browser/labels';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, IWorkbenchEditorConfiguration, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { Component } from 'vs/workbench/common/component';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { QuickOpenHandler, QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions, EditorQuickOpenEntry, CLOSE_ON_FOCUS_LOST_CONFIG } from 'vs/workbench/browser/quickopen';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IPickOpenEntry, IFilePickOpenEntry, IInputOptions, IQuickOpenService, IPickOptions, IShowOptions, IPickOpenItem } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IPickOpenEntry, IFilePickOpenEntry, IQuickOpenService, IShowOptions, IPickOpenItem, IStringPickOptions, ITypedPickOptions } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
@@ -56,6 +53,9 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Dimension, addClass } from 'vs/base/browser/dom';
|
||||
import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay';
|
||||
|
||||
const HELP_PREFIX = '?';
|
||||
|
||||
@@ -72,37 +72,38 @@ interface IInternalPickOptions {
|
||||
ignoreFocusLost?: boolean;
|
||||
quickNavigateConfiguration?: IQuickNavigateConfiguration;
|
||||
onDidType?: (value: string) => any;
|
||||
onDidFocus?: (item: any) => void;
|
||||
}
|
||||
|
||||
export class QuickOpenController extends Component implements IQuickOpenService {
|
||||
|
||||
private static readonly MAX_SHORT_RESPONSE_TIME = 500;
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
private static readonly ID = 'workbench.component.quickopen';
|
||||
|
||||
private readonly _onShow: Emitter<void>;
|
||||
private readonly _onHide: Emitter<void>;
|
||||
_serviceBrand: any;
|
||||
|
||||
private readonly _onShow: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onShow(): Event<void> { return this._onShow.event; }
|
||||
|
||||
private readonly _onHide: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onHide(): Event<void> { return this._onHide.event; }
|
||||
|
||||
private quickOpenWidget: QuickOpenWidget;
|
||||
private pickOpenWidget: QuickOpenWidget;
|
||||
private layoutDimensions: Dimension;
|
||||
private mapResolvedHandlersToPrefix: { [prefix: string]: TPromise<QuickOpenHandler>; };
|
||||
private mapContextKeyToContext: { [id: string]: IContextKey<boolean>; };
|
||||
private handlerOnOpenCalled: { [prefix: string]: boolean; };
|
||||
private mapResolvedHandlersToPrefix: { [prefix: string]: TPromise<QuickOpenHandler>; } = Object.create(null);
|
||||
private mapContextKeyToContext: { [id: string]: IContextKey<boolean>; } = Object.create(null);
|
||||
private handlerOnOpenCalled: { [prefix: string]: boolean; } = Object.create(null);
|
||||
private currentResultToken: string;
|
||||
private currentPickerToken: string;
|
||||
private inQuickOpenMode: IContextKey<boolean>;
|
||||
private promisesToCompleteOnHide: ValueCallback[];
|
||||
private promisesToCompleteOnHide: ValueCallback[] = [];
|
||||
private previousActiveHandlerDescriptor: QuickOpenHandlerDescriptor;
|
||||
private actionProvider = new ContributableActionProvider();
|
||||
private visibilityChangeTimeoutHandle: number;
|
||||
private closeOnFocusLost: boolean;
|
||||
private editorHistoryHandler: EditorHistoryHandler;
|
||||
|
||||
constructor(
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IEditorGroupsService private editorGroupService: IEditorGroupsService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@@ -113,28 +114,17 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
) {
|
||||
super(QuickOpenController.ID, themeService);
|
||||
|
||||
this.mapResolvedHandlersToPrefix = {};
|
||||
this.handlerOnOpenCalled = {};
|
||||
this.mapContextKeyToContext = {};
|
||||
|
||||
this.promisesToCompleteOnHide = [];
|
||||
|
||||
this.editorHistoryHandler = this.instantiationService.createInstance(EditorHistoryHandler);
|
||||
|
||||
this.inQuickOpenMode = new RawContextKey<boolean>('inQuickOpen', false).bindTo(contextKeyService);
|
||||
|
||||
this._onShow = new Emitter<void>();
|
||||
this._onHide = new Emitter<void>();
|
||||
|
||||
this.updateConfiguration();
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration()));
|
||||
this.toUnbind.push(this.partService.onTitleBarVisibilityChange(() => this.positionQuickOpenWidget()));
|
||||
this.toUnbind.push(browser.onDidChangeZoomLevel(() => this.positionQuickOpenWidget()));
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration()));
|
||||
this._register(this.partService.onTitleBarVisibilityChange(() => this.positionQuickOpenWidget()));
|
||||
this._register(browser.onDidChangeZoomLevel(() => this.positionQuickOpenWidget()));
|
||||
}
|
||||
|
||||
private updateConfiguration(): void {
|
||||
@@ -145,15 +135,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
}
|
||||
|
||||
public get onShow(): Event<void> {
|
||||
return this._onShow.event;
|
||||
}
|
||||
|
||||
public get onHide(): Event<void> {
|
||||
return this._onHide.event;
|
||||
}
|
||||
|
||||
public navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void {
|
||||
navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void {
|
||||
if (this.quickOpenWidget) {
|
||||
this.quickOpenWidget.navigate(next, quickNavigate);
|
||||
}
|
||||
@@ -163,91 +145,11 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
}
|
||||
|
||||
public input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): TPromise<string> {
|
||||
if (this.pickOpenWidget && this.pickOpenWidget.isVisible()) {
|
||||
this.pickOpenWidget.hide(HideReason.CANCELED);
|
||||
}
|
||||
|
||||
const defaultMessage = options.prompt
|
||||
? nls.localize('inputModeEntryDescription', "{0} (Press 'Enter' to confirm or 'Escape' to cancel)", options.prompt)
|
||||
: nls.localize('inputModeEntry', "Press 'Enter' to confirm your input or 'Escape' to cancel");
|
||||
|
||||
let currentPick = defaultMessage;
|
||||
let currentValidation: TPromise<boolean>;
|
||||
let currentDecoration: Severity;
|
||||
let lastValue: string;
|
||||
|
||||
const init = (resolve: (value: IPickOpenEntry | TPromise<IPickOpenEntry>) => any, reject: (value: any) => any) => {
|
||||
|
||||
// open quick pick with just one choice. we will recurse whenever
|
||||
// the validation/success message changes
|
||||
this.doPick(TPromise.as([{ label: currentPick, tooltip: currentPick /* make sure message/validation can be read through the hover */ }]), {
|
||||
ignoreFocusLost: options.ignoreFocusLost,
|
||||
autoFocus: { autoFocusFirstEntry: true },
|
||||
password: options.password,
|
||||
placeHolder: options.placeHolder,
|
||||
value: lastValue === void 0 ? options.value : lastValue,
|
||||
valueSelection: options.valueSelection,
|
||||
inputDecoration: currentDecoration,
|
||||
onDidType: (value) => {
|
||||
if (lastValue !== value) {
|
||||
|
||||
lastValue = value;
|
||||
|
||||
if (options.validateInput) {
|
||||
if (currentValidation) {
|
||||
currentValidation.cancel();
|
||||
}
|
||||
|
||||
currentValidation = TPromise.timeout(100).then(() => {
|
||||
return options.validateInput(value).then(message => {
|
||||
currentDecoration = !!message ? Severity.Error : void 0;
|
||||
const newPick = message || defaultMessage;
|
||||
if (newPick !== currentPick) {
|
||||
options.valueSelection = null;
|
||||
currentPick = newPick;
|
||||
resolve(new TPromise<any>(init));
|
||||
}
|
||||
|
||||
return !message;
|
||||
});
|
||||
}, err => {
|
||||
// ignore
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, token).then(resolve, reject);
|
||||
};
|
||||
|
||||
return new TPromise(init).then(item => {
|
||||
|
||||
if (!currentValidation) {
|
||||
if (options.validateInput) {
|
||||
currentValidation = options
|
||||
.validateInput(lastValue === void 0 ? options.value : lastValue)
|
||||
.then(message => !message);
|
||||
} else {
|
||||
currentValidation = TPromise.as(true);
|
||||
}
|
||||
}
|
||||
|
||||
return currentValidation.then(valid => {
|
||||
if (valid && item) {
|
||||
return lastValue === void 0 ? (options.value || '') : lastValue;
|
||||
}
|
||||
|
||||
return void 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public pick(picks: TPromise<string[]>, options?: IPickOptions, token?: CancellationToken): TPromise<string>;
|
||||
public pick<T extends IPickOpenEntry>(picks: TPromise<T[]>, options?: IPickOptions, token?: CancellationToken): TPromise<string>;
|
||||
public pick(picks: string[], options?: IPickOptions, token?: CancellationToken): TPromise<string>;
|
||||
public pick<T extends IPickOpenEntry>(picks: T[], options?: IPickOptions, token?: CancellationToken): TPromise<T>;
|
||||
public pick(arg1: string[] | TPromise<string[]> | IPickOpenEntry[] | TPromise<IPickOpenEntry[]>, options?: IPickOptions, token?: CancellationToken): TPromise<string | IPickOpenEntry> {
|
||||
pick(picks: TPromise<string[]>, options?: IStringPickOptions, token?: CancellationToken): TPromise<string>;
|
||||
pick<T extends IPickOpenEntry>(picks: TPromise<T[]>, options?: ITypedPickOptions<T>, token?: CancellationToken): TPromise<string>;
|
||||
pick(picks: string[], options?: IStringPickOptions, token?: CancellationToken): TPromise<string>;
|
||||
pick<T extends IPickOpenEntry>(picks: T[], options?: ITypedPickOptions<T>, token?: CancellationToken): TPromise<T>;
|
||||
pick(arg1: string[] | TPromise<string[]> | IPickOpenEntry[] | TPromise<IPickOpenEntry[]>, options?: IStringPickOptions | ITypedPickOptions<IPickOpenEntry>, token?: CancellationToken): TPromise<string | IPickOpenEntry> {
|
||||
if (!options) {
|
||||
options = Object.create(null);
|
||||
}
|
||||
@@ -278,13 +180,13 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
this.pickOpenWidget.hide(HideReason.CANCELED);
|
||||
}
|
||||
|
||||
return new TPromise<string | IPickOpenEntry>((resolve, reject, progress) => {
|
||||
return new TPromise<string | IPickOpenEntry>((resolve, reject) => {
|
||||
|
||||
function onItem(item: IPickOpenEntry): string | IPickOpenEntry {
|
||||
return item && isAboutStrings ? item.label : item;
|
||||
}
|
||||
|
||||
this.doPick(entryPromise, options, token).then(item => resolve(onItem(item)), err => reject(err), item => progress(onItem(item)));
|
||||
this.doPick(entryPromise, options, token).then(item => resolve(onItem(item)), err => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -300,7 +202,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
// Create upon first open
|
||||
if (!this.pickOpenWidget) {
|
||||
this.pickOpenWidget = new QuickOpenWidget(
|
||||
this.pickOpenWidget = this._register(new QuickOpenWidget(
|
||||
document.getElementById(this.partService.getWorkbenchElementId()),
|
||||
{
|
||||
onOk: () => { /* ignore, handle later */ },
|
||||
@@ -313,8 +215,8 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
keyboardSupport: false,
|
||||
treeCreator: (container, config, opts) => this.instantiationService.createInstance(WorkbenchTree, container, config, opts)
|
||||
}
|
||||
);
|
||||
this.toUnbind.push(attachQuickOpenStyler(this.pickOpenWidget, this.themeService, { background: SIDE_BAR_BACKGROUND, foreground: SIDE_BAR_FOREGROUND }));
|
||||
));
|
||||
this._register(attachQuickOpenStyler(this.pickOpenWidget, this.themeService, { background: SIDE_BAR_BACKGROUND, foreground: SIDE_BAR_FOREGROUND }));
|
||||
|
||||
const pickOpenContainer = this.pickOpenWidget.create();
|
||||
addClass(pickOpenContainer, 'show-file-icons');
|
||||
@@ -346,7 +248,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
this.pickOpenWidget.layout(this.layoutDimensions);
|
||||
}
|
||||
|
||||
return new TPromise<IPickOpenEntry>((complete, error, progress) => {
|
||||
return new TPromise<IPickOpenEntry>((complete, error) => {
|
||||
|
||||
// Detect cancellation while pick promise is loading
|
||||
this.pickOpenWidget.setCallbacks({
|
||||
@@ -377,7 +279,14 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
// Model
|
||||
const model = new QuickOpenModel([], new PickOpenActionProvider());
|
||||
const entries = picks.map((e, index) => this.instantiationService.createInstance(PickOpenEntry, e, index, () => progress(e), () => this.pickOpenWidget.refresh()));
|
||||
const entries = picks.map((e, index) => {
|
||||
const onPreview = () => {
|
||||
if (options.onDidFocus) {
|
||||
options.onDidFocus(e);
|
||||
}
|
||||
};
|
||||
return this.instantiationService.createInstance(PickOpenEntry, e, index, onPreview, () => this.pickOpenWidget.refresh());
|
||||
});
|
||||
if (picks.length === 0) {
|
||||
entries.push(this.instantiationService.createInstance(PickOpenEntry, { label: nls.localize('emptyPicks', "There are no entries to pick from") }, 0, null, null));
|
||||
}
|
||||
@@ -497,7 +406,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
});
|
||||
}
|
||||
|
||||
public accept(): void {
|
||||
accept(): void {
|
||||
[this.quickOpenWidget, this.pickOpenWidget].forEach(w => {
|
||||
if (w && w.isVisible()) {
|
||||
w.accept();
|
||||
@@ -505,7 +414,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
});
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
focus(): void {
|
||||
[this.quickOpenWidget, this.pickOpenWidget].forEach(w => {
|
||||
if (w && w.isVisible()) {
|
||||
w.focus();
|
||||
@@ -513,7 +422,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
});
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
close(): void {
|
||||
[this.quickOpenWidget, this.pickOpenWidget].forEach(w => {
|
||||
if (w && w.isVisible()) {
|
||||
w.hide(HideReason.CANCELED);
|
||||
@@ -522,22 +431,14 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
|
||||
private emitQuickOpenVisibilityChange(isVisible: boolean): void {
|
||||
if (this.visibilityChangeTimeoutHandle) {
|
||||
window.clearTimeout(this.visibilityChangeTimeoutHandle);
|
||||
if (isVisible) {
|
||||
this._onShow.fire();
|
||||
} else {
|
||||
this._onHide.fire();
|
||||
}
|
||||
|
||||
this.visibilityChangeTimeoutHandle = setTimeout(() => {
|
||||
if (isVisible) {
|
||||
this._onShow.fire();
|
||||
} else {
|
||||
this._onHide.fire();
|
||||
}
|
||||
|
||||
this.visibilityChangeTimeoutHandle = void 0;
|
||||
}, 100 /* to prevent flashing, we accumulate visibility changes over a timeout of 100ms */);
|
||||
}
|
||||
|
||||
public show(prefix?: string, options?: IShowOptions): TPromise<void> {
|
||||
show(prefix?: string, options?: IShowOptions): TPromise<void> {
|
||||
let quickNavigateConfiguration = options ? options.quickNavigateConfiguration : void 0;
|
||||
let inputSelection = options ? options.inputSelection : void 0;
|
||||
let autoFocus = options ? options.autoFocus : void 0;
|
||||
@@ -555,7 +456,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
// Create upon first open
|
||||
if (!this.quickOpenWidget) {
|
||||
this.quickOpenWidget = new QuickOpenWidget(
|
||||
this.quickOpenWidget = this._register(new QuickOpenWidget(
|
||||
document.getElementById(this.partService.getWorkbenchElementId()),
|
||||
{
|
||||
onOk: () => { /* ignore */ },
|
||||
@@ -569,8 +470,8 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
keyboardSupport: false,
|
||||
treeCreator: (container, config, opts) => this.instantiationService.createInstance(WorkbenchTree, container, config, opts)
|
||||
}
|
||||
);
|
||||
this.toUnbind.push(attachQuickOpenStyler(this.quickOpenWidget, this.themeService, { background: SIDE_BAR_BACKGROUND, foreground: SIDE_BAR_FOREGROUND }));
|
||||
));
|
||||
this._register(attachQuickOpenStyler(this.quickOpenWidget, this.themeService, { background: SIDE_BAR_BACKGROUND, foreground: SIDE_BAR_FOREGROUND }));
|
||||
|
||||
const quickOpenContainer = this.quickOpenWidget.create();
|
||||
addClass(quickOpenContainer, 'show-file-icons');
|
||||
@@ -597,8 +498,8 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
if (!quickNavigateConfiguration) {
|
||||
autoFocus = { autoFocusFirstEntry: true };
|
||||
} else {
|
||||
const visibleEditorCount = this.editorService.getVisibleEditors().length;
|
||||
autoFocus = { autoFocusFirstEntry: visibleEditorCount === 0, autoFocusSecondEntry: visibleEditorCount !== 0 };
|
||||
const autoFocusFirstEntry = this.editorGroupService.activeGroup.count === 0;
|
||||
autoFocus = { autoFocusFirstEntry, autoFocusSecondEntry: !autoFocusFirstEntry };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,7 +538,6 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
this.pickOpenWidget.hide(HideReason.FOCUS_LOST);
|
||||
}
|
||||
|
||||
this.inQuickOpenMode.set(true);
|
||||
this.emitQuickOpenVisibilityChange(true);
|
||||
}
|
||||
|
||||
@@ -649,14 +549,12 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
// Pass to handlers
|
||||
for (let prefix in this.mapResolvedHandlersToPrefix) {
|
||||
if (this.mapResolvedHandlersToPrefix.hasOwnProperty(prefix)) {
|
||||
const promise = this.mapResolvedHandlersToPrefix[prefix];
|
||||
promise.then(handler => {
|
||||
this.handlerOnOpenCalled[prefix] = false;
|
||||
const promise = this.mapResolvedHandlersToPrefix[prefix];
|
||||
promise.then(handler => {
|
||||
this.handlerOnOpenCalled[prefix] = false;
|
||||
|
||||
handler.onClose(reason === HideReason.CANCELED); // Don't check if onOpen was called to preserve old behaviour for now
|
||||
});
|
||||
}
|
||||
handler.onClose(reason === HideReason.CANCELED); // Don't check if onOpen was called to preserve old behaviour for now
|
||||
});
|
||||
}
|
||||
|
||||
// Complete promises that are waiting
|
||||
@@ -666,11 +564,10 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
|
||||
if (reason !== HideReason.FOCUS_LOST) {
|
||||
this.restoreFocus(); // focus back to editor unless user clicked somewhere else
|
||||
this.editorGroupService.activeGroup.focus(); // focus back to editor group unless user clicked somewhere else
|
||||
}
|
||||
|
||||
// Reset context keys
|
||||
this.inQuickOpenMode.reset();
|
||||
this.resetQuickOpenContextKeys();
|
||||
|
||||
// Events
|
||||
@@ -717,15 +614,6 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
return new QuickOpenModel(entries, this.actionProvider);
|
||||
}
|
||||
|
||||
private restoreFocus(): void {
|
||||
|
||||
// Try to focus active editor
|
||||
const editor = this.editorService.getActiveEditor();
|
||||
if (editor) {
|
||||
editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
private onType(value: string): void {
|
||||
|
||||
// look for a handler
|
||||
@@ -981,7 +869,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
return this.mapResolvedHandlersToPrefix[id] = TPromise.as(handler.instantiate(this.instantiationService));
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
layout(dimension: Dimension): void {
|
||||
this.layoutDimensions = dimension;
|
||||
if (this.quickOpenWidget) {
|
||||
this.quickOpenWidget.layout(this.layoutDimensions);
|
||||
@@ -991,18 +879,6 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
this.pickOpenWidget.layout(this.layoutDimensions);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.quickOpenWidget) {
|
||||
this.quickOpenWidget.dispose();
|
||||
}
|
||||
|
||||
if (this.pickOpenWidget) {
|
||||
this.pickOpenWidget.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class PlaceholderQuickOpenEntry extends QuickOpenEntryGroup {
|
||||
@@ -1014,7 +890,7 @@ class PlaceholderQuickOpenEntry extends QuickOpenEntryGroup {
|
||||
this.placeHolderLabel = placeHolderLabel;
|
||||
}
|
||||
|
||||
public getLabel(): string {
|
||||
getLabel(): string {
|
||||
return this.placeHolderLabel;
|
||||
}
|
||||
}
|
||||
@@ -1063,7 +939,7 @@ class PickOpenEntry extends PlaceholderQuickOpenEntry implements IPickOpenItem {
|
||||
this.fileKind = fileItem.fileKind;
|
||||
}
|
||||
|
||||
public matchesFuzzy(query: string, options: IInternalPickOptions): { labelHighlights: IMatch[], descriptionHighlights: IMatch[], detailHighlights: IMatch[] } {
|
||||
matchesFuzzy(query: string, options: IInternalPickOptions): { labelHighlights: IMatch[], descriptionHighlights: IMatch[], detailHighlights: IMatch[] } {
|
||||
if (!this.labelOcticons) {
|
||||
this.labelOcticons = parseOcticons(this.getLabel()); // parse on demand
|
||||
}
|
||||
@@ -1080,72 +956,72 @@ class PickOpenEntry extends PlaceholderQuickOpenEntry implements IPickOpenItem {
|
||||
};
|
||||
}
|
||||
|
||||
public getPayload(): any {
|
||||
getPayload(): any {
|
||||
return this.payload;
|
||||
}
|
||||
|
||||
public remove(): void {
|
||||
remove(): void {
|
||||
super.setHidden(true);
|
||||
this.removed = true;
|
||||
|
||||
this.onRemove();
|
||||
}
|
||||
|
||||
public isHidden(): boolean {
|
||||
isHidden(): boolean {
|
||||
return this.removed || super.isHidden();
|
||||
}
|
||||
|
||||
public get action(): IAction {
|
||||
get action(): IAction {
|
||||
return this._action;
|
||||
}
|
||||
|
||||
public get index(): number {
|
||||
get index(): number {
|
||||
return this._index;
|
||||
}
|
||||
|
||||
public getLabelOptions(): IIconLabelValueOptions {
|
||||
getLabelOptions(): IIconLabelValueOptions {
|
||||
return {
|
||||
extraClasses: this.resource ? getIconClasses(this.modelService, this.modeService, this.resource, this.fileKind) : []
|
||||
};
|
||||
}
|
||||
|
||||
public get shouldRunWithContext(): IEntryRunContext {
|
||||
get shouldRunWithContext(): IEntryRunContext {
|
||||
return this._shouldRunWithContext;
|
||||
}
|
||||
|
||||
public getDescription(): string {
|
||||
getDescription(): string {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public getDetail(): string {
|
||||
getDetail(): string {
|
||||
return this.detail;
|
||||
}
|
||||
|
||||
public getTooltip(): string {
|
||||
getTooltip(): string {
|
||||
return this.tooltip;
|
||||
}
|
||||
|
||||
public getDescriptionTooltip(): string {
|
||||
getDescriptionTooltip(): string {
|
||||
return this.descriptionTooltip;
|
||||
}
|
||||
|
||||
public showBorder(): boolean {
|
||||
showBorder(): boolean {
|
||||
return this.hasSeparator;
|
||||
}
|
||||
|
||||
public getGroupLabel(): string {
|
||||
getGroupLabel(): string {
|
||||
return this.separatorLabel;
|
||||
}
|
||||
|
||||
public shouldAlwaysShow(): boolean {
|
||||
shouldAlwaysShow(): boolean {
|
||||
return this.alwaysShow;
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
public run(mode: Mode, context: IEntryRunContext): boolean {
|
||||
run(mode: Mode, context: IEntryRunContext): boolean {
|
||||
if (mode === Mode.OPEN) {
|
||||
this._shouldRunWithContext = context;
|
||||
|
||||
@@ -1161,23 +1037,23 @@ class PickOpenEntry extends PlaceholderQuickOpenEntry implements IPickOpenItem {
|
||||
}
|
||||
|
||||
class PickOpenActionProvider implements IActionProvider {
|
||||
public hasActions(tree: ITree, element: PickOpenEntry): boolean {
|
||||
hasActions(tree: ITree, element: PickOpenEntry): boolean {
|
||||
return !!element.action;
|
||||
}
|
||||
|
||||
public getActions(tree: ITree, element: PickOpenEntry): TPromise<IAction[]> {
|
||||
getActions(tree: ITree, element: PickOpenEntry): TPromise<IAction[]> {
|
||||
return TPromise.as(element.action ? [element.action] : []);
|
||||
}
|
||||
|
||||
public hasSecondaryActions(tree: ITree, element: PickOpenEntry): boolean {
|
||||
hasSecondaryActions(tree: ITree, element: PickOpenEntry): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public getSecondaryActions(tree: ITree, element: PickOpenEntry): TPromise<IAction[]> {
|
||||
getSecondaryActions(tree: ITree, element: PickOpenEntry): TPromise<IAction[]> {
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
public getActionItem(tree: ITree, element: PickOpenEntry, action: Action): BaseActionItem {
|
||||
getActionItem(tree: ITree, element: PickOpenEntry, action: Action): BaseActionItem {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1193,7 +1069,7 @@ class EditorHistoryHandler {
|
||||
this.scorerCache = Object.create(null);
|
||||
}
|
||||
|
||||
public getResults(searchValue?: string): QuickOpenEntry[] {
|
||||
getResults(searchValue?: string): QuickOpenEntry[] {
|
||||
|
||||
// Massage search for scoring
|
||||
const query = prepareQuery(searchValue);
|
||||
@@ -1248,7 +1124,7 @@ class EditorHistoryItemAccessorClass extends QuickOpenItemAccessorClass {
|
||||
super();
|
||||
}
|
||||
|
||||
public getItemDescription(entry: QuickOpenEntry): string {
|
||||
getItemDescription(entry: QuickOpenEntry): string {
|
||||
return this.allowMatchOnDescription ? entry.getDescription() : void 0;
|
||||
}
|
||||
}
|
||||
@@ -1269,13 +1145,12 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
|
||||
|
||||
constructor(
|
||||
input: IEditorInput | IResourceInput,
|
||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IModeService private modeService: IModeService,
|
||||
@IModelService private modelService: IModelService,
|
||||
@ITextFileService private textFileService: ITextFileService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IUriDisplayService uriDisplayService: IUriDisplayService,
|
||||
@IFileService fileService: IFileService
|
||||
) {
|
||||
super(editorService);
|
||||
@@ -1290,8 +1165,8 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
|
||||
} else {
|
||||
const resourceInput = input as IResourceInput;
|
||||
this.resource = resourceInput.resource;
|
||||
this.label = labels.getBaseLabel(resourceInput.resource);
|
||||
this.description = labels.getPathLabel(resources.dirname(this.resource), contextService, environmentService);
|
||||
this.label = resources.basenameOrAuthority(resourceInput.resource);
|
||||
this.description = uriDisplayService.getLabel(resources.dirname(this.resource), true);
|
||||
this.dirty = this.resource && this.textFileService.isDirty(this.resource);
|
||||
|
||||
if (this.dirty && this.textFileService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) {
|
||||
@@ -1300,45 +1175,45 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
|
||||
}
|
||||
}
|
||||
|
||||
public getIcon(): string {
|
||||
getIcon(): string {
|
||||
return this.dirty ? 'dirty' : '';
|
||||
}
|
||||
|
||||
public getLabel(): string {
|
||||
getLabel(): string {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
public getLabelOptions(): IIconLabelValueOptions {
|
||||
getLabelOptions(): IIconLabelValueOptions {
|
||||
return {
|
||||
extraClasses: getIconClasses(this.modelService, this.modeService, this.resource)
|
||||
};
|
||||
}
|
||||
|
||||
public getAriaLabel(): string {
|
||||
getAriaLabel(): string {
|
||||
return nls.localize('entryAriaLabel', "{0}, recently opened", this.getLabel());
|
||||
}
|
||||
|
||||
public getDescription(): string {
|
||||
getDescription(): string {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
public getInput(): IEditorInput | IResourceInput {
|
||||
getInput(): IEditorInput | IResourceInput {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
public run(mode: Mode, context: IEntryRunContext): boolean {
|
||||
run(mode: Mode, context: IEntryRunContext): boolean {
|
||||
if (mode === Mode.OPEN) {
|
||||
const sideBySide = !context.quickNavigateConfiguration && (context.keymods.alt || context.keymods.ctrlCmd);
|
||||
const pinned = !this.configurationService.getValue<IWorkbenchEditorConfiguration>().workbench.editor.enablePreviewFromQuickOpen || context.keymods.alt;
|
||||
|
||||
if (this.input instanceof EditorInput) {
|
||||
this.editorService.openEditor(this.input, { pinned }, sideBySide).done(null, errors.onUnexpectedError);
|
||||
this.editorService.openEditor(this.input, { pinned }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
} else {
|
||||
this.editorService.openEditor({ resource: (this.input as IResourceInput).resource, options: { pinned } }, sideBySide);
|
||||
this.editorService.openEditor({ resource: (this.input as IResourceInput).resource, options: { pinned } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1362,8 +1237,8 @@ function resourceForEditorHistory(input: EditorInput, fileService: IFileService)
|
||||
|
||||
export class RemoveFromEditorHistoryAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.removeFromEditorHistory';
|
||||
public static readonly LABEL = nls.localize('removeFromEditorHistory', "Remove From History");
|
||||
static readonly ID = 'workbench.action.removeFromEditorHistory';
|
||||
static readonly LABEL = nls.localize('removeFromEditorHistory', "Remove From History");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -1375,7 +1250,7 @@ export class RemoveFromEditorHistoryAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
run(): TPromise<any> {
|
||||
interface IHistoryPickEntry extends IFilePickOpenEntry {
|
||||
input: IEditorInput | IResourceInput;
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { RemoveFromEditorHistoryAction } from 'vs/workbench/browser/parts/quickopen/quickOpenController';
|
||||
import { QuickOpenSelectNextAction, QuickOpenSelectPreviousAction, inQuickOpenContext, getQuickNavigateHandler, QuickOpenNavigateNextAction, QuickOpenNavigatePreviousAction, defaultQuickOpenContext, QUICKOPEN_ACTION_ID, QUICKOPEN_ACION_LABEL } from 'vs/workbench/browser/parts/quickopen/quickopen';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.closeQuickOpen',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: inQuickOpenContext,
|
||||
primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape],
|
||||
handler: accessor => {
|
||||
@@ -29,7 +29,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.acceptSelectedQuickOpenItem',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: inQuickOpenContext,
|
||||
primary: null,
|
||||
handler: accessor => {
|
||||
@@ -42,7 +42,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.focusQuickOpen',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: inQuickOpenContext,
|
||||
primary: null,
|
||||
handler: accessor => {
|
||||
@@ -59,7 +59,7 @@ const globalQuickOpenKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, sec
|
||||
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: QUICKOPEN_ACTION_ID,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: globalQuickOpenKeybinding.primary,
|
||||
secondary: globalQuickOpenKeybinding.secondary,
|
||||
@@ -70,8 +70,8 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: { id: QUICKOPEN_ACTION_ID, title: QUICKOPEN_ACION_LABEL }
|
||||
});
|
||||
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectNextAction, QuickOpenSelectNextAction.ID, QuickOpenSelectNextAction.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } }, inQuickOpenContext, KeybindingsRegistry.WEIGHT.workbenchContrib(50)), 'Select Next in Quick Open');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectPreviousAction, QuickOpenSelectPreviousAction.ID, QuickOpenSelectPreviousAction.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } }, inQuickOpenContext, KeybindingsRegistry.WEIGHT.workbenchContrib(50)), 'Select Previous in Quick Open');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectNextAction, QuickOpenSelectNextAction.ID, QuickOpenSelectNextAction.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Next in Quick Open');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectPreviousAction, QuickOpenSelectPreviousAction.ID, QuickOpenSelectPreviousAction.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Previous in Quick Open');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenNavigateNextAction, QuickOpenNavigateNextAction.ID, QuickOpenNavigateNextAction.LABEL), 'Navigate Next in Quick Open');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenNavigatePreviousAction, QuickOpenNavigatePreviousAction.ID, QuickOpenNavigatePreviousAction.LABEL), 'Navigate Previous in Quick Open');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveFromEditorHistoryAction, RemoveFromEditorHistoryAction.ID, RemoveFromEditorHistoryAction.LABEL), 'Remove From History');
|
||||
@@ -79,7 +79,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveFromEditorHistor
|
||||
const quickOpenNavigateNextInFilePickerId = 'workbench.action.quickOpenNavigateNextInFilePicker';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: quickOpenNavigateNextInFilePickerId,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
handler: getQuickNavigateHandler(quickOpenNavigateNextInFilePickerId, true),
|
||||
when: defaultQuickOpenContext,
|
||||
primary: globalQuickOpenKeybinding.primary,
|
||||
@@ -90,7 +90,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
const quickOpenNavigatePreviousInFilePickerId = 'workbench.action.quickOpenNavigatePreviousInFilePicker';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: quickOpenNavigatePreviousInFilePickerId,
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
handler: getQuickNavigateHandler(quickOpenNavigatePreviousInFilePickerId, false),
|
||||
when: defaultQuickOpenContext,
|
||||
primary: globalQuickOpenKeybinding.primary | KeyMod.Shift,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
@@ -46,16 +47,18 @@ export class BaseQuickOpenNavigateAction extends Action {
|
||||
private next: boolean,
|
||||
private quickNavigate: boolean,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService,
|
||||
@IQuickInputService private quickInputService: IQuickInputService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(event?: any): TPromise<any> {
|
||||
run(event?: any): TPromise<any> {
|
||||
const keys = this.keybindingService.lookupKeybindings(this.id);
|
||||
const quickNavigate = this.quickNavigate ? { keybindings: keys } : void 0;
|
||||
|
||||
this.quickOpenService.navigate(this.next, quickNavigate);
|
||||
this.quickInputService.navigate(this.next, quickNavigate);
|
||||
|
||||
return TPromise.as(true);
|
||||
}
|
||||
@@ -65,70 +68,76 @@ export function getQuickNavigateHandler(id: string, next?: boolean): ICommandHan
|
||||
return accessor => {
|
||||
const keybindingService = accessor.get(IKeybindingService);
|
||||
const quickOpenService = accessor.get(IQuickOpenService);
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
|
||||
const keys = keybindingService.lookupKeybindings(id);
|
||||
const quickNavigate = { keybindings: keys };
|
||||
|
||||
quickOpenService.navigate(next, quickNavigate);
|
||||
quickInputService.navigate(next, quickNavigate);
|
||||
};
|
||||
}
|
||||
|
||||
export class QuickOpenNavigateNextAction extends BaseQuickOpenNavigateAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.quickOpenNavigateNext';
|
||||
public static readonly LABEL = nls.localize('quickNavigateNext', "Navigate Next in Quick Open");
|
||||
static readonly ID = 'workbench.action.quickOpenNavigateNext';
|
||||
static readonly LABEL = nls.localize('quickNavigateNext', "Navigate Next in Quick Open");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickOpenService quickOpenService: IQuickOpenService,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label, true, true, quickOpenService, keybindingService);
|
||||
super(id, label, true, true, quickOpenService, quickInputService, keybindingService);
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickOpenNavigatePreviousAction extends BaseQuickOpenNavigateAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.quickOpenNavigatePrevious';
|
||||
public static readonly LABEL = nls.localize('quickNavigatePrevious', "Navigate Previous in Quick Open");
|
||||
static readonly ID = 'workbench.action.quickOpenNavigatePrevious';
|
||||
static readonly LABEL = nls.localize('quickNavigatePrevious', "Navigate Previous in Quick Open");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickOpenService quickOpenService: IQuickOpenService,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label, false, true, quickOpenService, keybindingService);
|
||||
super(id, label, false, true, quickOpenService, quickInputService, keybindingService);
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickOpenSelectNextAction extends BaseQuickOpenNavigateAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.quickOpenSelectNext';
|
||||
public static readonly LABEL = nls.localize('quickSelectNext', "Select Next in Quick Open");
|
||||
static readonly ID = 'workbench.action.quickOpenSelectNext';
|
||||
static readonly LABEL = nls.localize('quickSelectNext', "Select Next in Quick Open");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickOpenService quickOpenService: IQuickOpenService,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label, true, false, quickOpenService, keybindingService);
|
||||
super(id, label, true, false, quickOpenService, quickInputService, keybindingService);
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickOpenSelectPreviousAction extends BaseQuickOpenNavigateAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.quickOpenSelectPrevious';
|
||||
public static readonly LABEL = nls.localize('quickSelectPrevious', "Select Previous in Quick Open");
|
||||
static readonly ID = 'workbench.action.quickOpenSelectPrevious';
|
||||
static readonly LABEL = nls.localize('quickSelectPrevious', "Select Previous in Quick Open");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickOpenService quickOpenService: IQuickOpenService,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label, false, false, quickOpenService, keybindingService);
|
||||
super(id, label, false, false, quickOpenService, quickInputService, keybindingService);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
.monaco-workbench > .sidebar > .title > .title-label span {
|
||||
.monaco-workbench > .sidebar > .title > .title-label h2 {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,7 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
|
||||
export class SidebarPart extends CompositePart<Viewlet> {
|
||||
|
||||
public static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid';
|
||||
|
||||
public _serviceBrand: any;
|
||||
static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid';
|
||||
|
||||
private blockOpeningViewlet: boolean;
|
||||
|
||||
@@ -69,22 +67,22 @@ export class SidebarPart extends CompositePart<Viewlet> {
|
||||
);
|
||||
}
|
||||
|
||||
public get onDidViewletOpen(): Event<IViewlet> {
|
||||
get onDidViewletOpen(): Event<IViewlet> {
|
||||
return this._onDidCompositeOpen.event as Event<IViewlet>;
|
||||
}
|
||||
|
||||
public get onDidViewletClose(): Event<IViewlet> {
|
||||
get onDidViewletClose(): Event<IViewlet> {
|
||||
return this._onDidCompositeClose.event as Event<IViewlet>;
|
||||
}
|
||||
|
||||
public createTitleArea(parent: HTMLElement): HTMLElement {
|
||||
createTitleArea(parent: HTMLElement): HTMLElement {
|
||||
const titleArea = super.createTitleArea(parent);
|
||||
$(titleArea).on(EventType.CONTEXT_MENU, (e: MouseEvent) => this.onTitleAreaContextMenu(new StandardMouseEvent(e)));
|
||||
$(titleArea).on(EventType.CONTEXT_MENU, (e: MouseEvent) => this.onTitleAreaContextMenu(new StandardMouseEvent(e)), this.toDispose);
|
||||
|
||||
return titleArea;
|
||||
}
|
||||
|
||||
public updateStyles(): void {
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
// Part container
|
||||
@@ -103,7 +101,7 @@ export class SidebarPart extends CompositePart<Viewlet> {
|
||||
container.style('border-left-color', !isPositionLeft ? borderColor : null);
|
||||
}
|
||||
|
||||
public openViewlet(id: string, focus?: boolean): TPromise<Viewlet> {
|
||||
openViewlet(id: string, focus?: boolean): TPromise<Viewlet> {
|
||||
if (this.blockOpeningViewlet) {
|
||||
return TPromise.as(null); // Workaround against a potential race condition
|
||||
}
|
||||
@@ -122,19 +120,19 @@ export class SidebarPart extends CompositePart<Viewlet> {
|
||||
return promise.then(() => this.openComposite(id, focus)) as TPromise<Viewlet>;
|
||||
}
|
||||
|
||||
public getActiveViewlet(): IViewlet {
|
||||
getActiveViewlet(): IViewlet {
|
||||
return <IViewlet>this.getActiveComposite();
|
||||
}
|
||||
|
||||
public getLastActiveViewletId(): string {
|
||||
getLastActiveViewletId(): string {
|
||||
return this.getLastActiveCompositetId();
|
||||
}
|
||||
|
||||
public hideActiveViewlet(): TPromise<void> {
|
||||
hideActiveViewlet(): TPromise<void> {
|
||||
return this.hideActiveComposite().then(composite => void 0);
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): Dimension[] {
|
||||
layout(dimension: Dimension): Dimension[] {
|
||||
if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
return [dimension];
|
||||
}
|
||||
@@ -162,8 +160,8 @@ export class SidebarPart extends CompositePart<Viewlet> {
|
||||
|
||||
class FocusSideBarAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.focusSideBar';
|
||||
public static readonly LABEL = nls.localize('focusSideBar', "Focus into Side Bar");
|
||||
static readonly ID = 'workbench.action.focusSideBar';
|
||||
static readonly LABEL = nls.localize('focusSideBar', "Focus into Side Bar");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -174,7 +172,7 @@ class FocusSideBarAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
run(): TPromise<any> {
|
||||
|
||||
// Show side bar
|
||||
if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
.monaco-workbench > .part.statusbar {
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
width: 100%;
|
||||
height: 22px;
|
||||
font-size: 12px;
|
||||
@@ -52,7 +53,6 @@
|
||||
}
|
||||
|
||||
.monaco-workbench > .part.statusbar > .statusbar-entry > span {
|
||||
cursor: default;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||