提交 19c999bd 编写于 作者: B Benjamin Pasero

pinned tabs - swap action for pinned tabs unless compact

上级 15e3acf6
...@@ -10,7 +10,7 @@ import * as DOM from 'vs/base/browser/dom'; ...@@ -10,7 +10,7 @@ import * as DOM from 'vs/base/browser/dom';
import * as types from 'vs/base/common/types'; import * as types from 'vs/base/common/types';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Event, Emitter } from 'vs/base/common/event'; import { Emitter } from 'vs/base/common/event';
import { IActionViewItemOptions, ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IActionViewItemOptions, ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
export const enum ActionsOrientation { export const enum ActionsOrientation {
...@@ -47,8 +47,9 @@ export class ActionBar extends Disposable implements IActionRunner { ...@@ -47,8 +47,9 @@ export class ActionBar extends Disposable implements IActionRunner {
private _actionRunner: IActionRunner; private _actionRunner: IActionRunner;
private _context: unknown; private _context: unknown;
private _orientation: ActionsOrientation; private readonly _orientation: ActionsOrientation;
private _triggerKeys: ActionTrigger; private readonly _triggerKeys: ActionTrigger;
private _actionIds: string[];
// View Items // View Items
viewItems: IActionViewItem[]; viewItems: IActionViewItem[];
...@@ -60,16 +61,16 @@ export class ActionBar extends Disposable implements IActionRunner { ...@@ -60,16 +61,16 @@ export class ActionBar extends Disposable implements IActionRunner {
protected actionsList: HTMLElement; protected actionsList: HTMLElement;
private _onDidBlur = this._register(new Emitter<void>()); private _onDidBlur = this._register(new Emitter<void>());
readonly onDidBlur: Event<void> = this._onDidBlur.event; readonly onDidBlur = this._onDidBlur.event;
private _onDidCancel = this._register(new Emitter<void>()); private _onDidCancel = this._register(new Emitter<void>());
readonly onDidCancel: Event<void> = this._onDidCancel.event; readonly onDidCancel = this._onDidCancel.event;
private _onDidRun = this._register(new Emitter<IRunEvent>()); private _onDidRun = this._register(new Emitter<IRunEvent>());
readonly onDidRun: Event<IRunEvent> = this._onDidRun.event; readonly onDidRun = this._onDidRun.event;
private _onDidBeforeRun = this._register(new Emitter<IRunEvent>()); private _onDidBeforeRun = this._register(new Emitter<IRunEvent>());
readonly onDidBeforeRun: Event<IRunEvent> = this._onDidBeforeRun.event; readonly onDidBeforeRun = this._onDidBeforeRun.event;
constructor(container: HTMLElement, options: IActionBarOptions = {}) { constructor(container: HTMLElement, options: IActionBarOptions = {}) {
super(); super();
...@@ -92,6 +93,7 @@ export class ActionBar extends Disposable implements IActionRunner { ...@@ -92,6 +93,7 @@ export class ActionBar extends Disposable implements IActionRunner {
this._register(this._actionRunner.onDidRun(e => this._onDidRun.fire(e))); this._register(this._actionRunner.onDidRun(e => this._onDidRun.fire(e)));
this._register(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e))); this._register(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e)));
this._actionIds = [];
this.viewItems = []; this.viewItems = [];
this.focusedItem = undefined; this.focusedItem = undefined;
...@@ -245,6 +247,10 @@ export class ActionBar extends Disposable implements IActionRunner { ...@@ -245,6 +247,10 @@ export class ActionBar extends Disposable implements IActionRunner {
return this.domNode; return this.domNode;
} }
hasAction(action: IAction): boolean {
return this._actionIds.includes(action.id);
}
push(arg: IAction | ReadonlyArray<IAction>, options: IActionOptions = {}): void { push(arg: IAction | ReadonlyArray<IAction>, options: IActionOptions = {}): void {
const actions: ReadonlyArray<IAction> = Array.isArray(arg) ? arg : [arg]; const actions: ReadonlyArray<IAction> = Array.isArray(arg) ? arg : [arg];
...@@ -279,9 +285,11 @@ export class ActionBar extends Disposable implements IActionRunner { ...@@ -279,9 +285,11 @@ export class ActionBar extends Disposable implements IActionRunner {
if (index === null || index < 0 || index >= this.actionsList.children.length) { if (index === null || index < 0 || index >= this.actionsList.children.length) {
this.actionsList.appendChild(actionViewItemElement); this.actionsList.appendChild(actionViewItemElement);
this.viewItems.push(item); this.viewItems.push(item);
this._actionIds.push(action.id);
} else { } else {
this.actionsList.insertBefore(actionViewItemElement, this.actionsList.children[index]); this.actionsList.insertBefore(actionViewItemElement, this.actionsList.children[index]);
this.viewItems.splice(index, 0, item); this.viewItems.splice(index, 0, item);
this._actionIds.splice(index, 0, action.id);
index++; index++;
} }
}); });
...@@ -317,12 +325,14 @@ export class ActionBar extends Disposable implements IActionRunner { ...@@ -317,12 +325,14 @@ export class ActionBar extends Disposable implements IActionRunner {
if (index >= 0 && index < this.viewItems.length) { if (index >= 0 && index < this.viewItems.length) {
this.actionsList.removeChild(this.actionsList.childNodes[index]); this.actionsList.removeChild(this.actionsList.childNodes[index]);
dispose(this.viewItems.splice(index, 1)); dispose(this.viewItems.splice(index, 1));
this._actionIds.splice(index, 1);
} }
} }
clear(): void { clear(): void {
dispose(this.viewItems); dispose(this.viewItems);
this.viewItems = []; this.viewItems = [];
this._actionIds = [];
DOM.clearNode(this.actionsList); DOM.clearNode(this.actionsList);
} }
...@@ -463,6 +473,8 @@ export class ActionBar extends Disposable implements IActionRunner { ...@@ -463,6 +473,8 @@ export class ActionBar extends Disposable implements IActionRunner {
dispose(this.viewItems); dispose(this.viewItems);
this.viewItems = []; this.viewItems = [];
this._actionIds = [];
DOM.removeNode(this.getContainer()); DOM.removeNode(this.getContainer());
super.dispose(); super.dispose();
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as assert from 'assert'; import * as assert from 'assert';
import { prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; import { ActionBar, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action, Separator } from 'vs/base/common/actions'; import { Action, Separator } from 'vs/base/common/actions';
suite('Actionbar', () => { suite('Actionbar', () => {
...@@ -24,4 +24,37 @@ suite('Actionbar', () => { ...@@ -24,4 +24,37 @@ suite('Actionbar', () => {
assert(actions[1] === a5); assert(actions[1] === a5);
assert(actions[2] === a6); assert(actions[2] === a6);
}); });
test('hasAction()', function () {
const container = document.createElement('div');
const actionbar = new ActionBar(container);
let a1 = new Action('a1');
let a2 = new Action('a2');
actionbar.push(a1);
assert.equal(actionbar.hasAction(a1), true);
assert.equal(actionbar.hasAction(a2), false);
actionbar.pull(0);
assert.equal(actionbar.hasAction(a1), false);
actionbar.push(a1, { index: 1 });
actionbar.push(a2, { index: 0 });
assert.equal(actionbar.hasAction(a1), true);
assert.equal(actionbar.hasAction(a2), true);
actionbar.pull(0);
assert.equal(actionbar.hasAction(a1), true);
assert.equal(actionbar.hasAction(a2), false);
actionbar.pull(0);
assert.equal(actionbar.hasAction(a1), false);
assert.equal(actionbar.hasAction(a2), false);
actionbar.push(a1);
assert.equal(actionbar.hasAction(a1), true);
actionbar.clear();
assert.equal(actionbar.hasAction(a1), false);
});
}); });
...@@ -10,7 +10,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la ...@@ -10,7 +10,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la
import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICommandService } from 'vs/platform/commands/common/commands';
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups } from 'vs/workbench/browser/parts/editor/editorCommands'; import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups, UNPIN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
...@@ -406,6 +406,24 @@ export class CloseEditorAction extends Action { ...@@ -406,6 +406,24 @@ export class CloseEditorAction extends Action {
} }
} }
export class UnpinEditorAction extends Action {
static readonly ID = 'workbench.action.unpinActiveEditor';
static readonly LABEL = nls.localize('unpinEditor', "Unpin Editor");
constructor(
id: string,
label: string,
@ICommandService private readonly commandService: ICommandService
) {
super(id, label, Codicon.pin.classNames);
}
run(context?: IEditorCommandsContext): Promise<void> {
return this.commandService.executeCommand(UNPIN_EDITOR_COMMAND_ID, undefined, context);
}
}
export class CloseOneEditorAction extends Action { export class CloseOneEditorAction extends Action {
static readonly ID = 'workbench.action.closeActiveEditor'; static readonly ID = 'workbench.action.closeActiveEditor';
......
...@@ -62,9 +62,9 @@ ...@@ -62,9 +62,9 @@
padding-left: 10px; padding-left: 10px;
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.close-button-right, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-right,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.close-button-off:not(.sticky-compact) { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-off:not(.sticky-compact) {
padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab close button is not left (unless sticky-compact) */ padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab actions is not left (unless sticky-compact) */
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit {
...@@ -123,19 +123,19 @@ ...@@ -123,19 +123,19 @@
position: static; position: static;
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-left .action-label { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-left .action-label {
margin-right: 4px !important; margin-right: 4px !important;
} }
.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.tab-actions-left::after,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-off::after { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off::after {
content: ''; content: '';
display: flex; display: flex;
flex: 0; 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) */ 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 { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-left {
min-width: 80px; /* make more room for close button when it shows to the 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 */ padding-right: 5px; /* we need less room when sizing is shrink */
} }
...@@ -148,7 +148,7 @@ ...@@ -148,7 +148,7 @@
pointer-events: none; /* prevents cursor flickering (fixes https://github.com/Microsoft/vscode/issues/38753) */ 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 { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-left {
flex-direction: row-reverse; flex-direction: row-reverse;
padding-left: 0; padding-left: 0;
padding-right: 10px; padding-right: 10px;
...@@ -236,57 +236,57 @@ ...@@ -236,57 +236,57 @@
height: 16px; /* tweak the icon size of the editor labels when icons are enabled */ height: 16px; /* tweak the icon size of the editor labels when icons are enabled */
} }
/* Tab Close */ /* Tab Actions */
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-close { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions {
margin-top: auto; margin-top: auto;
margin-bottom: auto; margin-bottom: auto;
width: 28px; width: 28px;
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink > .tab-actions {
flex: 0; flex: 0;
overflow: hidden; /* let the close button be pushed out of view when sizing is set to shrink to make more room... */ overflow: hidden; /* let the tab actions 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.dirty.tab-actions-right.sizing-shrink > .tab-actions,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink:hover > .tab-close, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink:hover > .tab-actions,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-right.sizing-shrink > .tab-close:focus-within { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink > .tab-actions:focus-within {
overflow: visible; /* ...but still show the close button on hover, focus and when dirty */ overflow: visible; /* ...but still show the tab actions on hover, focus and when dirty */
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off:not(.dirty) > .tab-close, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off:not(.dirty) > .tab-actions,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.sticky-compact > .tab-close { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.sticky-compact > .tab-actions {
display: none; /* hide the close action bar when we are configured to hide it (unless dirty, but always when sticky-compact) */ display: none; /* hide the tab actions when we are configured to hide it (unless dirty, but always when sticky-compact) */
} }
.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.active > .tab-actions .action-label, /* always show tab actions 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 > .tab-actions .action-label:focus, /* always show tab actions 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:hover > .tab-actions .action-label, /* always show tab actions 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.active:hover > .tab-actions .action-label, /* always show tab actions 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 */ .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label { /* always show tab actions for dirty tabs */
opacity: 1; opacity: 1;
} }
.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-close .action-label.codicon { .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-actions .action-label.codicon {
color: inherit; color: inherit;
font-size: 16px; font-size: 16px;
} }
/* change close icon to dirty state icon */ /* change tab actions icon to dirty state icon if tab dirty */
.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-close .action-label:not(:hover)::before, .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover)::before,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-close .action-label:not(:hover)::before { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover)::before {
content: "\ea71"; /* use `circle-filled` icon unicode */ content: "\ea71"; /* use `circle-filled` icon unicode */
} }
.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 > .tab-actions .action-label, /* show tab actions 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.active:hover > .tab-actions .action-label, /* show tab actions 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.dirty > .tab-actions .action-label, /* show tab actions 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 */ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-actions .action-label { /* show tab actions dimmed for inactive group */
opacity: 0.5; opacity: 0.5;
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-close .action-label { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions .action-label {
opacity: 0; opacity: 0;
display: block; display: block;
height: 16px; height: 16px;
...@@ -297,26 +297,26 @@ ...@@ -297,26 +297,26 @@
margin-right: 0.5em; margin-right: 0.5em;
} }
/* No Tab Close Button */ /* No Tab Actions */
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off {
padding-right: 10px; /* give a little bit more room if close button is off */ padding-right: 10px; /* give a little bit more room if tab actions is off */
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-button-off:not(.sticky-compact) { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off:not(.sticky-compact) {
padding-right: 5px; /* we need less room when sizing is shrink (unless tab is sticky-compact) */ padding-right: 5px; /* we need less room when sizing is shrink (unless tab is sticky-compact) */
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty-border-top > .tab-close { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.dirty-border-top > .tab-actions {
display: none; /* hide dirty state when highlightModifiedTabs is enabled and when running without close button */ display: none; /* hide dirty state when highlightModifiedTabs is enabled and when running without tab actions */
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty:not(.dirty-border-top):not(.sticky-compact) { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.dirty:not(.dirty-border-top):not(.sticky-compact) {
padding-right: 0; /* remove extra padding when we are running without close button (unless tab is sticky-compact) */ padding-right: 0; /* remove extra padding when we are running without tab actions (unless tab is sticky-compact) */
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off > .tab-close { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off > .tab-actions {
pointer-events: none; /* don't allow dirty state/close button to be clicked when running without close button */ pointer-events: none; /* don't allow tab actions to be clicked when running without tab actions */
} }
/* Editor Actions */ /* Editor Actions */
......
...@@ -20,7 +20,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; ...@@ -20,7 +20,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMenuService } from 'vs/platform/actions/common/actions'; import { IMenuService } from 'vs/platform/actions/common/actions';
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl'; import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IDisposable, dispose, DisposableStore, combinedDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IDisposable, dispose, DisposableStore, combinedDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { getOrSet } from 'vs/base/common/map'; import { getOrSet } from 'vs/base/common/map';
...@@ -35,7 +35,7 @@ import { MergeGroupMode, IMergeGroupOptions, GroupsArrangement, IEditorGroupsSer ...@@ -35,7 +35,7 @@ import { MergeGroupMode, IMergeGroupOptions, GroupsArrangement, IEditorGroupsSer
import { addClass, addDisposableListener, hasClass, EventType, EventHelper, removeClass, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode } from 'vs/base/browser/dom'; import { addClass, addDisposableListener, hasClass, EventType, EventHelper, removeClass, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode } from 'vs/base/browser/dom';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IEditorGroupsAccessor, IEditorGroupView, EditorServiceImpl, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor'; import { IEditorGroupsAccessor, IEditorGroupView, EditorServiceImpl, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
import { CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorActions'; import { CloseOneEditorAction, UnpinEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl'; import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
...@@ -45,6 +45,7 @@ import { basenameOrAuthority } from 'vs/base/common/resources'; ...@@ -45,6 +45,7 @@ import { basenameOrAuthority } from 'vs/base/common/resources';
import { RunOnceScheduler } from 'vs/base/common/async'; import { RunOnceScheduler } from 'vs/base/common/async';
import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { IPath, win32, posix } from 'vs/base/common/path'; import { IPath, win32, posix } from 'vs/base/common/path';
import { insert } from 'vs/base/common/arrays';
interface IEditorInputLabel { interface IEditorInputLabel {
name?: string; name?: string;
...@@ -74,10 +75,12 @@ export class TabsTitleControl extends TitleControl { ...@@ -74,10 +75,12 @@ export class TabsTitleControl extends TitleControl {
private editorToolbarContainer: HTMLElement | undefined; private editorToolbarContainer: HTMLElement | undefined;
private tabsScrollbar: ScrollableElement | undefined; private tabsScrollbar: ScrollableElement | undefined;
private closeOneEditorAction: CloseOneEditorAction; private readonly closeEditorAction = this._register(this.instantiationService.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL));
private readonly unpinEditorAction = this._register(this.instantiationService.createInstance(UnpinEditorAction, UnpinEditorAction.ID, UnpinEditorAction.LABEL));
private tabResourceLabels: ResourceLabels; private readonly tabResourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER));
private tabLabels: IEditorInputLabel[] = []; private tabLabels: IEditorInputLabel[] = [];
private tabActionBars: ActionBar[] = [];
private tabDisposables: IDisposable[] = []; private tabDisposables: IDisposable[] = [];
private dimension: Dimension | undefined; private dimension: Dimension | undefined;
...@@ -108,9 +111,6 @@ export class TabsTitleControl extends TitleControl { ...@@ -108,9 +111,6 @@ export class TabsTitleControl extends TitleControl {
) { ) {
super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickInputService, themeService, extensionService, configurationService, fileService); super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickInputService, themeService, extensionService, configurationService, fileService);
this.tabResourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER));
this.closeOneEditorAction = this._register(this.instantiationService.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL));
// Resolve the correct path library for the OS we are on // Resolve the correct path library for the OS we are on
// If we are connected to remote, this accounts for the // If we are connected to remote, this accounts for the
// remote OS. // remote OS.
...@@ -426,6 +426,7 @@ export class TabsTitleControl extends TitleControl { ...@@ -426,6 +426,7 @@ export class TabsTitleControl extends TitleControl {
this.tabDisposables = dispose(this.tabDisposables); this.tabDisposables = dispose(this.tabDisposables);
this.tabResourceLabels.clear(); this.tabResourceLabels.clear();
this.tabLabels = []; this.tabLabels = [];
this.tabActionBars = [];
this.clearEditorActionsToolbar(); this.clearEditorActionsToolbar();
} }
...@@ -439,8 +440,8 @@ export class TabsTitleControl extends TitleControl { ...@@ -439,8 +440,8 @@ export class TabsTitleControl extends TitleControl {
this.tabLabels.splice(targetIndex, 0, editorLabel); this.tabLabels.splice(targetIndex, 0, editorLabel);
// As such we need to redraw each tab // As such we need to redraw each tab
this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel) => { this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => {
this.redrawTab(editor, index, tabContainer, tabLabelWidget, tabLabel); this.redrawTab(editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar);
}); });
// Moving an editor requires a layout to keep the active editor visible // Moving an editor requires a layout to keep the active editor visible
...@@ -462,7 +463,7 @@ export class TabsTitleControl extends TitleControl { ...@@ -462,7 +463,7 @@ export class TabsTitleControl extends TitleControl {
private doHandleStickyEditorChange(editor: IEditorInput): void { private doHandleStickyEditorChange(editor: IEditorInput): void {
// Update tab // Update tab
this.withTab(editor, (editor, index, tabContainer, tabLabelWidget, tabLabel) => this.redrawTab(editor, index, tabContainer, tabLabelWidget, tabLabel)); this.withTab(editor, (editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => this.redrawTab(editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar));
// A change to the sticky state requires a layout to keep the active editor visible // A change to the sticky state requires a layout to keep the active editor visible
this.layout(this.dimension); this.layout(this.dimension);
...@@ -535,23 +536,24 @@ export class TabsTitleControl extends TitleControl { ...@@ -535,23 +536,24 @@ export class TabsTitleControl extends TitleControl {
this.redraw(); this.redraw();
} }
private forEachTab(fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void { private forEachTab(fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel, tabActionBar: ActionBar) => void): void {
this.group.editors.forEach((editor, index) => { this.group.editors.forEach((editor, index) => {
this.doWithTab(index, editor, fn); this.doWithTab(index, editor, fn);
}); });
} }
private withTab(editor: IEditorInput, fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void { private withTab(editor: IEditorInput, fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel, tabActionBar: ActionBar) => void): void {
this.doWithTab(this.group.getIndexOfEditor(editor), editor, fn); this.doWithTab(this.group.getIndexOfEditor(editor), editor, fn);
} }
private doWithTab(index: number, editor: IEditorInput, fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void { private doWithTab(index: number, editor: IEditorInput, fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel, tabActionBar: ActionBar) => void): void {
const tabsContainer = assertIsDefined(this.tabsContainer); const tabsContainer = assertIsDefined(this.tabsContainer);
const tabContainer = tabsContainer.children[index] as HTMLElement; const tabContainer = tabsContainer.children[index] as HTMLElement;
const tabResourceLabel = this.tabResourceLabels.get(index); const tabResourceLabel = this.tabResourceLabels.get(index);
const tabLabel = this.tabLabels[index]; const tabLabel = this.tabLabels[index];
const tabActionBar = this.tabActionBars[index];
if (tabContainer && tabResourceLabel && tabLabel) { if (tabContainer && tabResourceLabel && tabLabel) {
fn(editor, index, tabContainer, tabResourceLabel, tabLabel); fn(editor, index, tabContainer, tabResourceLabel, tabLabel, tabActionBar);
} }
} }
...@@ -575,26 +577,35 @@ export class TabsTitleControl extends TitleControl { ...@@ -575,26 +577,35 @@ export class TabsTitleControl extends TitleControl {
// Tab Editor Label // Tab Editor Label
const editorLabel = this.tabResourceLabels.create(tabContainer); const editorLabel = this.tabResourceLabels.create(tabContainer);
// Tab Close Button // Tab Actions
const tabCloseContainer = document.createElement('div'); const tabActionsContainer = document.createElement('div');
addClass(tabCloseContainer, 'tab-close'); addClass(tabActionsContainer, 'tab-actions');
tabContainer.appendChild(tabCloseContainer); tabContainer.appendChild(tabActionsContainer);
const tabActionRunner = new EditorCommandsContextActionRunner({ groupId: this.group.id, editorIndex: index });
const tabActionBar = new ActionBar(tabActionsContainer, {
ariaLabel: localize('ariaLabelTabActions', "Tab actions"),
actionRunner: tabActionRunner,
});
tabActionBar.onDidBeforeRun(e => {
if (e.action.id === this.closeEditorAction.id) {
this.blockRevealActiveTabOnce();
}
});
const tabActionBarDisposable = combinedDisposable(tabActionBar, toDisposable(insert(this.tabActionBars, tabActionBar)));
// Tab Border Bottom // Tab Border Bottom
const tabBorderBottomContainer = document.createElement('div'); const tabBorderBottomContainer = document.createElement('div');
addClass(tabBorderBottomContainer, 'tab-border-bottom-container'); addClass(tabBorderBottomContainer, 'tab-border-bottom-container');
tabContainer.appendChild(tabBorderBottomContainer); tabContainer.appendChild(tabBorderBottomContainer);
const tabActionRunner = new EditorCommandsContextActionRunner({ groupId: this.group.id, editorIndex: index });
const tabActionBar = new ActionBar(tabCloseContainer, { ariaLabel: localize('araLabelTabActions', "Tab actions"), actionRunner: tabActionRunner });
tabActionBar.push(this.closeOneEditorAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.closeOneEditorAction) });
tabActionBar.onDidBeforeRun(() => this.blockRevealActiveTabOnce());
// Eventing // Eventing
const eventsDisposable = this.registerTabListeners(tabContainer, index, tabsContainer, tabsScrollbar); const eventsDisposable = this.registerTabListeners(tabContainer, index, tabsContainer, tabsScrollbar);
this.tabDisposables.push(combinedDisposable(eventsDisposable, tabActionBar, tabActionRunner, editorLabel)); this.tabDisposables.push(combinedDisposable(eventsDisposable, tabActionBarDisposable, tabActionRunner, editorLabel));
return tabContainer; return tabContainer;
} }
...@@ -657,7 +668,7 @@ export class TabsTitleControl extends TitleControl { ...@@ -657,7 +668,7 @@ export class TabsTitleControl extends TitleControl {
EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */); EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */);
this.blockRevealActiveTabOnce(); this.blockRevealActiveTabOnce();
this.closeOneEditorAction.run({ groupId: this.group.id, editorIndex: index }); this.closeEditorAction.run({ groupId: this.group.id, editorIndex: index });
} }
})); }));
...@@ -1002,8 +1013,8 @@ export class TabsTitleControl extends TitleControl { ...@@ -1002,8 +1013,8 @@ export class TabsTitleControl extends TitleControl {
} }
// For each tab // For each tab
this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel) => { this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => {
this.redrawTab(editor, index, tabContainer, tabLabelWidget, tabLabel); this.redrawTab(editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar);
}); });
// Update Editor Actions Toolbar // Update Editor Actions Toolbar
...@@ -1013,24 +1024,32 @@ export class TabsTitleControl extends TitleControl { ...@@ -1013,24 +1024,32 @@ export class TabsTitleControl extends TitleControl {
this.layout(this.dimension); this.layout(this.dimension);
} }
private redrawTab(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel): void { private redrawTab(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel, tabActionBar: ActionBar): void {
const isTabSticky = this.group.isSticky(index);
const options = this.accessor.partOptions;
// Label // Label
this.redrawLabel(editor, index, tabContainer, tabLabelWidget, tabLabel); this.redrawLabel(editor, index, tabContainer, tabLabelWidget, tabLabel);
// Action
const tabAction = isTabSticky ? this.unpinEditorAction : this.closeEditorAction;
if (!tabActionBar.hasAction(tabAction)) {
if (!tabActionBar.isEmpty()) {
tabActionBar.clear();
}
tabActionBar.push(tabAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(tabAction) });
}
// Borders / Outline // Borders / Outline
const borderRightColor = (this.getColor(TAB_BORDER) || this.getColor(contrastBorder)); const borderRightColor = (this.getColor(TAB_BORDER) || this.getColor(contrastBorder));
tabContainer.style.borderRight = borderRightColor ? `1px solid ${borderRightColor}` : ''; tabContainer.style.borderRight = borderRightColor ? `1px solid ${borderRightColor}` : '';
tabContainer.style.outlineColor = this.getColor(activeContrastBorder) || ''; tabContainer.style.outlineColor = this.getColor(activeContrastBorder) || '';
// Settings // Settings
const isTabSticky = this.group.isSticky(index); const tabActionsVisibility = isTabSticky && options.pinnedTabSizing === 'compact' ? 'off' /* treat sticky compact tabs as tabCloseButton: 'off' */ : options.tabCloseButton;
const options = this.accessor.partOptions;
const tabCloseButton = isTabSticky && options.pinnedTabSizing === 'compact' ? 'off' /* treat sticky compact tabs as tabCloseButton: 'off' */ : options.tabCloseButton;
['off', 'left', 'right'].forEach(option => { ['off', 'left', 'right'].forEach(option => {
const domAction = tabCloseButton === option ? addClass : removeClass; const domAction = tabActionsVisibility === option ? addClass : removeClass;
domAction(tabContainer, `close-button-${option}`); domAction(tabContainer, `tab-actions-${option}`);
}); });
const tabSizing = isTabSticky && options.pinnedTabSizing === 'shrink' ? 'shrink' /* treat sticky shrink tabs as tabSizing: 'shrink' */ : options.tabSizing; const tabSizing = isTabSticky && options.pinnedTabSizing === 'shrink' ? 'shrink' /* treat sticky shrink tabs as tabSizing: 'shrink' */ : options.tabSizing;
...@@ -1523,10 +1542,10 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = ...@@ -1523,10 +1542,10 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
outline-offset: -5px; outline-offset: -5px;
} }
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-close .action-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-actions .action-label,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-close .action-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-actions .action-label,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-close .action-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label,
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-close .action-label { .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-actions .action-label {
opacity: 1 !important; opacity: 1 !important;
} }
`); `);
......
...@@ -73,7 +73,7 @@ registerSingleton(IAccessibilityService, NativeAccessibilityService, true); ...@@ -73,7 +73,7 @@ registerSingleton(IAccessibilityService, NativeAccessibilityService, true);
class LinuxAccessibilityContribution implements IWorkbenchContribution { class LinuxAccessibilityContribution implements IWorkbenchContribution {
constructor( constructor(
@IJSONEditingService jsonEditingService: IJSONEditingService, @IJSONEditingService jsonEditingService: IJSONEditingService,
@IAccessibilityService accessibilityService: AccessibilityService, @IAccessibilityService accessibilityService: IAccessibilityService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
) { ) {
const forceRendererAccessibility = () => { const forceRendererAccessibility = () => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册