mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-20 07:12:43 +01:00
Merge branch 'main' into merogge/alert-cue
This commit is contained in:
@@ -29,7 +29,7 @@ jobs:
|
||||
sudo update-rc.d xvfb defaults
|
||||
sudo service xvfb start
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
sudo update-rc.d xvfb defaults
|
||||
sudo service xvfb start
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
@@ -184,7 +184,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
@@ -256,7 +256,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
steps:
|
||||
- uses: 'actions/checkout@v4'
|
||||
|
||||
- uses: 'actions/setup-node@v3'
|
||||
- uses: 'actions/setup-node@v4'
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
|
||||
@@ -64,12 +64,12 @@
|
||||
"command.deleteBranch": "Delete Branch...",
|
||||
"command.renameBranch": "Rename Branch...",
|
||||
"command.cherryPick": "Cherry Pick...",
|
||||
"command.merge": "Merge Branch...",
|
||||
"command.merge": "Merge...",
|
||||
"command.mergeAbort": "Abort Merge",
|
||||
"command.rebase": "Rebase Branch...",
|
||||
"command.createTag": "Create Tag",
|
||||
"command.deleteTag": "Delete Tag",
|
||||
"command.deleteRemoteTag": "Delete Remote Tag",
|
||||
"command.deleteTag": "Delete Tag...",
|
||||
"command.deleteRemoteTag": "Delete Remote Tag...",
|
||||
"command.fetch": "Fetch",
|
||||
"command.fetchPrune": "Fetch (Prune)",
|
||||
"command.fetchAll": "Fetch From All Remotes",
|
||||
|
||||
+348
-210
@@ -8,7 +8,7 @@ import * as path from 'path';
|
||||
import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon } from 'vscode';
|
||||
import TelemetryReporter from '@vscode/extension-telemetry';
|
||||
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
|
||||
import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote } from './api/git';
|
||||
import { ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote } from './api/git';
|
||||
import { Git, Stash } from './git';
|
||||
import { Model } from './model';
|
||||
import { Repository, Resource, ResourceGroupType } from './repository';
|
||||
@@ -20,120 +20,179 @@ import { ApiRepository } from './api/api1';
|
||||
import { getRemoteSourceActions, pickRemoteSource } from './remoteSource';
|
||||
import { RemoteSourceAction } from './api/git-base';
|
||||
|
||||
class CheckoutItem implements QuickPickItem {
|
||||
abstract class CheckoutCommandItem implements QuickPickItem {
|
||||
abstract get label(): string;
|
||||
get description(): string { return ''; }
|
||||
get alwaysShow(): boolean { return true; }
|
||||
}
|
||||
|
||||
class CreateBranchItem extends CheckoutCommandItem {
|
||||
get label(): string { return l10n.t('{0} Create new branch...', '$(plus)'); }
|
||||
}
|
||||
|
||||
class CreateBranchFromItem extends CheckoutCommandItem {
|
||||
get label(): string { return l10n.t('{0} Create new branch from...', '$(plus)'); }
|
||||
}
|
||||
|
||||
class CheckoutDetachedItem extends CheckoutCommandItem {
|
||||
get label(): string { return l10n.t('{0} Checkout detached...', '$(debug-disconnect)'); }
|
||||
}
|
||||
|
||||
class RefItemSeparator implements QuickPickItem {
|
||||
get kind(): QuickPickItemKind { return QuickPickItemKind.Separator; }
|
||||
|
||||
get label(): string {
|
||||
switch (this.refType) {
|
||||
case RefType.Head:
|
||||
return l10n.t('branches');
|
||||
case RefType.RemoteHead:
|
||||
return l10n.t('remote branches');
|
||||
case RefType.Tag:
|
||||
return l10n.t('tags');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private readonly refType: RefType) { }
|
||||
}
|
||||
|
||||
class RefItem implements QuickPickItem {
|
||||
|
||||
get label(): string {
|
||||
switch (this.ref.type) {
|
||||
case RefType.Head:
|
||||
return `$(git-branch) ${this.ref.name ?? this.shortCommit}`;
|
||||
case RefType.RemoteHead:
|
||||
return `$(cloud) ${this.ref.name ?? this.shortCommit}`;
|
||||
case RefType.Tag:
|
||||
return `$(tag) ${this.ref.name ?? this.shortCommit}`;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
get description(): string {
|
||||
switch (this.ref.type) {
|
||||
case RefType.Head:
|
||||
return this.shortCommit;
|
||||
case RefType.RemoteHead:
|
||||
return l10n.t('Remote branch at {0}', this.shortCommit);
|
||||
case RefType.Tag:
|
||||
return l10n.t('Tag at {0}', this.shortCommit);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
protected get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); }
|
||||
get label(): string { return `${this.repository.isBranchProtected(this.ref) ? '$(lock)' : '$(git-branch)'} ${this.ref.name || this.shortCommit}`; }
|
||||
get description(): string { return this.shortCommit; }
|
||||
get refName(): string | undefined { return this.ref.name; }
|
||||
get refRemote(): string | undefined { return this.ref.remote; }
|
||||
get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); }
|
||||
|
||||
private _buttons?: QuickInputButton[];
|
||||
get buttons(): QuickInputButton[] | undefined { return this._buttons; }
|
||||
set buttons(newButtons: QuickInputButton[] | undefined) { this._buttons = newButtons; }
|
||||
|
||||
constructor(protected repository: Repository, protected ref: Ref, protected _buttons?: QuickInputButton[]) { }
|
||||
constructor(protected readonly ref: Ref) { }
|
||||
}
|
||||
|
||||
async run(opts?: { detached?: boolean }): Promise<void> {
|
||||
class CheckoutItem extends RefItem {
|
||||
|
||||
async run(repository: Repository, opts?: { detached?: boolean }): Promise<void> {
|
||||
if (!this.ref.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
|
||||
const config = workspace.getConfiguration('git', Uri.file(repository.root));
|
||||
const pullBeforeCheckout = config.get<boolean>('pullBeforeCheckout', false) === true;
|
||||
|
||||
const treeish = opts?.detached ? this.ref.commit ?? this.ref.name : this.ref.name;
|
||||
await this.repository.checkout(treeish, { ...opts, pullBeforeCheckout });
|
||||
await repository.checkout(treeish, { ...opts, pullBeforeCheckout });
|
||||
}
|
||||
}
|
||||
|
||||
class CheckoutTagItem extends CheckoutItem {
|
||||
class CheckoutProtectedItem extends CheckoutItem {
|
||||
|
||||
override get label(): string { return `$(tag) ${this.ref.name || this.shortCommit}`; }
|
||||
override get description(): string {
|
||||
return l10n.t('Tag at {0}', this.shortCommit);
|
||||
override get label(): string {
|
||||
return `$(lock) ${this.ref.name ?? this.shortCommit}`;
|
||||
}
|
||||
|
||||
override async run(opts?: { detached?: boolean }): Promise<void> {
|
||||
if (!this.ref.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.repository.checkout(this.ref.name, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class CheckoutRemoteHeadItem extends CheckoutItem {
|
||||
class CheckoutRemoteHeadItem extends RefItem {
|
||||
|
||||
override get label(): string { return `$(cloud) ${this.ref.name || this.shortCommit}`; }
|
||||
override get description(): string {
|
||||
return l10n.t('Remote branch at {0}', this.shortCommit);
|
||||
}
|
||||
|
||||
override async run(opts?: { detached?: boolean }): Promise<void> {
|
||||
async run(repository: Repository, opts?: { detached?: boolean }): Promise<void> {
|
||||
if (!this.ref.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (opts?.detached) {
|
||||
await this.repository.checkout(this.ref.commit ?? this.ref.name, opts);
|
||||
await repository.checkout(this.ref.commit ?? this.ref.name, opts);
|
||||
return;
|
||||
}
|
||||
|
||||
const branches = await this.repository.findTrackingBranches(this.ref.name);
|
||||
const branches = await repository.findTrackingBranches(this.ref.name);
|
||||
|
||||
if (branches.length > 0) {
|
||||
await this.repository.checkout(branches[0].name!, opts);
|
||||
await repository.checkout(branches[0].name!, opts);
|
||||
} else {
|
||||
await this.repository.checkoutTracking(this.ref.name, opts);
|
||||
await repository.checkoutTracking(this.ref.name, opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BranchDeleteItem implements QuickPickItem {
|
||||
class CheckoutTagItem extends RefItem {
|
||||
|
||||
private get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); }
|
||||
get branchName(): string | undefined { return this.ref.name; }
|
||||
get label(): string { return this.branchName || ''; }
|
||||
get description(): string { return this.shortCommit; }
|
||||
|
||||
constructor(private ref: Ref) { }
|
||||
|
||||
async run(repository: Repository, force?: boolean): Promise<void> {
|
||||
if (!this.branchName) {
|
||||
async run(repository: Repository, opts?: { detached?: boolean }): Promise<void> {
|
||||
if (!this.ref.name) {
|
||||
return;
|
||||
}
|
||||
await repository.deleteBranch(this.branchName, force);
|
||||
|
||||
await repository.checkout(this.ref.name, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class MergeItem implements QuickPickItem {
|
||||
class BranchDeleteItem extends RefItem {
|
||||
|
||||
private shortCommit: string;
|
||||
|
||||
get label(): string {
|
||||
return this.ref.type === RefType.RemoteHead ?
|
||||
`$(cloud) ${this.ref.name ?? this.shortCommit}` :
|
||||
`${this.repository.isBranchProtected(this.ref) ? '$(lock)' : '$(git-branch)'} ${this.ref.name ?? this.shortCommit}`;
|
||||
}
|
||||
|
||||
get description(): string {
|
||||
return this.ref.type === RefType.RemoteHead ? l10n.t('Remote branch at {0}', this.shortCommit) : this.shortCommit;
|
||||
}
|
||||
|
||||
constructor(private readonly repository: Repository, private readonly ref: Ref) {
|
||||
this.shortCommit = (this.ref.commit ?? '').substring(0, 8);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
await this.repository.merge(this.ref.name ?? this.ref.commit!);
|
||||
async run(repository: Repository, force?: boolean): Promise<void> {
|
||||
if (this.ref.name) {
|
||||
await repository.deleteBranch(this.ref.name, force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RebaseItem implements QuickPickItem {
|
||||
class TagDeleteItem extends RefItem {
|
||||
|
||||
get label(): string { return this.ref.name || ''; }
|
||||
description: string = '';
|
||||
async run(repository: Repository): Promise<void> {
|
||||
if (this.ref.name) {
|
||||
await repository.deleteTag(this.ref.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(readonly ref: Ref) { }
|
||||
class RemoteTagDeleteItem extends RefItem {
|
||||
|
||||
override get description(): string {
|
||||
return l10n.t('Remote tag at {0}', this.shortCommit);
|
||||
}
|
||||
|
||||
async run(repository: Repository, remote: string): Promise<void> {
|
||||
if (this.ref.name) {
|
||||
await repository.deleteRemoteTag(remote, this.ref.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MergeItem extends RefItem {
|
||||
|
||||
async run(repository: Repository): Promise<void> {
|
||||
if (this.ref.name || this.ref.commit) {
|
||||
await repository.merge(this.ref.name ?? this.ref.commit!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RebaseItem extends RefItem {
|
||||
|
||||
async run(repository: Repository): Promise<void> {
|
||||
if (this.ref?.name) {
|
||||
@@ -142,22 +201,11 @@ class RebaseItem implements QuickPickItem {
|
||||
}
|
||||
}
|
||||
|
||||
class CreateBranchItem implements QuickPickItem {
|
||||
get label(): string { return '$(plus) ' + l10n.t('Create new branch...'); }
|
||||
get description(): string { return ''; }
|
||||
get alwaysShow(): boolean { return true; }
|
||||
}
|
||||
class RebaseUpstreamItem extends RebaseItem {
|
||||
|
||||
class CreateBranchFromItem implements QuickPickItem {
|
||||
get label(): string { return '$(plus) ' + l10n.t('Create new branch from...'); }
|
||||
get description(): string { return ''; }
|
||||
get alwaysShow(): boolean { return true; }
|
||||
}
|
||||
|
||||
class CheckoutDetachedItem implements QuickPickItem {
|
||||
get label(): string { return '$(debug-disconnect) ' + l10n.t('Checkout detached...'); }
|
||||
get description(): string { return ''; }
|
||||
get alwaysShow(): boolean { return true; }
|
||||
override get description(): string {
|
||||
return '(upstream)';
|
||||
}
|
||||
}
|
||||
|
||||
class HEADItem implements QuickPickItem {
|
||||
@@ -265,7 +313,7 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{
|
||||
return { merge, resolved, unresolved, deletionConflicts };
|
||||
}
|
||||
|
||||
async function createCheckoutItems(repository: Repository, detached = false): Promise<CheckoutItem[]> {
|
||||
async function createCheckoutItems(repository: Repository, detached = false): Promise<QuickPickItem[]> {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const checkoutTypeConfig = config.get<string | string[]>('checkoutType');
|
||||
let checkoutTypes: string[];
|
||||
@@ -284,39 +332,13 @@ async function createCheckoutItems(repository: Repository, detached = false): Pr
|
||||
}
|
||||
|
||||
const refs = await repository.getRefs();
|
||||
const processors = checkoutTypes.map(type => getCheckoutProcessor(repository, type))
|
||||
.filter(p => !!p) as CheckoutProcessor[];
|
||||
|
||||
for (const ref of refs) {
|
||||
if (!detached && ref.name === 'origin/HEAD') {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const processor of processors) {
|
||||
processor.onRef(ref);
|
||||
}
|
||||
}
|
||||
const refProcessors = checkoutTypes.map(type => getCheckoutRefProcessor(repository, type))
|
||||
.filter(p => !!p) as RefProcessor[];
|
||||
|
||||
const buttons = await getRemoteRefItemButtons(repository);
|
||||
let fallbackRemoteButtons: RemoteSourceActionButton[] | undefined = [];
|
||||
const remote = repository.remotes.find(r => r.pushUrl === repository.HEAD?.remote || r.fetchUrl === repository.HEAD?.remote) ?? repository.remotes[0];
|
||||
const remoteUrl = remote?.pushUrl ?? remote?.fetchUrl;
|
||||
if (remoteUrl) {
|
||||
fallbackRemoteButtons = buttons.get(remoteUrl);
|
||||
}
|
||||
const itemsProcessor = new CheckoutItemsProcessor(refProcessors, repository, buttons, detached);
|
||||
|
||||
return processors.reduce<CheckoutItem[]>((r, p) => r.concat(...p.items.map((item) => {
|
||||
if (item.refRemote) {
|
||||
const matchingRemote = repository.remotes.find((remote) => remote.name === item.refRemote);
|
||||
const remoteUrl = matchingRemote?.pushUrl ?? matchingRemote?.fetchUrl;
|
||||
if (remoteUrl) {
|
||||
item.buttons = buttons.get(item.refRemote);
|
||||
}
|
||||
}
|
||||
|
||||
item.buttons = fallbackRemoteButtons;
|
||||
return item;
|
||||
})), []);
|
||||
return itemsProcessor.processRefs(refs);
|
||||
}
|
||||
|
||||
type RemoteSourceActionButton = {
|
||||
@@ -347,30 +369,181 @@ async function getRemoteRefItemButtons(repository: Repository) {
|
||||
return remoteUrlsToActions;
|
||||
}
|
||||
|
||||
class CheckoutProcessor {
|
||||
class RefProcessor {
|
||||
protected readonly refs: Ref[] = [];
|
||||
|
||||
private refs: Ref[] = [];
|
||||
get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(this.repository, r)); }
|
||||
constructor(private repository: Repository, private type: RefType, private ctor: { new(repository: Repository, ref: Ref): CheckoutItem }) { }
|
||||
get items(): QuickPickItem[] {
|
||||
const items = this.refs.map(r => new this.ctor(r));
|
||||
return items.length === 0 ? items : [new RefItemSeparator(this.type), ...items];
|
||||
}
|
||||
|
||||
onRef(ref: Ref): void {
|
||||
if (ref.type === this.type) {
|
||||
this.refs.push(ref);
|
||||
constructor(protected readonly type: RefType, protected readonly ctor: { new(ref: Ref): QuickPickItem } = RefItem) { }
|
||||
|
||||
processRef(ref: Ref): boolean {
|
||||
if (!ref.name && !ref.commit) {
|
||||
return false;
|
||||
}
|
||||
if (ref.type !== this.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.refs.push(ref);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function getCheckoutProcessor(repository: Repository, type: string): CheckoutProcessor | undefined {
|
||||
switch (type) {
|
||||
case 'local':
|
||||
return new CheckoutProcessor(repository, RefType.Head, CheckoutItem);
|
||||
case 'remote':
|
||||
return new CheckoutProcessor(repository, RefType.RemoteHead, CheckoutRemoteHeadItem);
|
||||
case 'tags':
|
||||
return new CheckoutProcessor(repository, RefType.Tag, CheckoutTagItem);
|
||||
class RefItemsProcessor {
|
||||
|
||||
constructor(protected readonly processors: RefProcessor[]) { }
|
||||
|
||||
processRefs(refs: Ref[]): QuickPickItem[] {
|
||||
for (const ref of refs) {
|
||||
for (const processor of this.processors) {
|
||||
if (processor.processRef(ref)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result: QuickPickItem[] = [];
|
||||
for (const processor of this.processors) {
|
||||
result.push(...processor.items);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class RebaseItemsProcessors extends RefItemsProcessor {
|
||||
|
||||
private upstreamName: string | undefined;
|
||||
|
||||
constructor(private readonly repository: Repository) {
|
||||
super([
|
||||
new RefProcessor(RefType.Head, RebaseItem),
|
||||
new RefProcessor(RefType.RemoteHead, RebaseItem)
|
||||
]);
|
||||
|
||||
if (this.repository.HEAD?.upstream) {
|
||||
this.upstreamName = `${this.repository.HEAD?.upstream.remote}/${this.repository.HEAD?.upstream.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
override processRefs(refs: Ref[]): QuickPickItem[] {
|
||||
const result: QuickPickItem[] = [];
|
||||
|
||||
for (const ref of refs) {
|
||||
if (ref.name === this.repository.HEAD?.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ref.name === this.upstreamName) {
|
||||
result.push(new RebaseUpstreamItem(ref));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const processor of this.processors) {
|
||||
if (processor.processRef(ref)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const processor of this.processors) {
|
||||
result.push(...processor.items);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class CheckoutRefProcessor extends RefProcessor {
|
||||
|
||||
override get items(): QuickPickItem[] {
|
||||
const items = this.refs.map(ref => {
|
||||
return this.repository.isBranchProtected(ref) ?
|
||||
new CheckoutProtectedItem(ref) :
|
||||
new CheckoutItem(ref);
|
||||
});
|
||||
|
||||
return items.length === 0 ? items : [new RefItemSeparator(this.type), ...items];
|
||||
}
|
||||
|
||||
constructor(private readonly repository: Repository) {
|
||||
super(RefType.Head);
|
||||
}
|
||||
}
|
||||
|
||||
class CheckoutItemsProcessor extends RefItemsProcessor {
|
||||
|
||||
private defaultButtons: RemoteSourceActionButton[] | undefined;
|
||||
|
||||
constructor(
|
||||
processors: RefProcessor[],
|
||||
private readonly repository: Repository,
|
||||
private readonly buttons: Map<string, RemoteSourceActionButton[]>,
|
||||
private readonly detached = false) {
|
||||
super(processors);
|
||||
|
||||
// Default button(s)
|
||||
const remote = repository.remotes.find(r => r.pushUrl === repository.HEAD?.remote || r.fetchUrl === repository.HEAD?.remote) ?? repository.remotes[0];
|
||||
const remoteUrl = remote?.pushUrl ?? remote?.fetchUrl;
|
||||
if (remoteUrl) {
|
||||
this.defaultButtons = buttons.get(remoteUrl);
|
||||
}
|
||||
}
|
||||
|
||||
override processRefs(refs: Ref[]): QuickPickItem[] {
|
||||
for (const ref of refs) {
|
||||
if (!this.detached && ref.name === 'origin/HEAD') {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const processor of this.processors) {
|
||||
if (processor.processRef(ref)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result: QuickPickItem[] = [];
|
||||
for (const processor of this.processors) {
|
||||
for (const item of processor.items) {
|
||||
if (!(item instanceof RefItem)) {
|
||||
result.push(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Button(s)
|
||||
if (item.refRemote) {
|
||||
const matchingRemote = this.repository.remotes.find((remote) => remote.name === item.refRemote);
|
||||
const remoteUrl = matchingRemote?.pushUrl ?? matchingRemote?.fetchUrl;
|
||||
if (remoteUrl) {
|
||||
item.buttons = this.buttons.get(item.refRemote);
|
||||
}
|
||||
} else {
|
||||
item.buttons = this.defaultButtons;
|
||||
}
|
||||
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function getCheckoutRefProcessor(repository: Repository, type: string): RefProcessor | undefined {
|
||||
switch (type) {
|
||||
case 'local':
|
||||
return new CheckoutRefProcessor(repository);
|
||||
case 'remote':
|
||||
return new RefProcessor(RefType.RemoteHead, CheckoutRemoteHeadItem);
|
||||
case 'tags':
|
||||
return new RefProcessor(RefType.Tag, CheckoutTagItem);
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getRepositoryLabel(repositoryRoot: string): string {
|
||||
@@ -391,12 +564,6 @@ function sanitizeRemoteName(name: string) {
|
||||
return name && name.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, '-');
|
||||
}
|
||||
|
||||
class TagItem implements QuickPickItem {
|
||||
get label(): string { return `$(tag) ${this.ref.name ?? ''}`; }
|
||||
get description(): string { return this.ref.commit?.substr(0, 8) ?? ''; }
|
||||
constructor(readonly ref: Ref) { }
|
||||
}
|
||||
|
||||
enum PushType {
|
||||
Push,
|
||||
PushTo,
|
||||
@@ -2230,7 +2397,7 @@ export class CommandCenter {
|
||||
const picks: QuickPickItem[] = [];
|
||||
|
||||
if (!opts?.detached) {
|
||||
picks.push(createBranch, createBranchFrom, checkoutDetached, { label: '', kind: QuickPickItemKind.Separator });
|
||||
picks.push(createBranch, createBranchFrom, checkoutDetached);
|
||||
}
|
||||
|
||||
const quickpick = window.createQuickPick();
|
||||
@@ -2272,7 +2439,7 @@ export class CommandCenter {
|
||||
const item = choice as CheckoutItem;
|
||||
|
||||
try {
|
||||
await item.run(opts);
|
||||
await item.run(repository, opts);
|
||||
} catch (err) {
|
||||
if (err.gitErrorCode !== GitErrorCodes.DirtyWorkTree) {
|
||||
throw err;
|
||||
@@ -2285,10 +2452,10 @@ export class CommandCenter {
|
||||
|
||||
if (choice === force) {
|
||||
await this.cleanAll(repository);
|
||||
await item.run(opts);
|
||||
await item.run(repository, opts);
|
||||
} else if (choice === stash || choice === migrate) {
|
||||
if (await this._stash(repository)) {
|
||||
await item.run(opts);
|
||||
await item.run(repository, opts);
|
||||
|
||||
if (choice === migrate) {
|
||||
await this.stashPopLatest(repository);
|
||||
@@ -2408,7 +2575,14 @@ export class CommandCenter {
|
||||
|
||||
if (from) {
|
||||
const getRefPicks = async () => {
|
||||
return [new HEADItem(repository), ...await createCheckoutItems(repository)];
|
||||
const refs = await repository.getRefs();
|
||||
const refProcessors = new RefItemsProcessor([
|
||||
new RefProcessor(RefType.Head),
|
||||
new RefProcessor(RefType.RemoteHead),
|
||||
new RefProcessor(RefType.Tag)
|
||||
]);
|
||||
|
||||
return [new HEADItem(repository), ...refProcessors.processRefs(refs)];
|
||||
};
|
||||
|
||||
const placeHolder = l10n.t('Select a ref to create the branch from');
|
||||
@@ -2418,7 +2592,7 @@ export class CommandCenter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (choice.refName) {
|
||||
if (choice instanceof RefItem && choice.refName) {
|
||||
target = choice.refName;
|
||||
}
|
||||
}
|
||||
@@ -2448,10 +2622,10 @@ export class CommandCenter {
|
||||
const placeHolder = l10n.t('Select a branch to delete');
|
||||
const choice = await window.showQuickPick<BranchDeleteItem>(getBranchPicks(), { placeHolder });
|
||||
|
||||
if (!choice || !choice.branchName) {
|
||||
if (!choice || !choice.refName) {
|
||||
return;
|
||||
}
|
||||
name = choice.branchName;
|
||||
name = choice.refName;
|
||||
run = force => choice.run(repository, force);
|
||||
}
|
||||
|
||||
@@ -2499,32 +2673,23 @@ export class CommandCenter {
|
||||
|
||||
@command('git.merge', { repository: true })
|
||||
async merge(repository: Repository): Promise<void> {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const checkoutType = config.get<string | string[]>('checkoutType');
|
||||
const includeRemotes = checkoutType === 'all' || checkoutType === 'remote' || checkoutType?.includes('remote');
|
||||
|
||||
const getBranchPicks = async (): Promise<MergeItem[]> => {
|
||||
const getQuickPickItems = async (): Promise<QuickPickItem[]> => {
|
||||
const refs = await repository.getRefs();
|
||||
const itemsProcessor = new RefItemsProcessor([
|
||||
new RefProcessor(RefType.Head, MergeItem),
|
||||
new RefProcessor(RefType.RemoteHead, MergeItem),
|
||||
new RefProcessor(RefType.Tag, MergeItem)
|
||||
]);
|
||||
|
||||
const heads = refs.filter(ref => ref.type === RefType.Head)
|
||||
.filter(ref => ref.name || ref.commit)
|
||||
.map(ref => new MergeItem(repository, ref as Branch));
|
||||
|
||||
const remoteHeads = (includeRemotes ? refs.filter(ref => ref.type === RefType.RemoteHead) : [])
|
||||
.filter(ref => ref.name || ref.commit)
|
||||
.map(ref => new MergeItem(repository, ref as Branch));
|
||||
|
||||
return [...heads, ...remoteHeads];
|
||||
return itemsProcessor.processRefs(refs);
|
||||
};
|
||||
|
||||
const placeHolder = l10n.t('Select a branch to merge from');
|
||||
const choice = await window.showQuickPick<MergeItem>(getBranchPicks(), { placeHolder });
|
||||
const placeHolder = l10n.t('Select a branch or tag to merge from');
|
||||
const choice = await window.showQuickPick(getQuickPickItems(), { placeHolder });
|
||||
|
||||
if (!choice) {
|
||||
return;
|
||||
if (choice instanceof MergeItem) {
|
||||
await choice.run(repository);
|
||||
}
|
||||
|
||||
await choice.run();
|
||||
}
|
||||
|
||||
@command('git.mergeAbort', { repository: true })
|
||||
@@ -2534,45 +2699,19 @@ export class CommandCenter {
|
||||
|
||||
@command('git.rebase', { repository: true })
|
||||
async rebase(repository: Repository): Promise<void> {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const checkoutType = config.get<string | string[]>('checkoutType');
|
||||
const includeRemotes = checkoutType === 'all' || checkoutType === 'remote' || checkoutType?.includes('remote');
|
||||
|
||||
const getBranchPicks = async () => {
|
||||
const getQuickPickItems = async (): Promise<QuickPickItem[]> => {
|
||||
const refs = await repository.getRefs();
|
||||
const itemsProcessor = new RebaseItemsProcessors(repository);
|
||||
|
||||
const heads = refs.filter(ref => ref.type === RefType.Head)
|
||||
.filter(ref => ref.name !== repository.HEAD?.name)
|
||||
.filter(ref => ref.name || ref.commit);
|
||||
|
||||
const remoteHeads = (includeRemotes ? refs.filter(ref => ref.type === RefType.RemoteHead) : [])
|
||||
.filter(ref => ref.name || ref.commit);
|
||||
|
||||
const picks = [...heads, ...remoteHeads].map(ref => new RebaseItem(ref));
|
||||
|
||||
// set upstream branch as first
|
||||
if (repository.HEAD?.upstream) {
|
||||
const upstreamName = `${repository.HEAD?.upstream.remote}/${repository.HEAD?.upstream.name}`;
|
||||
const index = picks.findIndex(e => e.ref.name === upstreamName);
|
||||
|
||||
if (index > -1) {
|
||||
const [ref] = picks.splice(index, 1);
|
||||
ref.description = '(upstream)';
|
||||
picks.unshift(ref);
|
||||
}
|
||||
}
|
||||
|
||||
return picks;
|
||||
return itemsProcessor.processRefs(refs);
|
||||
};
|
||||
|
||||
const placeHolder = l10n.t('Select a branch to rebase onto');
|
||||
const choice = await window.showQuickPick<RebaseItem>(getBranchPicks(), { placeHolder });
|
||||
const choice = await window.showQuickPick(getQuickPickItems(), { placeHolder });
|
||||
|
||||
if (!choice) {
|
||||
return;
|
||||
if (choice instanceof RebaseItem) {
|
||||
await choice.run(repository);
|
||||
}
|
||||
|
||||
await choice.run(repository);
|
||||
}
|
||||
|
||||
@command('git.createTag', { repository: true })
|
||||
@@ -2599,16 +2738,16 @@ export class CommandCenter {
|
||||
|
||||
@command('git.deleteTag', { repository: true })
|
||||
async deleteTag(repository: Repository): Promise<void> {
|
||||
const tagPicks = async (): Promise<TagItem[] | QuickPickItem[]> => {
|
||||
const tagPicks = async (): Promise<TagDeleteItem[] | QuickPickItem[]> => {
|
||||
const remoteTags = await repository.getRefs({ pattern: 'refs/tags' });
|
||||
return remoteTags.length === 0 ? [{ label: l10n.t('$(info) This repository has no tags.') }] : remoteTags.map(ref => new TagItem(ref));
|
||||
return remoteTags.length === 0 ? [{ label: l10n.t('$(info) This repository has no tags.') }] : remoteTags.map(ref => new TagDeleteItem(ref));
|
||||
};
|
||||
|
||||
const placeHolder = l10n.t('Select a tag to delete');
|
||||
const choice = await window.showQuickPick<TagItem | QuickPickItem>(tagPicks(), { placeHolder });
|
||||
const choice = await window.showQuickPick<TagDeleteItem | QuickPickItem>(tagPicks(), { placeHolder });
|
||||
|
||||
if (choice && choice instanceof TagItem && choice.ref.name) {
|
||||
await repository.deleteTag(choice.ref.name);
|
||||
if (choice instanceof TagDeleteItem) {
|
||||
await choice.run(repository);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2635,7 +2774,7 @@ export class CommandCenter {
|
||||
remoteName = remotePick.remoteName;
|
||||
}
|
||||
|
||||
const remoteTagPicks = async (): Promise<TagItem[] | QuickPickItem[]> => {
|
||||
const remoteTagPicks = async (): Promise<RemoteTagDeleteItem[] | QuickPickItem[]> => {
|
||||
const remoteTagsRaw = await repository.getRemoteRefs(remoteName, { tags: true });
|
||||
|
||||
// Deduplicate annotated and lightweight tags
|
||||
@@ -2650,14 +2789,14 @@ export class CommandCenter {
|
||||
}
|
||||
}
|
||||
|
||||
return remoteTags.length === 0 ? [{ label: l10n.t('$(info) Remote "{0}" has no tags.', remoteName) }] : remoteTags.map(ref => new TagItem(ref));
|
||||
return remoteTags.length === 0 ? [{ label: l10n.t('$(info) Remote "{0}" has no tags.', remoteName) }] : remoteTags.map(ref => new RemoteTagDeleteItem(ref));
|
||||
};
|
||||
|
||||
const tagPickPlaceholder = l10n.t('Select a tag to delete');
|
||||
const remoteTagPick = await window.showQuickPick<TagItem | QuickPickItem>(remoteTagPicks(), { placeHolder: tagPickPlaceholder });
|
||||
const tagPickPlaceholder = l10n.t('Select a remote tag to delete');
|
||||
const remoteTagPick = await window.showQuickPick<RemoteTagDeleteItem | QuickPickItem>(remoteTagPicks(), { placeHolder: tagPickPlaceholder });
|
||||
|
||||
if (remoteTagPick && remoteTagPick instanceof TagItem && remoteTagPick.ref.name) {
|
||||
await repository.deleteRemoteTag(remoteName, remoteTagPick.ref.name);
|
||||
if (remoteTagPick instanceof RemoteTagDeleteItem) {
|
||||
await remoteTagPick.run(repository, remoteName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2747,21 +2886,20 @@ export class CommandCenter {
|
||||
remoteName = remotePick.label;
|
||||
}
|
||||
|
||||
const getBranchPicks = async (): Promise<QuickPickItem[]> => {
|
||||
const remoteRefs = await repository.getRefs();
|
||||
const remoteRefsFiltered = remoteRefs.filter(r => (r.remote === remoteName));
|
||||
return remoteRefsFiltered.map(r => ({ label: r.name! }));
|
||||
const getBranchPicks = async (): Promise<RefItem[]> => {
|
||||
const remoteRefs = await repository.getRefs({ pattern: `refs/remotes/${remoteName}/` });
|
||||
return remoteRefs.map(r => new RefItem(r));
|
||||
};
|
||||
|
||||
const branchPlaceHolder = l10n.t('Pick a branch to pull from');
|
||||
const branchPick = await window.showQuickPick(getBranchPicks(), { placeHolder: branchPlaceHolder });
|
||||
|
||||
if (!branchPick) {
|
||||
if (!branchPick || !branchPick.refName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const remoteCharCnt = remoteName.length;
|
||||
await repository.pullFrom(false, remoteName, branchPick.label.slice(remoteCharCnt + 1));
|
||||
await repository.pullFrom(false, remoteName, branchPick.refName.slice(remoteCharCnt + 1));
|
||||
}
|
||||
|
||||
@command('git.pull', { repository: true })
|
||||
|
||||
+8
-8
@@ -80,14 +80,14 @@
|
||||
"@vscode/windows-mutex": "^0.4.4",
|
||||
"@vscode/windows-process-tree": "^0.5.0",
|
||||
"@vscode/windows-registry": "^1.1.0",
|
||||
"@xterm/addon-canvas": "0.6.0-beta.19",
|
||||
"@xterm/addon-image": "0.7.0-beta.17",
|
||||
"@xterm/addon-search": "0.14.0-beta.19",
|
||||
"@xterm/addon-serialize": "0.12.0-beta.19",
|
||||
"@xterm/addon-unicode11": "0.7.0-beta.19",
|
||||
"@xterm/addon-webgl": "0.17.0-beta.19",
|
||||
"@xterm/headless": "5.4.0-beta.19",
|
||||
"@xterm/xterm": "5.4.0-beta.19",
|
||||
"@xterm/addon-canvas": "0.6.0-beta.20",
|
||||
"@xterm/addon-image": "0.7.0-beta.18",
|
||||
"@xterm/addon-search": "0.14.0-beta.20",
|
||||
"@xterm/addon-serialize": "0.12.0-beta.20",
|
||||
"@xterm/addon-unicode11": "0.7.0-beta.20",
|
||||
"@xterm/addon-webgl": "0.17.0-beta.20",
|
||||
"@xterm/headless": "5.4.0-beta.20",
|
||||
"@xterm/xterm": "5.4.0-beta.20",
|
||||
"graceful-fs": "4.2.11",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
|
||||
+8
-8
@@ -13,14 +13,14 @@
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"@vscode/windows-process-tree": "^0.5.0",
|
||||
"@vscode/windows-registry": "^1.1.0",
|
||||
"@xterm/addon-canvas": "0.6.0-beta.19",
|
||||
"@xterm/addon-image": "0.7.0-beta.17",
|
||||
"@xterm/addon-search": "0.14.0-beta.19",
|
||||
"@xterm/addon-serialize": "0.12.0-beta.19",
|
||||
"@xterm/addon-unicode11": "0.7.0-beta.19",
|
||||
"@xterm/addon-webgl": "0.17.0-beta.19",
|
||||
"@xterm/headless": "5.4.0-beta.19",
|
||||
"@xterm/xterm": "5.4.0-beta.19",
|
||||
"@xterm/addon-canvas": "0.6.0-beta.20",
|
||||
"@xterm/addon-image": "0.7.0-beta.18",
|
||||
"@xterm/addon-search": "0.14.0-beta.20",
|
||||
"@xterm/addon-serialize": "0.12.0-beta.20",
|
||||
"@xterm/addon-unicode11": "0.7.0-beta.20",
|
||||
"@xterm/addon-webgl": "0.17.0-beta.20",
|
||||
"@xterm/headless": "5.4.0-beta.20",
|
||||
"@xterm/xterm": "5.4.0-beta.20",
|
||||
"cookie": "^0.4.0",
|
||||
"graceful-fs": "4.2.11",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
"@microsoft/1ds-post-js": "^3.2.13",
|
||||
"@vscode/iconv-lite-umd": "0.7.0",
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"@xterm/addon-canvas": "0.6.0-beta.19",
|
||||
"@xterm/addon-image": "0.7.0-beta.17",
|
||||
"@xterm/addon-search": "0.14.0-beta.19",
|
||||
"@xterm/addon-serialize": "0.12.0-beta.19",
|
||||
"@xterm/addon-unicode11": "0.7.0-beta.19",
|
||||
"@xterm/addon-webgl": "0.17.0-beta.19",
|
||||
"@xterm/xterm": "5.4.0-beta.19",
|
||||
"@xterm/addon-canvas": "0.6.0-beta.20",
|
||||
"@xterm/addon-image": "0.7.0-beta.18",
|
||||
"@xterm/addon-search": "0.14.0-beta.20",
|
||||
"@xterm/addon-serialize": "0.12.0-beta.20",
|
||||
"@xterm/addon-unicode11": "0.7.0-beta.20",
|
||||
"@xterm/addon-webgl": "0.17.0-beta.20",
|
||||
"@xterm/xterm": "5.4.0-beta.20",
|
||||
"jschardet": "3.0.0",
|
||||
"tas-client-umd": "0.1.8",
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
|
||||
+28
-28
@@ -48,40 +48,40 @@
|
||||
resolved "https://registry.yarnpkg.com/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.21.tgz#89b48f293f6aa3341bb888c1118d16ff13b032d3"
|
||||
integrity sha512-zSUH9HYCw5qsCtd7b31yqkpaCU6jhtkKLkvOOA8yTrIRfBSOFb8PPhgmMicD7B/m+t4PwOJXzU1XDtrM9Fd3/g==
|
||||
|
||||
"@xterm/addon-canvas@0.6.0-beta.19":
|
||||
version "0.6.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.6.0-beta.19.tgz#5f8896b884d2a558a28eb197f7f4766ac37d33a0"
|
||||
integrity sha512-i+26DqYgI/PZViCiEK4Vu8c4Fi5J0i+TwnFfBDLcumHH07Al1uRd5BRaVg/i93vk6bRyEIOiOiIToXSf37ov/w==
|
||||
"@xterm/addon-canvas@0.6.0-beta.20":
|
||||
version "0.6.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.6.0-beta.20.tgz#078dddef70caf880b2cb121fdda37d301fc13156"
|
||||
integrity sha512-tHhsuqElE7LNiDJPbZzgVpmbcG2Dk6i2vh1EI+DzSByUWScDqLoeJbVPE5Xd2UW2garo24lxErpnIAlsytcA3A==
|
||||
|
||||
"@xterm/addon-image@0.7.0-beta.17":
|
||||
version "0.7.0-beta.17"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.7.0-beta.17.tgz#bc3d6f09619ef1f3b68c7009d29ee14003a26b30"
|
||||
integrity sha512-nnHVoYVoh+CpT4FQN/ALKesr96YvdVNUzQRQo4aAARUKst5DFaHQX9Yn/qLDN5s0WCqI3bgIEo8UAakfHITumA==
|
||||
"@xterm/addon-image@0.7.0-beta.18":
|
||||
version "0.7.0-beta.18"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.7.0-beta.18.tgz#588ea2d0841cff48c63bde1bfcdf56e9494dc6af"
|
||||
integrity sha512-+HQ+IBmHPelzjRJ5zO3XkjbeQNr2Zrf5wAlbPhy4EGSD0mDCqHJSfzZ8wKrhx7t8qpfiA8eTpWu/M76WsEnlnA==
|
||||
|
||||
"@xterm/addon-search@0.14.0-beta.19":
|
||||
version "0.14.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.14.0-beta.19.tgz#2bc13378765f0d5e72d9bcb7887e23eee31de7eb"
|
||||
integrity sha512-Y1pPdtdZj0xRQ/Is4jdO0dyZe+uM6AhWi3v2U4sdJmhz2mxVe/HAKBHkx6tyfMtX9ge/9ZYajd/Sy8rkjIFdmQ==
|
||||
"@xterm/addon-search@0.14.0-beta.20":
|
||||
version "0.14.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.14.0-beta.20.tgz#cac366b1be1eb02cf9fe9537933f26f227d030c8"
|
||||
integrity sha512-1LOL/OzWSrCBpndiBeeE2S1rxtKKgU1ucYFSG3P68W0J4VQz/Ksci1BgDKsgspj9jzpsGhdql3zwa5WEM7n4Pg==
|
||||
|
||||
"@xterm/addon-serialize@0.12.0-beta.19":
|
||||
version "0.12.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.12.0-beta.19.tgz#465a9525a420b8f0d12eed419d6e051f9a887814"
|
||||
integrity sha512-3v6a4/4gxAwoyJsBp6vJBofymgTH8paSHl8K2uQfFuLosOavNyCtrNPiNne7tpppK6t8zCDJ/mVrXNptz4Mlsg==
|
||||
"@xterm/addon-serialize@0.12.0-beta.20":
|
||||
version "0.12.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.12.0-beta.20.tgz#5fe126194ff4dc466b92a0946e081e039a14ad21"
|
||||
integrity sha512-GdRCQDjLyVNBxCFnhfCWsMmuqv2PryUkOaNl4z5MqB5lBUkiEnRNY0u/s5f34+2zrijp3h0O/f9JDLW4gSUQgw==
|
||||
|
||||
"@xterm/addon-unicode11@0.7.0-beta.19":
|
||||
version "0.7.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.7.0-beta.19.tgz#0e81775f84346a97c657fb4cd7702de1cfe4b83f"
|
||||
integrity sha512-U5O+JLklO4qtptWAWUw14QRWdalLl0bFAQxLKuTtDmusgfn33pNDRD6RH3R+IHhO2e6svAwrw27OcCEcdn0AJQ==
|
||||
"@xterm/addon-unicode11@0.7.0-beta.20":
|
||||
version "0.7.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.7.0-beta.20.tgz#5d3c97320898dd6766f2dc127deb4f071c8698c2"
|
||||
integrity sha512-4/uwJ6lV/xJplT7hJc7sO4Im4XNvEXHnUEFIs03FFp8ZUfu3U6wcBk6/GoKMwJKJtGVNxotiD6ZzJ5v8IBH6nA==
|
||||
|
||||
"@xterm/addon-webgl@0.17.0-beta.19":
|
||||
version "0.17.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.17.0-beta.19.tgz#fefe44c20b4d4d070363e03ae33087505a88807e"
|
||||
integrity sha512-L59l9Cd4KTMCwnw2HPi/cUgH4iL5dK7VERK/wSTWEGYMOi3WGTmXqsg8ftfR2jFC9P33eqYNVJDmbr+pF0XsDg==
|
||||
"@xterm/addon-webgl@0.17.0-beta.20":
|
||||
version "0.17.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.17.0-beta.20.tgz#443845ac5ac755cf762b105ed237b30426b07137"
|
||||
integrity sha512-iqvXNSTfKIcO9FBraNwdO/ixPrTHok8CBN/wjlnGLv0ZMc4zLAiKE8+PHyg9ZY38QJfS+4Ouo8KsuZwoOYfnNA==
|
||||
|
||||
"@xterm/xterm@5.4.0-beta.19":
|
||||
version "5.4.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.4.0-beta.19.tgz#5c9c5cacd0cf2a0719086ce03ab035480b1cfd96"
|
||||
integrity sha512-Rx/Y/y3YGjpiW6IUq8UlE6qrTYuUlEfpVg/BS6kIPr8/cUOchE1fsKWCMxz/u2bIyQyEovRi892iYcQJ4scstw==
|
||||
"@xterm/xterm@5.4.0-beta.20":
|
||||
version "5.4.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.4.0-beta.20.tgz#28bbbbc73eceb6ef3e1e095de195cf849d0cbfb6"
|
||||
integrity sha512-nkY91qBy5pe1HlW9LOoLcyG6v4teEsliEtUVshAO42NrJDaPniSn28O5m5832UjZOdjLCY58QlcBkZUquODGrQ==
|
||||
|
||||
jschardet@3.0.0:
|
||||
version "3.0.0"
|
||||
|
||||
+32
-32
@@ -114,45 +114,45 @@
|
||||
resolved "https://registry.yarnpkg.com/@vscode/windows-registry/-/windows-registry-1.1.0.tgz#03dace7c29c46f658588b9885b9580e453ad21f9"
|
||||
integrity sha512-5AZzuWJpGscyiMOed0IuyEwt6iKmV5Us7zuwCDCFYMIq7tsvooO9BUiciywsvuthGz6UG4LSpeDeCxvgMVhnIw==
|
||||
|
||||
"@xterm/addon-canvas@0.6.0-beta.19":
|
||||
version "0.6.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.6.0-beta.19.tgz#5f8896b884d2a558a28eb197f7f4766ac37d33a0"
|
||||
integrity sha512-i+26DqYgI/PZViCiEK4Vu8c4Fi5J0i+TwnFfBDLcumHH07Al1uRd5BRaVg/i93vk6bRyEIOiOiIToXSf37ov/w==
|
||||
"@xterm/addon-canvas@0.6.0-beta.20":
|
||||
version "0.6.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.6.0-beta.20.tgz#078dddef70caf880b2cb121fdda37d301fc13156"
|
||||
integrity sha512-tHhsuqElE7LNiDJPbZzgVpmbcG2Dk6i2vh1EI+DzSByUWScDqLoeJbVPE5Xd2UW2garo24lxErpnIAlsytcA3A==
|
||||
|
||||
"@xterm/addon-image@0.7.0-beta.17":
|
||||
version "0.7.0-beta.17"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.7.0-beta.17.tgz#bc3d6f09619ef1f3b68c7009d29ee14003a26b30"
|
||||
integrity sha512-nnHVoYVoh+CpT4FQN/ALKesr96YvdVNUzQRQo4aAARUKst5DFaHQX9Yn/qLDN5s0WCqI3bgIEo8UAakfHITumA==
|
||||
"@xterm/addon-image@0.7.0-beta.18":
|
||||
version "0.7.0-beta.18"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.7.0-beta.18.tgz#588ea2d0841cff48c63bde1bfcdf56e9494dc6af"
|
||||
integrity sha512-+HQ+IBmHPelzjRJ5zO3XkjbeQNr2Zrf5wAlbPhy4EGSD0mDCqHJSfzZ8wKrhx7t8qpfiA8eTpWu/M76WsEnlnA==
|
||||
|
||||
"@xterm/addon-search@0.14.0-beta.19":
|
||||
version "0.14.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.14.0-beta.19.tgz#2bc13378765f0d5e72d9bcb7887e23eee31de7eb"
|
||||
integrity sha512-Y1pPdtdZj0xRQ/Is4jdO0dyZe+uM6AhWi3v2U4sdJmhz2mxVe/HAKBHkx6tyfMtX9ge/9ZYajd/Sy8rkjIFdmQ==
|
||||
"@xterm/addon-search@0.14.0-beta.20":
|
||||
version "0.14.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.14.0-beta.20.tgz#cac366b1be1eb02cf9fe9537933f26f227d030c8"
|
||||
integrity sha512-1LOL/OzWSrCBpndiBeeE2S1rxtKKgU1ucYFSG3P68W0J4VQz/Ksci1BgDKsgspj9jzpsGhdql3zwa5WEM7n4Pg==
|
||||
|
||||
"@xterm/addon-serialize@0.12.0-beta.19":
|
||||
version "0.12.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.12.0-beta.19.tgz#465a9525a420b8f0d12eed419d6e051f9a887814"
|
||||
integrity sha512-3v6a4/4gxAwoyJsBp6vJBofymgTH8paSHl8K2uQfFuLosOavNyCtrNPiNne7tpppK6t8zCDJ/mVrXNptz4Mlsg==
|
||||
"@xterm/addon-serialize@0.12.0-beta.20":
|
||||
version "0.12.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.12.0-beta.20.tgz#5fe126194ff4dc466b92a0946e081e039a14ad21"
|
||||
integrity sha512-GdRCQDjLyVNBxCFnhfCWsMmuqv2PryUkOaNl4z5MqB5lBUkiEnRNY0u/s5f34+2zrijp3h0O/f9JDLW4gSUQgw==
|
||||
|
||||
"@xterm/addon-unicode11@0.7.0-beta.19":
|
||||
version "0.7.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.7.0-beta.19.tgz#0e81775f84346a97c657fb4cd7702de1cfe4b83f"
|
||||
integrity sha512-U5O+JLklO4qtptWAWUw14QRWdalLl0bFAQxLKuTtDmusgfn33pNDRD6RH3R+IHhO2e6svAwrw27OcCEcdn0AJQ==
|
||||
"@xterm/addon-unicode11@0.7.0-beta.20":
|
||||
version "0.7.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.7.0-beta.20.tgz#5d3c97320898dd6766f2dc127deb4f071c8698c2"
|
||||
integrity sha512-4/uwJ6lV/xJplT7hJc7sO4Im4XNvEXHnUEFIs03FFp8ZUfu3U6wcBk6/GoKMwJKJtGVNxotiD6ZzJ5v8IBH6nA==
|
||||
|
||||
"@xterm/addon-webgl@0.17.0-beta.19":
|
||||
version "0.17.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.17.0-beta.19.tgz#fefe44c20b4d4d070363e03ae33087505a88807e"
|
||||
integrity sha512-L59l9Cd4KTMCwnw2HPi/cUgH4iL5dK7VERK/wSTWEGYMOi3WGTmXqsg8ftfR2jFC9P33eqYNVJDmbr+pF0XsDg==
|
||||
"@xterm/addon-webgl@0.17.0-beta.20":
|
||||
version "0.17.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.17.0-beta.20.tgz#443845ac5ac755cf762b105ed237b30426b07137"
|
||||
integrity sha512-iqvXNSTfKIcO9FBraNwdO/ixPrTHok8CBN/wjlnGLv0ZMc4zLAiKE8+PHyg9ZY38QJfS+4Ouo8KsuZwoOYfnNA==
|
||||
|
||||
"@xterm/headless@5.4.0-beta.19":
|
||||
version "5.4.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/headless/-/headless-5.4.0-beta.19.tgz#e84a7cebda581273032f82fc8c53941204bc7f93"
|
||||
integrity sha512-y7Ne2G/Tgn6bHr14eBHqcq5gGFFCHKGBzmXTExT1Z4Fb6ofPACPWAo60S5B6uh49W4Ts13gQYZ5C0XEvHud0Eg==
|
||||
"@xterm/headless@5.4.0-beta.20":
|
||||
version "5.4.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/headless/-/headless-5.4.0-beta.20.tgz#af26d3d0e2cdd615ccfac4a8660181fee19898fd"
|
||||
integrity sha512-H/as1d67J43/CB8xt1Yg/eJMbq1yopwG1bDBKdsf2ro8A1PmJFXNzaDB+wSgoH42fCusSpLJvXtUvDLtqfvBTg==
|
||||
|
||||
"@xterm/xterm@5.4.0-beta.19":
|
||||
version "5.4.0-beta.19"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.4.0-beta.19.tgz#5c9c5cacd0cf2a0719086ce03ab035480b1cfd96"
|
||||
integrity sha512-Rx/Y/y3YGjpiW6IUq8UlE6qrTYuUlEfpVg/BS6kIPr8/cUOchE1fsKWCMxz/u2bIyQyEovRi892iYcQJ4scstw==
|
||||
"@xterm/xterm@5.4.0-beta.20":
|
||||
version "5.4.0-beta.20"
|
||||
resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.4.0-beta.20.tgz#28bbbbc73eceb6ef3e1e095de195cf849d0cbfb6"
|
||||
integrity sha512-nkY91qBy5pe1HlW9LOoLcyG6v4teEsliEtUVshAO42NrJDaPniSn28O5m5832UjZOdjLCY58QlcBkZUquODGrQ==
|
||||
|
||||
agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0:
|
||||
version "7.1.0"
|
||||
|
||||
@@ -3,52 +3,67 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { $window } from 'vs/base/browser/window';
|
||||
import { $window, CodeWindow, mainWindow } from 'vs/base/browser/window';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, markAsSingleton } from 'vs/base/common/lifecycle';
|
||||
|
||||
class WindowManager {
|
||||
|
||||
public static readonly INSTANCE = new WindowManager();
|
||||
static readonly INSTANCE = new WindowManager();
|
||||
|
||||
// --- Zoom Level
|
||||
private _zoomLevel: number = 0;
|
||||
|
||||
public getZoomLevel(): number {
|
||||
return this._zoomLevel;
|
||||
private readonly mapWindowIdToZoomLevel = new Map<number, number>();
|
||||
|
||||
private readonly _onDidChangeZoomLevel = new Emitter<number>();
|
||||
readonly onDidChangeZoomLevel = this._onDidChangeZoomLevel.event;
|
||||
|
||||
getZoomLevel(targetWindow: Window): number {
|
||||
return this.mapWindowIdToZoomLevel.get(this.getWindowId(targetWindow)) ?? 0;
|
||||
}
|
||||
public setZoomLevel(zoomLevel: number): void {
|
||||
if (this._zoomLevel === zoomLevel) {
|
||||
setZoomLevel(zoomLevel: number, targetWindow: Window): void {
|
||||
if (this.getZoomLevel(targetWindow) === zoomLevel) {
|
||||
return;
|
||||
}
|
||||
this._zoomLevel = zoomLevel;
|
||||
|
||||
const targetWindowId = this.getWindowId(targetWindow);
|
||||
this.mapWindowIdToZoomLevel.set(targetWindowId, zoomLevel);
|
||||
this._onDidChangeZoomLevel.fire(targetWindowId);
|
||||
}
|
||||
|
||||
// --- Zoom Factor
|
||||
private _zoomFactor: number = 1;
|
||||
|
||||
public getZoomFactor(): number {
|
||||
return this._zoomFactor;
|
||||
private readonly mapWindowIdToZoomFactor = new Map<number, number>();
|
||||
|
||||
getZoomFactor(targetWindow: Window): number {
|
||||
return this.mapWindowIdToZoomFactor.get(this.getWindowId(targetWindow)) ?? 1;
|
||||
}
|
||||
public setZoomFactor(zoomFactor: number): void {
|
||||
this._zoomFactor = zoomFactor;
|
||||
setZoomFactor(zoomFactor: number, targetWindow: Window): void {
|
||||
this.mapWindowIdToZoomFactor.set(this.getWindowId(targetWindow), zoomFactor);
|
||||
}
|
||||
|
||||
// --- Fullscreen
|
||||
private _fullscreen: boolean = false;
|
||||
private readonly _onDidChangeFullscreen = new Emitter<void>();
|
||||
|
||||
public readonly onDidChangeFullscreen: Event<void> = this._onDidChangeFullscreen.event;
|
||||
public setFullscreen(fullscreen: boolean): void {
|
||||
if (this._fullscreen === fullscreen) {
|
||||
private readonly _onDidChangeFullscreen = new Emitter<number>();
|
||||
readonly onDidChangeFullscreen = this._onDidChangeFullscreen.event;
|
||||
|
||||
private readonly mapWindowIdToFullScreen = new Map<number, boolean>();
|
||||
|
||||
setFullscreen(fullscreen: boolean, targetWindow: Window): void {
|
||||
if (this.isFullscreen(targetWindow) === fullscreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fullscreen = fullscreen;
|
||||
this._onDidChangeFullscreen.fire();
|
||||
const windowId = this.getWindowId(targetWindow);
|
||||
this.mapWindowIdToFullScreen.set(windowId, fullscreen);
|
||||
this._onDidChangeFullscreen.fire(windowId);
|
||||
}
|
||||
public isFullscreen(): boolean {
|
||||
return this._fullscreen;
|
||||
isFullscreen(targetWindow: Window): boolean {
|
||||
return !!this.mapWindowIdToFullScreen.get(this.getWindowId(targetWindow));
|
||||
}
|
||||
|
||||
private getWindowId(targetWindow: Window): number {
|
||||
return (targetWindow as CodeWindow).vscodeWindowId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +73,7 @@ class WindowManager {
|
||||
class DevicePixelRatioMonitor extends Disposable {
|
||||
|
||||
private readonly _onDidChange = this._register(new Emitter<void>());
|
||||
public readonly onDidChange = this._onDidChange.event;
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
private readonly _listener: () => void;
|
||||
private _mediaQueryList: MediaQueryList | null;
|
||||
@@ -86,11 +101,11 @@ class DevicePixelRatioMonitor extends Disposable {
|
||||
class PixelRatioImpl extends Disposable {
|
||||
|
||||
private readonly _onDidChange = this._register(new Emitter<number>());
|
||||
public readonly onDidChange = this._onDidChange.event;
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
private _value: number;
|
||||
|
||||
public get value(): number {
|
||||
get value(): number {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
@@ -131,21 +146,21 @@ class PixelRatioFacade {
|
||||
/**
|
||||
* Get the current value.
|
||||
*/
|
||||
public get value(): number {
|
||||
get value(): number {
|
||||
return this._getOrCreatePixelRatioMonitor().value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for changes.
|
||||
*/
|
||||
public get onDidChange(): Event<number> {
|
||||
get onDidChange(): Event<number> {
|
||||
return this._getOrCreatePixelRatioMonitor().onDidChange;
|
||||
}
|
||||
}
|
||||
|
||||
export function addMatchMediaChangeListener(query: string | MediaQueryList, callback: (this: MediaQueryList, ev: MediaQueryListEvent) => any): void {
|
||||
export function addMatchMediaChangeListener(targetWindow: Window, query: string | MediaQueryList, callback: (this: MediaQueryList, ev: MediaQueryListEvent) => any): void {
|
||||
if (typeof query === 'string') {
|
||||
query = $window.matchMedia(query);
|
||||
query = targetWindow.matchMedia(query);
|
||||
}
|
||||
query.addEventListener('change', callback);
|
||||
}
|
||||
@@ -160,26 +175,27 @@ export function addMatchMediaChangeListener(query: string | MediaQueryList, call
|
||||
export const PixelRatio = new PixelRatioFacade();
|
||||
|
||||
/** A zoom index, e.g. 1, 2, 3 */
|
||||
export function setZoomLevel(zoomLevel: number): void {
|
||||
WindowManager.INSTANCE.setZoomLevel(zoomLevel);
|
||||
export function setZoomLevel(zoomLevel: number, targetWindow: Window): void {
|
||||
WindowManager.INSTANCE.setZoomLevel(zoomLevel, targetWindow);
|
||||
}
|
||||
export function getZoomLevel(): number {
|
||||
return WindowManager.INSTANCE.getZoomLevel();
|
||||
export function getZoomLevel(targetWindow: Window): number {
|
||||
return WindowManager.INSTANCE.getZoomLevel(targetWindow);
|
||||
}
|
||||
export const onDidChangeZoomLevel = WindowManager.INSTANCE.onDidChangeZoomLevel;
|
||||
|
||||
/** The zoom scale for an index, e.g. 1, 1.2, 1.4 */
|
||||
export function getZoomFactor(): number {
|
||||
return WindowManager.INSTANCE.getZoomFactor();
|
||||
export function getZoomFactor(targetWindow: Window): number {
|
||||
return WindowManager.INSTANCE.getZoomFactor(targetWindow);
|
||||
}
|
||||
export function setZoomFactor(zoomFactor: number): void {
|
||||
WindowManager.INSTANCE.setZoomFactor(zoomFactor);
|
||||
export function setZoomFactor(zoomFactor: number, targetWindow: Window): void {
|
||||
WindowManager.INSTANCE.setZoomFactor(zoomFactor, targetWindow);
|
||||
}
|
||||
|
||||
export function setFullscreen(fullscreen: boolean): void {
|
||||
WindowManager.INSTANCE.setFullscreen(fullscreen);
|
||||
export function setFullscreen(fullscreen: boolean, targetWindow: Window): void {
|
||||
WindowManager.INSTANCE.setFullscreen(fullscreen, targetWindow);
|
||||
}
|
||||
export function isFullscreen(): boolean {
|
||||
return WindowManager.INSTANCE.isFullscreen();
|
||||
export function isFullscreen(targetWindow: Window): boolean {
|
||||
return WindowManager.INSTANCE.isFullscreen(targetWindow);
|
||||
}
|
||||
export const onDidChangeFullscreen = WindowManager.INSTANCE.onDidChangeFullscreen;
|
||||
|
||||
@@ -194,11 +210,11 @@ export const isElectron = (userAgent.indexOf('Electron/') >= 0);
|
||||
export const isAndroid = (userAgent.indexOf('Android') >= 0);
|
||||
|
||||
let standalone = false;
|
||||
if ($window.matchMedia) {
|
||||
const standaloneMatchMedia = $window.matchMedia('(display-mode: standalone) or (display-mode: window-controls-overlay)');
|
||||
const fullScreenMatchMedia = $window.matchMedia('(display-mode: fullscreen)');
|
||||
if (typeof mainWindow.matchMedia === 'function') {
|
||||
const standaloneMatchMedia = mainWindow.matchMedia('(display-mode: standalone) or (display-mode: window-controls-overlay)');
|
||||
const fullScreenMatchMedia = mainWindow.matchMedia('(display-mode: fullscreen)');
|
||||
standalone = standaloneMatchMedia.matches;
|
||||
addMatchMediaChangeListener(standaloneMatchMedia, ({ matches }) => {
|
||||
addMatchMediaChangeListener(mainWindow, standaloneMatchMedia, ({ matches }) => {
|
||||
// entering fullscreen would change standaloneMatchMedia.matches to false
|
||||
// if standalone is true (running as PWA) and entering fullscreen, skip this change
|
||||
if (standalone && fullScreenMatchMedia.matches) {
|
||||
|
||||
@@ -261,7 +261,7 @@ export let runAtThisOrScheduleAtNextAnimationFrame: (targetWindow: Window, runne
|
||||
*/
|
||||
export let scheduleAtNextAnimationFrame: (targetWindow: Window, runner: () => void, priority?: number) => IDisposable;
|
||||
|
||||
export function disposableWindowInterval(targetWindow: Window & typeof globalThis, handler: () => void | boolean /* stop interval */ | Promise<unknown>, interval: number, iterations?: number): IDisposable {
|
||||
export function disposableWindowInterval(targetWindow: Window, handler: () => void | boolean /* stop interval */ | Promise<unknown>, interval: number, iterations?: number): IDisposable {
|
||||
let iteration = 0;
|
||||
const timer = targetWindow.setInterval(() => {
|
||||
iteration++;
|
||||
|
||||
@@ -17,11 +17,12 @@ import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { KeyCode, KeyMod, ScanCode, ScanCodeUtils } from 'vs/base/common/keyCodes';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keybindings';
|
||||
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import 'vs/css!./menubar';
|
||||
import * as nls from 'vs/nls';
|
||||
import { mainWindow } from 'vs/base/browser/window';
|
||||
|
||||
const $ = DOM.$;
|
||||
|
||||
@@ -87,6 +88,8 @@ export class MenuBar extends Disposable {
|
||||
private numMenusShown: number = 0;
|
||||
private overflowLayoutScheduled: IDisposable | undefined = undefined;
|
||||
|
||||
private readonly menuDisposables = this._register(new DisposableStore());
|
||||
|
||||
constructor(private container: HTMLElement, private options: IMenuBarOptions, private menuStyle: IMenuStyles) {
|
||||
super();
|
||||
|
||||
@@ -751,6 +754,7 @@ export class MenuBar extends Disposable {
|
||||
}
|
||||
|
||||
if (this.focusedMenu) {
|
||||
this.cleanupCustomMenu();
|
||||
this.showCustomMenu(this.focusedMenu.index, this.openedViaKeyboard);
|
||||
}
|
||||
break;
|
||||
@@ -783,7 +787,7 @@ export class MenuBar extends Disposable {
|
||||
private setUnfocusedState(): void {
|
||||
if (this.options.visibility === 'toggle' || this.options.visibility === 'hidden') {
|
||||
this.focusState = MenubarState.HIDDEN;
|
||||
} else if (this.options.visibility === 'classic' && browser.isFullscreen()) {
|
||||
} else if (this.options.visibility === 'classic' && browser.isFullscreen(mainWindow)) {
|
||||
this.focusState = MenubarState.HIDDEN;
|
||||
} else {
|
||||
this.focusState = MenubarState.VISIBLE;
|
||||
@@ -985,6 +989,7 @@ export class MenuBar extends Disposable {
|
||||
|
||||
this.focusedMenu = { index: this.focusedMenu.index };
|
||||
}
|
||||
this.menuDisposables.clear();
|
||||
}
|
||||
|
||||
private showCustomMenu(menuIndex: number, selectFirst = true): void {
|
||||
@@ -1025,9 +1030,8 @@ export class MenuBar extends Disposable {
|
||||
useEventAsContext: true
|
||||
};
|
||||
|
||||
const menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions, this.menuStyle));
|
||||
|
||||
this._register(menuWidget.onDidCancel(() => {
|
||||
const menuWidget = this.menuDisposables.add(new Menu(menuHolder, customMenu.actions, menuOptions, this.menuStyle));
|
||||
this.menuDisposables.add(menuWidget.onDidCancel(() => {
|
||||
this.focusState = MenubarState.FOCUSED;
|
||||
}));
|
||||
|
||||
|
||||
@@ -87,7 +87,8 @@ export class MouseWheelClassifier {
|
||||
}
|
||||
|
||||
public acceptStandardWheelEvent(e: StandardWheelEvent): void {
|
||||
const osZoomFactor = dom.getWindow(e.browserEvent).devicePixelRatio / getZoomFactor();
|
||||
const targetWindow = dom.getWindow(e.browserEvent);
|
||||
const osZoomFactor = targetWindow.devicePixelRatio / getZoomFactor(targetWindow);
|
||||
if (platform.isWindows || platform.isLinux) {
|
||||
// On Windows and Linux, the incoming delta events are multiplied with the OS zoom factor.
|
||||
// The OS zoom factor can be reverse engineered by using the device pixel ratio and the configured zoom factor into account.
|
||||
|
||||
@@ -516,6 +516,13 @@ export class CodeApplication extends Disposable {
|
||||
|
||||
validatedIpcMain.on('vscode:reloadWindow', event => event.sender.reload());
|
||||
|
||||
validatedIpcMain.handle('vscode:notifyZoomLevel', async (event, zoomLevel: number | undefined) => {
|
||||
const window = this.windowsMainService?.getWindowById(event.sender.id);
|
||||
if (window) {
|
||||
window.notifyZoomLevel(zoomLevel);
|
||||
}
|
||||
});
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ export class IssueReporter extends Disposable {
|
||||
|
||||
this.setUpTypes();
|
||||
this.setEventHandlers();
|
||||
applyZoom(configuration.data.zoomLevel);
|
||||
applyZoom(configuration.data.zoomLevel, mainWindow);
|
||||
this.applyStyles(configuration.data.styles);
|
||||
this.handleExtensionData(configuration.data.enabledExtensions);
|
||||
this.updateExperimentsInfo(configuration.data.experiments);
|
||||
@@ -435,12 +435,12 @@ export class IssueReporter extends Disposable {
|
||||
|
||||
// Cmd/Ctrl + zooms in
|
||||
if (cmdOrCtrlKey && e.keyCode === 187) {
|
||||
zoomIn();
|
||||
zoomIn(mainWindow);
|
||||
}
|
||||
|
||||
// Cmd/Ctrl - zooms out
|
||||
if (cmdOrCtrlKey && e.keyCode === 189) {
|
||||
zoomOut();
|
||||
zoomOut(mainWindow);
|
||||
}
|
||||
|
||||
// With latest electron upgrade, cmd+a is no longer propagating correctly for inputs in this window on mac
|
||||
|
||||
@@ -288,12 +288,12 @@ class ProcessExplorer {
|
||||
|
||||
// Cmd/Ctrl + zooms in
|
||||
if (cmdOrCtrlKey && e.keyCode === 187) {
|
||||
zoomIn();
|
||||
zoomIn(mainWindow);
|
||||
}
|
||||
|
||||
// Cmd/Ctrl - zooms out
|
||||
if (cmdOrCtrlKey && e.keyCode === 189) {
|
||||
zoomOut();
|
||||
zoomOut(mainWindow);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -595,7 +595,7 @@ export function startup(configuration: ProcessExplorerWindowConfiguration): void
|
||||
const platformClass = configuration.data.platform === 'win32' ? 'windows' : configuration.data.platform === 'linux' ? 'linux' : 'mac';
|
||||
mainWindow.document.body.classList.add(platformClass); // used by our fonts
|
||||
createCodiconStyleSheet();
|
||||
applyZoom(configuration.data.zoomLevel);
|
||||
applyZoom(configuration.data.zoomLevel, mainWindow);
|
||||
|
||||
new ProcessExplorer(configuration.windowId, configuration.data);
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ export class ElementSizeObserver extends Disposable {
|
||||
// Otherwise we will postpone to the next animation frame.
|
||||
// We'll use `observeContentRect` to store the content rect we received.
|
||||
|
||||
let observeContentRect: DOMRectReadOnly | null = null;
|
||||
let observedDimenstion: IDimension | null = null;
|
||||
const observeNow = () => {
|
||||
if (observeContentRect) {
|
||||
this.observe({ width: observeContentRect.width, height: observeContentRect.height });
|
||||
if (observedDimenstion) {
|
||||
this.observe({ width: observedDimenstion.width, height: observedDimenstion.height });
|
||||
} else {
|
||||
this.observe();
|
||||
}
|
||||
@@ -75,7 +75,11 @@ export class ElementSizeObserver extends Disposable {
|
||||
};
|
||||
|
||||
this._resizeObserver = new ResizeObserver((entries) => {
|
||||
observeContentRect = (entries && entries[0] && entries[0].contentRect ? entries[0].contentRect : null);
|
||||
if (entries && entries[0] && entries[0].contentRect) {
|
||||
observedDimenstion = { width: entries[0].contentRect.width, height: entries[0].contentRect.height };
|
||||
} else {
|
||||
observedDimenstion = null;
|
||||
}
|
||||
shouldObserve = true;
|
||||
update();
|
||||
});
|
||||
|
||||
+20
-3
@@ -159,10 +159,17 @@ export class DiffEditorViewZones extends Disposable {
|
||||
|
||||
const deletedCodeLineBreaksComputer = !renderSideBySide ? this._editors.modified._getViewModel()?.createLineBreaksComputer() : undefined;
|
||||
if (deletedCodeLineBreaksComputer) {
|
||||
const originalModel = this._editors.original.getModel()!;
|
||||
for (const a of alignmentsVal) {
|
||||
if (a.diff) {
|
||||
for (let i = a.originalRange.startLineNumber; i < a.originalRange.endLineNumberExclusive; i++) {
|
||||
deletedCodeLineBreaksComputer?.addRequest(this._editors.original.getModel()!.getLineContent(i), null, null);
|
||||
// `i` can be out of bound when the diff has not been updated yet.
|
||||
// In this case, we do an early return.
|
||||
// TODO@hediet: Fix this by applying the edit directly to the diff model, so that the diff is always valid.
|
||||
if (i > originalModel.getLineCount()) {
|
||||
return { orig: origViewZones, mod: modViewZones };
|
||||
}
|
||||
deletedCodeLineBreaksComputer?.addRequest(originalModel.getLineContent(i), null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,8 +193,15 @@ export class DiffEditorViewZones extends Disposable {
|
||||
|
||||
const deletedCodeDomNode = document.createElement('div');
|
||||
deletedCodeDomNode.classList.add('view-lines', 'line-delete', 'monaco-mouse-cursor-text');
|
||||
const originalModel = this._editors.original.getModel()!;
|
||||
// `a.originalRange` can be out of bound when the diff has not been updated yet.
|
||||
// In this case, we do an early return.
|
||||
// TODO@hediet: Fix this by applying the edit directly to the diff model, so that the diff is always valid.
|
||||
if (a.originalRange.endLineNumberExclusive - 1 > originalModel.getLineCount()) {
|
||||
return { orig: origViewZones, mod: modViewZones };
|
||||
}
|
||||
const source = new LineSource(
|
||||
a.originalRange.mapToLineArray(l => this._editors.original.getModel()!.tokenization.getLineTokens(l)),
|
||||
a.originalRange.mapToLineArray(l => originalModel.tokenization.getLineTokens(l)),
|
||||
a.originalRange.mapToLineArray(_ => lineBreakData[lineBreakDataIdx++]),
|
||||
mightContainNonBasicASCII,
|
||||
mightContainRTL,
|
||||
@@ -551,7 +565,10 @@ function computeRangeAlignment(
|
||||
// There is some unmodified text on this line before the diff
|
||||
emitAlignment(i.originalRange.startLineNumber, i.modifiedRange.startLineNumber);
|
||||
}
|
||||
if (i.originalRange.endColumn < originalEditor.getModel()!.getLineMaxColumn(i.originalRange.endLineNumber)) {
|
||||
const originalModel = originalEditor.getModel()!;
|
||||
// When the diff is invalid, the ranges might be out of bounds (this should be fixed in the diff model by applying edits directly).
|
||||
const maxColumn = i.originalRange.endLineNumber <= originalModel.getLineCount() ? originalModel.getLineMaxColumn(i.originalRange.endLineNumber) : Number.MAX_SAFE_INTEGER;
|
||||
if (i.originalRange.endColumn < maxColumn) {
|
||||
// // There is some unmodified text on this line after the diff
|
||||
emitAlignment(i.originalRange.endLineNumber, i.modifiedRange.endLineNumber);
|
||||
}
|
||||
|
||||
@@ -410,7 +410,8 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
return null;
|
||||
}
|
||||
|
||||
return EditorSimpleWorker.computeDiff(original, modified, options, algorithm);
|
||||
const result = EditorSimpleWorker.computeDiff(original, modified, options, algorithm);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static computeDiff(originalTextModel: ICommonModel | ITextModel, modifiedTextModel: ICommonModel | ITextModel, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): IDiffComputationResult {
|
||||
|
||||
@@ -261,7 +261,7 @@ export class StandaloneThemeService extends Disposable implements IStandaloneThe
|
||||
this._updateCSS();
|
||||
}));
|
||||
|
||||
addMatchMediaChangeListener('(forced-colors: active)', () => {
|
||||
addMatchMediaChangeListener(mainWindow, '(forced-colors: active)', () => {
|
||||
this._onOSSchemeChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { BrowserWindow, WebContents } from 'electron';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
|
||||
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IStateService } from 'vs/platform/state/node/state';
|
||||
import { IBaseWindow } from 'vs/platform/window/electron-main/window';
|
||||
@@ -33,12 +34,11 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow {
|
||||
@IEnvironmentMainService environmentMainService: IEnvironmentMainService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IStateService stateService: IStateService
|
||||
@IStateService stateService: IStateService,
|
||||
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService
|
||||
) {
|
||||
super(configurationService, stateService, environmentMainService);
|
||||
|
||||
contents.removeAllListeners('devtools-reload-page'); // remove built in listener as aux windows have no reload
|
||||
|
||||
// Try to claim window
|
||||
this.tryClaimWindow();
|
||||
}
|
||||
@@ -61,6 +61,9 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow {
|
||||
|
||||
// Disable Menu
|
||||
window.setMenu(null);
|
||||
|
||||
// Lifecycle
|
||||
this.lifecycleMainService.registerAuxWindow(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface IAuxiliaryWindowsMainService {
|
||||
|
||||
readonly onDidMaximizeWindow: Event<IAuxiliaryWindow>;
|
||||
readonly onDidUnmaximizeWindow: Event<IAuxiliaryWindow>;
|
||||
readonly onDidChangeFullScreen: Event<IAuxiliaryWindow>;
|
||||
readonly onDidTriggerSystemContextMenu: Event<{ readonly window: IAuxiliaryWindow; readonly x: number; readonly y: number }>;
|
||||
|
||||
createWindow(): BrowserWindowConstructorOptions;
|
||||
|
||||
@@ -24,6 +24,9 @@ export class AuxiliaryWindowsMainService extends Disposable implements IAuxiliar
|
||||
private readonly _onDidUnmaximizeWindow = this._register(new Emitter<IAuxiliaryWindow>());
|
||||
readonly onDidUnmaximizeWindow = this._onDidUnmaximizeWindow.event;
|
||||
|
||||
private readonly _onDidChangeFullScreen = this._register(new Emitter<IAuxiliaryWindow>());
|
||||
readonly onDidChangeFullScreen = this._onDidChangeFullScreen.event;
|
||||
|
||||
private readonly _onDidTriggerSystemContextMenu = this._register(new Emitter<{ window: IAuxiliaryWindow; x: number; y: number }>());
|
||||
readonly onDidTriggerSystemContextMenu = this._onDidTriggerSystemContextMenu.event;
|
||||
|
||||
@@ -85,6 +88,8 @@ export class AuxiliaryWindowsMainService extends Disposable implements IAuxiliar
|
||||
|
||||
disposables.add(auxiliaryWindow.onDidMaximize(() => this._onDidMaximizeWindow.fire(auxiliaryWindow)));
|
||||
disposables.add(auxiliaryWindow.onDidUnmaximize(() => this._onDidUnmaximizeWindow.fire(auxiliaryWindow)));
|
||||
disposables.add(auxiliaryWindow.onDidEnterFullScreen(() => this._onDidChangeFullScreen.fire(auxiliaryWindow)));
|
||||
disposables.add(auxiliaryWindow.onDidLeaveFullScreen(() => this._onDidChangeFullScreen.fire(auxiliaryWindow)));
|
||||
disposables.add(auxiliaryWindow.onDidTriggerSystemContextMenu(({ x, y }) => this._onDidTriggerSystemContextMenu.fire({ window: auxiliaryWindow, x, y })));
|
||||
|
||||
Event.once(auxiliaryWindow.onDidClose)(() => disposables.dispose());
|
||||
|
||||
@@ -18,6 +18,7 @@ import { IStateService } from 'vs/platform/state/node/state';
|
||||
import { ICodeWindow, LoadReason, UnloadReason } from 'vs/platform/window/electron-main/window';
|
||||
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
|
||||
import { IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow';
|
||||
|
||||
export const ILifecycleMainService = createDecorator<ILifecycleMainService>('lifecycleMainService');
|
||||
|
||||
@@ -131,6 +132,11 @@ export interface ILifecycleMainService {
|
||||
*/
|
||||
registerWindow(window: ICodeWindow): void;
|
||||
|
||||
/**
|
||||
* Make a `IAuxiliaryWindow` known to the lifecycle main service.
|
||||
*/
|
||||
registerAuxWindow(auxWindow: IAuxiliaryWindow): void;
|
||||
|
||||
/**
|
||||
* Reload a window. All lifecycle event handlers are triggered.
|
||||
*/
|
||||
@@ -472,6 +478,34 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
|
||||
});
|
||||
}
|
||||
|
||||
registerAuxWindow(auxWindow: IAuxiliaryWindow): void {
|
||||
const win = assertIsDefined(auxWindow.win);
|
||||
|
||||
win.on('close', e => {
|
||||
this.trace(`Lifecycle#auxWindow.on('close') - window ID ${auxWindow.id}`);
|
||||
|
||||
if (this._quitRequested) {
|
||||
this.trace(`Lifecycle#auxWindow.on('close') - preventDefault() because quit requested`);
|
||||
|
||||
// When quit is requested, Electron will close all
|
||||
// auxiliary windows before closing the main windows.
|
||||
// This prevents us from storing the auxiliary window
|
||||
// state on shutdown and thus we prevent closing if
|
||||
// quit is requested.
|
||||
//
|
||||
// Interestingly, this will not prevent the application
|
||||
// from quitting because the auxiliary windows will still
|
||||
// close once the owning window closes.
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
win.on('closed', () => {
|
||||
this.trace(`Lifecycle#auxWindow.on('closed') - window ID ${auxWindow.id}`);
|
||||
});
|
||||
}
|
||||
|
||||
async reload(window: ICodeWindow, cli?: NativeParsedArgs): Promise<void> {
|
||||
|
||||
// Only reload when the window has not vetoed this
|
||||
|
||||
@@ -53,6 +53,8 @@ export interface ICommonNativeHostService {
|
||||
readonly onDidFocusMainWindow: Event<number>;
|
||||
readonly onDidBlurMainWindow: Event<number>;
|
||||
|
||||
readonly onDidChangeWindowFullScreen: Event<number>;
|
||||
|
||||
readonly onDidFocusMainOrAuxiliaryWindow: Event<number>;
|
||||
readonly onDidBlurMainOrAuxiliaryWindow: Event<number>;
|
||||
|
||||
|
||||
@@ -92,6 +92,11 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
|
||||
Event.filter(Event.map(this.auxiliaryWindowsMainService.onDidUnmaximizeWindow, window => window.id), windowId => !!this.auxiliaryWindowsMainService.getWindowById(windowId))
|
||||
);
|
||||
|
||||
readonly onDidChangeWindowFullScreen = Event.any(
|
||||
Event.map(this.windowsMainService.onDidChangeFullScreen, window => window.id),
|
||||
Event.map(this.auxiliaryWindowsMainService.onDidChangeFullScreen, window => window.id)
|
||||
);
|
||||
|
||||
readonly onDidBlurMainWindow = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId));
|
||||
readonly onDidFocusMainWindow = Event.any(
|
||||
Event.map(Event.filter(Event.map(this.windowsMainService.onDidChangeWindowsCount, () => this.windowsMainService.getLastActiveWindow()), window => !!window), window => window!.id),
|
||||
|
||||
@@ -15,7 +15,6 @@ import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/lis
|
||||
import { IListOptions, IListStyles, List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IProgressBarStyles, ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
|
||||
import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
@@ -30,7 +29,7 @@ import { localize } from 'vs/nls';
|
||||
import { IInputBox, IKeyMods, IQuickInput, IQuickInputButton, IQuickInputHideEvent, IQuickInputToggle, IQuickNavigateConfiguration, IQuickPick, IQuickPickDidAcceptEvent, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent, IQuickPickWillAcceptEvent, IQuickWidget, ItemActivation, NO_KEY_MODS, QuickInputHideReason } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { QuickInputBox } from './quickInputBox';
|
||||
import { QuickInputList, QuickInputListFocus } from './quickInputList';
|
||||
import { getIconClass, renderQuickInputDescription } from './quickInputUtils';
|
||||
import { quickInputButtonToAction, renderQuickInputDescription } from './quickInputUtils';
|
||||
|
||||
export interface IQuickInputOptions {
|
||||
idPrefix: string;
|
||||
@@ -388,23 +387,23 @@ class QuickInput extends Disposable implements IQuickInput {
|
||||
if (this.buttonsUpdated) {
|
||||
this.buttonsUpdated = false;
|
||||
this.ui.leftActionBar.clear();
|
||||
const leftButtons = this.buttons.filter(button => button === backButton);
|
||||
this.ui.leftActionBar.push(leftButtons.map((button, index) => {
|
||||
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, async () => {
|
||||
this.onDidTriggerButtonEmitter.fire(button);
|
||||
});
|
||||
action.tooltip = button.tooltip || '';
|
||||
return action;
|
||||
}), { icon: true, label: false });
|
||||
const leftButtons = this.buttons
|
||||
.filter(button => button === backButton)
|
||||
.map((button, index) => quickInputButtonToAction(
|
||||
button,
|
||||
`id-${index}`,
|
||||
async () => this.onDidTriggerButtonEmitter.fire(button)
|
||||
));
|
||||
this.ui.leftActionBar.push(leftButtons, { icon: true, label: false });
|
||||
this.ui.rightActionBar.clear();
|
||||
const rightButtons = this.buttons.filter(button => button !== backButton);
|
||||
this.ui.rightActionBar.push(rightButtons.map((button, index) => {
|
||||
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, async () => {
|
||||
this.onDidTriggerButtonEmitter.fire(button);
|
||||
});
|
||||
action.tooltip = button.tooltip || '';
|
||||
return action;
|
||||
}), { icon: true, label: false });
|
||||
const rightButtons = this.buttons
|
||||
.filter(button => button !== backButton)
|
||||
.map((button, index) => quickInputButtonToAction(
|
||||
button,
|
||||
`id-${index}`,
|
||||
async () => this.onDidTriggerButtonEmitter.fire(button)
|
||||
));
|
||||
this.ui.rightActionBar.push(rightButtons, { icon: true, label: false });
|
||||
}
|
||||
if (this.togglesUpdated) {
|
||||
this.togglesUpdated = false;
|
||||
|
||||
@@ -8,12 +8,11 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { AriaRole } from 'vs/base/browser/ui/aria/aria';
|
||||
import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget';
|
||||
import { IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
|
||||
import { IHoverDelegate, IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
|
||||
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
|
||||
import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { IListAccessibilityProvider, IListOptions, IListStyles, List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
import { ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { compareAnything } from 'vs/base/common/comparers';
|
||||
@@ -30,7 +29,7 @@ import { ltrim } from 'vs/base/common/strings';
|
||||
import 'vs/css!./media/quickInput';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IQuickInputOptions } from 'vs/platform/quickinput/browser/quickInput';
|
||||
import { getIconClass } from 'vs/platform/quickinput/browser/quickInputUtils';
|
||||
import { quickInputButtonToAction } from 'vs/platform/quickinput/browser/quickInputUtils';
|
||||
import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent, QuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -235,7 +234,10 @@ class ListElementRenderer implements IListRenderer<IListElement, IListElementTem
|
||||
|
||||
static readonly ID = 'listelement';
|
||||
|
||||
constructor(private readonly themeService: IThemeService) { }
|
||||
constructor(
|
||||
private readonly themeService: IThemeService,
|
||||
private readonly hoverDelegate: IHoverDelegate | undefined,
|
||||
) { }
|
||||
|
||||
get templateId() {
|
||||
return ListElementRenderer.ID;
|
||||
@@ -267,7 +269,7 @@ class ListElementRenderer implements IListRenderer<IListElement, IListElementTem
|
||||
const row2 = dom.append(rows, $('.quick-input-list-row'));
|
||||
|
||||
// Label
|
||||
data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportIcons: true });
|
||||
data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportIcons: true, hoverDelegate: this.hoverDelegate });
|
||||
data.toDisposeTemplate.push(data.label);
|
||||
data.icon = <HTMLInputElement>dom.prepend(data.label.element, $('.quick-input-list-icon'));
|
||||
|
||||
@@ -277,14 +279,14 @@ class ListElementRenderer implements IListRenderer<IListElement, IListElementTem
|
||||
|
||||
// Detail
|
||||
const detailContainer = dom.append(row2, $('.quick-input-list-label-meta'));
|
||||
data.detail = new IconLabel(detailContainer, { supportHighlights: true, supportIcons: true });
|
||||
data.detail = new IconLabel(detailContainer, { supportHighlights: true, supportIcons: true, hoverDelegate: this.hoverDelegate });
|
||||
data.toDisposeTemplate.push(data.detail);
|
||||
|
||||
// Separator
|
||||
data.separator = dom.append(data.entry, $('.quick-input-list-separator'));
|
||||
|
||||
// Actions
|
||||
data.actionBar = new ActionBar(data.entry);
|
||||
data.actionBar = new ActionBar(data.entry, this.hoverDelegate ? { hoverDelegate: this.hoverDelegate } : undefined);
|
||||
data.actionBar.domNode.classList.add('quick-input-list-entry-action-bar');
|
||||
data.toDisposeTemplate.push(data.actionBar);
|
||||
|
||||
@@ -314,7 +316,8 @@ class ListElementRenderer implements IListRenderer<IListElement, IListElementTem
|
||||
// Label
|
||||
const options: IIconLabelValueOptions = {
|
||||
matches: labelHighlights || [],
|
||||
descriptionTitle: element.saneDescription,
|
||||
// If we have a tooltip, we want that to be shown and not any other hover
|
||||
descriptionTitle: element.saneTooltip ? undefined : element.saneDescription,
|
||||
descriptionMatches: descriptionHighlights || [],
|
||||
labelEscapeNewLines: true
|
||||
};
|
||||
@@ -336,7 +339,8 @@ class ListElementRenderer implements IListRenderer<IListElement, IListElementTem
|
||||
data.detail.element.style.display = '';
|
||||
data.detail.setLabel(element.saneDetail, undefined, {
|
||||
matches: detailHighlights,
|
||||
title: element.saneDetail,
|
||||
// If we have a tooltip, we want that to be shown and not any other hover
|
||||
title: element.saneTooltip ? undefined : element.saneDetail,
|
||||
labelEscapeNewLines: true
|
||||
});
|
||||
} else {
|
||||
@@ -355,30 +359,13 @@ class ListElementRenderer implements IListRenderer<IListElement, IListElementTem
|
||||
// Actions
|
||||
const buttons = mainItem.buttons;
|
||||
if (buttons && buttons.length) {
|
||||
data.actionBar.push(buttons.map((button, index): IAction => {
|
||||
let cssClasses = button.iconClass || (button.iconPath ? getIconClass(button.iconPath) : undefined);
|
||||
if (button.alwaysVisible) {
|
||||
cssClasses = cssClasses ? `${cssClasses} always-visible` : 'always-visible';
|
||||
}
|
||||
return {
|
||||
id: `id-${index}`,
|
||||
class: cssClasses,
|
||||
enabled: true,
|
||||
label: '',
|
||||
tooltip: button.tooltip || '',
|
||||
run: () => {
|
||||
mainItem.type !== 'separator'
|
||||
? element.fireButtonTriggered({
|
||||
button,
|
||||
item: mainItem
|
||||
})
|
||||
: element.fireSeparatorButtonTriggered({
|
||||
button,
|
||||
separator: mainItem
|
||||
});
|
||||
}
|
||||
};
|
||||
}), { icon: true, label: false });
|
||||
data.actionBar.push(buttons.map((button, index) => quickInputButtonToAction(
|
||||
button,
|
||||
`id-${index}`,
|
||||
() => mainItem.type !== 'separator'
|
||||
? element.fireButtonTriggered({ button, item: mainItem })
|
||||
: element.fireSeparatorButtonTriggered({ button, separator: mainItem })
|
||||
)), { icon: true, label: false });
|
||||
data.entry.classList.add('has-actions');
|
||||
} else {
|
||||
data.entry.classList.remove('has-actions');
|
||||
@@ -468,7 +455,7 @@ export class QuickInputList {
|
||||
this.container = dom.append(this.parent, $('.quick-input-list'));
|
||||
const delegate = new ListElementDelegate();
|
||||
const accessibilityProvider = new QuickInputAccessibilityProvider();
|
||||
this.list = options.createList('QuickInput', this.container, delegate, [new ListElementRenderer(themeService)], {
|
||||
this.list = options.createList('QuickInput', this.container, delegate, [new ListElementRenderer(themeService, options.hoverDelegate)], {
|
||||
identityProvider: {
|
||||
getId: element => {
|
||||
// always prefer item over separator because if item is defined, it must be the main item type
|
||||
|
||||
@@ -16,11 +16,13 @@ import { URI } from 'vs/base/common/uri';
|
||||
import 'vs/css!./media/quickInput';
|
||||
import { localize } from 'vs/nls';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IQuickInputButton } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
|
||||
const iconPathToClass: Record<string, string> = {};
|
||||
const iconClassGenerator = new IdGenerator('quick-input-button-icon-');
|
||||
|
||||
export function getIconClass(iconPath: { dark: URI; light?: URI } | undefined): string | undefined {
|
||||
function getIconClass(iconPath: { dark: URI; light?: URI } | undefined): string | undefined {
|
||||
if (!iconPath) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -39,6 +41,22 @@ export function getIconClass(iconPath: { dark: URI; light?: URI } | undefined):
|
||||
return iconClass;
|
||||
}
|
||||
|
||||
export function quickInputButtonToAction(button: IQuickInputButton, id: string, run: () => unknown): IAction {
|
||||
let cssClasses = button.iconClass || getIconClass(button.iconPath);
|
||||
if (button.alwaysVisible) {
|
||||
cssClasses = cssClasses ? `${cssClasses} always-visible` : 'always-visible';
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
label: '',
|
||||
tooltip: button.tooltip || '',
|
||||
class: cssClasses,
|
||||
enabled: true,
|
||||
run
|
||||
};
|
||||
}
|
||||
|
||||
export function renderQuickInputDescription(description: string, container: HTMLElement, actionHandler: { callback: (content: string) => void; disposables: DisposableStore }) {
|
||||
dom.reset(container);
|
||||
const parsed = parseLinkedText(description);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { Promises } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow';
|
||||
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||
import { ILifecycleMainService, IRelaunchHandler, LifecycleMainPhase, ShutdownEvent, ShutdownReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { IStateService } from 'vs/platform/state/node/state';
|
||||
@@ -41,6 +42,7 @@ export class TestLifecycleMainService implements ILifecycleMainService {
|
||||
phase = LifecycleMainPhase.Ready;
|
||||
|
||||
registerWindow(window: ICodeWindow): void { }
|
||||
registerAuxWindow(auxWindow: IAuxiliaryWindow): void { }
|
||||
async reload(window: ICodeWindow, cli?: NativeParsedArgs): Promise<void> { }
|
||||
async unload(window: ICodeWindow, reason: UnloadReason): Promise<boolean> { return true; }
|
||||
setRelaunchHandler(handler: IRelaunchHandler): void { }
|
||||
|
||||
@@ -347,6 +347,7 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native
|
||||
colorScheme: IColorScheme;
|
||||
autoDetectHighContrast?: boolean;
|
||||
autoDetectColorScheme?: boolean;
|
||||
isCustomZoomLevel?: boolean;
|
||||
|
||||
perfMarks: PerformanceMark[];
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ export interface IBaseWindow extends IDisposable {
|
||||
readonly onDidMaximize: Event<void>;
|
||||
readonly onDidUnmaximize: Event<void>;
|
||||
readonly onDidTriggerSystemContextMenu: Event<{ readonly x: number; readonly y: number }>;
|
||||
readonly onDidEnterFullScreen: Event<void>;
|
||||
readonly onDidLeaveFullScreen: Event<void>;
|
||||
readonly onDidClose: Event<void>;
|
||||
|
||||
readonly id: number;
|
||||
@@ -79,6 +81,8 @@ export interface ICodeWindow extends IBaseWindow {
|
||||
|
||||
updateTouchBar(items: ISerializableCommandAction[][]): void;
|
||||
|
||||
notifyZoomLevel(zoomLevel: number | undefined): void;
|
||||
|
||||
serializeWindowState(): IWindowState;
|
||||
}
|
||||
|
||||
@@ -129,6 +133,7 @@ export interface IWindowState {
|
||||
x?: number;
|
||||
y?: number;
|
||||
mode?: WindowMode;
|
||||
zoomLevel?: number;
|
||||
readonly display?: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,21 +4,35 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { getZoomLevel, setZoomFactor, setZoomLevel } from 'vs/base/browser/browser';
|
||||
import { getWindows } from 'vs/base/browser/dom';
|
||||
import { getActiveWindow, getWindows } from 'vs/base/browser/dom';
|
||||
import { mainWindow } from 'vs/base/browser/window';
|
||||
import { ISandboxGlobals, ipcRenderer, webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
import { zoomLevelToZoomFactor } from 'vs/platform/window/common/window';
|
||||
|
||||
export enum ApplyZoomTarget {
|
||||
ACTIVE_WINDOW = 1,
|
||||
ALL_WINDOWS
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a zoom level to the window. Also sets it in our in-memory
|
||||
* browser helper so that it can be accessed in non-electron layers.
|
||||
*/
|
||||
export function applyZoom(zoomLevel: number): void {
|
||||
for (const { window } of getWindows()) {
|
||||
getGlobals(window)?.webFrame?.setZoomLevel(zoomLevel);
|
||||
export function applyZoom(zoomLevel: number, target: ApplyZoomTarget | Window): void {
|
||||
const targetWindows: Window[] = [];
|
||||
if (target === ApplyZoomTarget.ACTIVE_WINDOW) {
|
||||
targetWindows.push(getActiveWindow());
|
||||
} else if (target === ApplyZoomTarget.ALL_WINDOWS) {
|
||||
targetWindows.push(...Array.from(getWindows()).map(({ window }) => window));
|
||||
} else {
|
||||
targetWindows.push(target);
|
||||
}
|
||||
|
||||
for (const targetWindow of targetWindows) {
|
||||
getGlobals(targetWindow)?.webFrame?.setZoomLevel(zoomLevel);
|
||||
setZoomFactor(zoomLevelToZoomFactor(zoomLevel), targetWindow);
|
||||
setZoomLevel(zoomLevel, targetWindow);
|
||||
}
|
||||
setZoomFactor(zoomLevelToZoomFactor(zoomLevel));
|
||||
setZoomLevel(zoomLevel);
|
||||
}
|
||||
|
||||
function getGlobals(win: Window): ISandboxGlobals | undefined {
|
||||
@@ -36,10 +50,10 @@ function getGlobals(win: Window): ISandboxGlobals | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function zoomIn(): void {
|
||||
applyZoom(getZoomLevel() + 1);
|
||||
export function zoomIn(target: ApplyZoomTarget | Window): void {
|
||||
applyZoom(getZoomLevel(typeof target === 'number' ? getActiveWindow() : target) + 1, target);
|
||||
}
|
||||
|
||||
export function zoomOut(): void {
|
||||
applyZoom(getZoomLevel() - 1);
|
||||
export function zoomOut(target: ApplyZoomTarget | Window): void {
|
||||
applyZoom(getZoomLevel(typeof target === 'number' ? getActiveWindow() : target) - 1, target);
|
||||
}
|
||||
|
||||
@@ -98,6 +98,12 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow {
|
||||
private readonly _onDidTriggerSystemContextMenu = this._register(new Emitter<{ x: number; y: number }>());
|
||||
readonly onDidTriggerSystemContextMenu = this._onDidTriggerSystemContextMenu.event;
|
||||
|
||||
private readonly _onDidEnterFullScreen = this._register(new Emitter<void>());
|
||||
readonly onDidEnterFullScreen = this._onDidEnterFullScreen.event;
|
||||
|
||||
private readonly _onDidLeaveFullScreen = this._register(new Emitter<void>());
|
||||
readonly onDidLeaveFullScreen = this._onDidLeaveFullScreen.event;
|
||||
|
||||
//#endregion
|
||||
|
||||
abstract readonly id: number;
|
||||
@@ -121,6 +127,8 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow {
|
||||
this._register(Event.fromNodeEventEmitter(win, 'focus')(() => {
|
||||
this._lastFocusTime = Date.now();
|
||||
}));
|
||||
this._register(Event.fromNodeEventEmitter(this._win, 'enter-full-screen')(() => this._onDidEnterFullScreen.fire()));
|
||||
this._register(Event.fromNodeEventEmitter(this._win, 'leave-full-screen')(() => this._onDidLeaveFullScreen.fire()));
|
||||
|
||||
// Sheet Offsets
|
||||
const useCustomTitleStyle = getTitleBarStyle(this.configurationService) === 'custom';
|
||||
@@ -470,6 +478,8 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
|
||||
private currentHttpProxy: string | undefined = undefined;
|
||||
private currentNoProxy: string | undefined = undefined;
|
||||
|
||||
private customZoomLevel: number | undefined = undefined;
|
||||
|
||||
private readonly configObjectUrl = this._register(this.protocolMainService.createIPCObjectUrl<INativeWindowConfiguration>());
|
||||
private pendingLoadConfig: INativeWindowConfiguration | undefined;
|
||||
private wasLoaded = false;
|
||||
@@ -670,14 +680,14 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
|
||||
}));
|
||||
|
||||
// Window Fullscreen
|
||||
this._register(Event.fromNodeEventEmitter(this._win, 'enter-full-screen')(() => {
|
||||
this._register(this.onDidEnterFullScreen(() => {
|
||||
this.sendWhenReady('vscode:enterFullScreen', CancellationToken.None);
|
||||
|
||||
this.joinNativeFullScreenTransition?.complete();
|
||||
this.joinNativeFullScreenTransition = undefined;
|
||||
}));
|
||||
|
||||
this._register(Event.fromNodeEventEmitter(this._win, 'leave-full-screen')(() => {
|
||||
this._register(this.onDidLeaveFullScreen(() => {
|
||||
this.sendWhenReady('vscode:leaveFullScreen', CancellationToken.None);
|
||||
|
||||
this.joinNativeFullScreenTransition?.complete();
|
||||
@@ -1042,6 +1052,11 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
|
||||
configuration.fullscreen = this.isFullScreen;
|
||||
configuration.maximized = this._win.isMaximized();
|
||||
configuration.partsSplash = this.themeMainService.getWindowSplash();
|
||||
configuration.zoomLevel = this.getZoomLevel();
|
||||
configuration.isCustomZoomLevel = typeof this.customZoomLevel === 'number';
|
||||
if (configuration.isCustomZoomLevel && configuration.partsSplash) {
|
||||
configuration.partsSplash.zoomLevel = configuration.zoomLevel;
|
||||
}
|
||||
|
||||
// Update with latest perf marks
|
||||
mark('code/willOpenNewWindow');
|
||||
@@ -1141,7 +1156,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
|
||||
|
||||
const defaultState = defaultWindowState();
|
||||
|
||||
const res = {
|
||||
return {
|
||||
mode: WindowMode.Fullscreen,
|
||||
display: display ? display.id : undefined,
|
||||
|
||||
@@ -1153,10 +1168,9 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
|
||||
width: this.windowState.width || defaultState.width,
|
||||
height: this.windowState.height || defaultState.height,
|
||||
x: this.windowState.x || 0,
|
||||
y: this.windowState.y || 0
|
||||
y: this.windowState.y || 0,
|
||||
zoomLevel: this.customZoomLevel
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const state: IWindowState = Object.create(null);
|
||||
@@ -1191,6 +1205,8 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
|
||||
state.height = bounds.height;
|
||||
}
|
||||
|
||||
state.zoomLevel = this.customZoomLevel;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1199,6 +1215,11 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
|
||||
|
||||
let hasMultipleDisplays = false;
|
||||
if (state) {
|
||||
|
||||
// Window zoom
|
||||
this.customZoomLevel = state.zoomLevel;
|
||||
|
||||
// Window dimensions
|
||||
try {
|
||||
const displays = screen.getAllDisplays();
|
||||
hasMultipleDisplays = displays.length > 1;
|
||||
@@ -1435,6 +1456,19 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
|
||||
}
|
||||
}
|
||||
|
||||
notifyZoomLevel(zoomLevel: number | undefined): void {
|
||||
this.customZoomLevel = zoomLevel;
|
||||
}
|
||||
|
||||
private getZoomLevel(): number | undefined {
|
||||
if (typeof this.customZoomLevel === 'number') {
|
||||
return this.customZoomLevel;
|
||||
}
|
||||
|
||||
const windowSettings = this.configurationService.getValue<IWindowSettings | undefined>('window');
|
||||
return windowSettings?.zoomLevel;
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this._win?.close();
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ export interface IWindowsMainService {
|
||||
readonly onDidSignalReadyWindow: Event<ICodeWindow>;
|
||||
readonly onDidMaximizeWindow: Event<ICodeWindow>;
|
||||
readonly onDidUnmaximizeWindow: Event<ICodeWindow>;
|
||||
readonly onDidChangeFullScreen: Event<ICodeWindow>;
|
||||
readonly onDidTriggerSystemContextMenu: Event<{ readonly window: ICodeWindow; readonly x: number; readonly y: number }>;
|
||||
readonly onDidDestroyWindow: Event<ICodeWindow>;
|
||||
|
||||
@@ -131,7 +132,7 @@ export function defaultBrowserWindowOptions(accessor: ServicesAccessor, windowSt
|
||||
webPreferences: {
|
||||
enableWebSQL: false,
|
||||
spellcheck: false,
|
||||
zoomFactor: zoomLevelToZoomFactor(windowSettings?.zoomLevel),
|
||||
zoomFactor: zoomLevelToZoomFactor(windowState?.zoomLevel ?? windowSettings?.zoomLevel),
|
||||
autoplayPolicy: 'user-gesture-required',
|
||||
// Enable experimental css highlight api https://chromestatus.com/feature/5436441440026624
|
||||
// Refs https://github.com/microsoft/vscode/issues/140098
|
||||
|
||||
@@ -198,6 +198,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
private readonly _onDidUnmaximizeWindow = this._register(new Emitter<ICodeWindow>());
|
||||
readonly onDidUnmaximizeWindow = this._onDidUnmaximizeWindow.event;
|
||||
|
||||
private readonly _onDidChangeFullScreen = this._register(new Emitter<ICodeWindow>());
|
||||
readonly onDidChangeFullScreen = this._onDidChangeFullScreen.event;
|
||||
|
||||
private readonly _onDidTriggerSystemContextMenu = this._register(new Emitter<{ window: ICodeWindow; x: number; y: number }>());
|
||||
readonly onDidTriggerSystemContextMenu = this._onDidTriggerSystemContextMenu.event;
|
||||
|
||||
@@ -1453,7 +1456,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
isInitialStartup: options.initialStartup,
|
||||
perfMarks: getMarks(),
|
||||
os: { release: release(), hostname: hostname(), arch: arch() },
|
||||
zoomLevel: typeof windowConfig?.zoomLevel === 'number' ? windowConfig.zoomLevel : undefined,
|
||||
|
||||
autoDetectHighContrast: windowConfig?.autoDetectHighContrast ?? true,
|
||||
autoDetectColorScheme: windowConfig?.autoDetectColorScheme ?? false,
|
||||
@@ -1498,6 +1500,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
disposables.add(Event.once(createdWindow.onDidDestroy)(() => this.onWindowDestroyed(createdWindow)));
|
||||
disposables.add(createdWindow.onDidMaximize(() => this._onDidMaximizeWindow.fire(createdWindow)));
|
||||
disposables.add(createdWindow.onDidUnmaximize(() => this._onDidUnmaximizeWindow.fire(createdWindow)));
|
||||
disposables.add(createdWindow.onDidEnterFullScreen(() => this._onDidChangeFullScreen.fire(createdWindow)));
|
||||
disposables.add(createdWindow.onDidLeaveFullScreen(() => this._onDidChangeFullScreen.fire(createdWindow)));
|
||||
disposables.add(createdWindow.onDidTriggerSystemContextMenu(({ x, y }) => this._onDidTriggerSystemContextMenu.fire({ window: createdWindow, x, y })));
|
||||
|
||||
const webContents = assertIsDefined(createdWindow.win?.webContents);
|
||||
|
||||
@@ -40,6 +40,8 @@ suite('WindowsFinder', () => {
|
||||
onDidSignalReady: Event<void> = Event.None;
|
||||
onDidClose: Event<void> = Event.None;
|
||||
onDidDestroy: Event<void> = Event.None;
|
||||
onDidEnterFullScreen: Event<void> = Event.None;
|
||||
onDidLeaveFullScreen: Event<void> = Event.None;
|
||||
whenClosedOrLoaded: Promise<void> = Promise.resolve();
|
||||
id: number = -1;
|
||||
win: Electron.BrowserWindow = null!;
|
||||
@@ -72,6 +74,7 @@ suite('WindowsFinder', () => {
|
||||
updateTouchBar(items: UriDto<ICommandAction>[][]): void { throw new Error('Method not implemented.'); }
|
||||
serializeWindowState(): IWindowState { throw new Error('Method not implemented'); }
|
||||
updateWindowControls(options: { height?: number | undefined; backgroundColor?: string | undefined; foregroundColor?: string | undefined }): void { throw new Error('Method not implemented.'); }
|
||||
notifyZoomLevel(level: number): void { throw new Error('Method not implemented.'); }
|
||||
dispose(): void { }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export class MainThreadEditorTabs implements MainThreadEditorTabsShape {
|
||||
this._dispoables.add(this._editorGroupsService.onDidRemoveGroup(() => this._createTabsModel()));
|
||||
|
||||
// Once everything is read go ahead and initialize the model
|
||||
this._editorGroupsService.mainPart.whenReady.then(() => this._createTabsModel());
|
||||
this._editorGroupsService.whenReady.then(() => this._createTabsModel());
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
@@ -39,6 +39,7 @@ import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/c
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
class InspectContextKeysAction extends Action2 {
|
||||
|
||||
@@ -467,6 +468,7 @@ class RemoveLargeStorageEntriesAction extends Action2 {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
const userDataProfileService = accessor.get(IUserDataProfileService);
|
||||
const dialogService = accessor.get(IDialogService);
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
|
||||
interface IStorageItem extends IQuickPickItem {
|
||||
readonly key: string;
|
||||
@@ -485,7 +487,7 @@ class RemoveLargeStorageEntriesAction extends Action2 {
|
||||
for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) {
|
||||
for (const key of storageService.keys(scope, target)) {
|
||||
const value = storageService.get(key, scope);
|
||||
if (value && value.length > RemoveLargeStorageEntriesAction.SIZE_THRESHOLD) {
|
||||
if (value && (!environmentService.isBuilt /* show all keys in dev */ || value.length > RemoveLargeStorageEntriesAction.SIZE_THRESHOLD)) {
|
||||
items.push({
|
||||
key,
|
||||
scope,
|
||||
|
||||
@@ -21,13 +21,14 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b
|
||||
import { ToggleAuxiliaryBarAction } from 'vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions';
|
||||
import { TogglePanelAction } from 'vs/workbench/browser/parts/panel/panelActions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelVisibleContext, SideBarVisibleContext, FocusedViewContext, InEditorZenModeContext, IsCenteredLayoutContext, MainEditorAreaVisibleContext, IsFullscreenContext, PanelPositionContext, IsAuxiliaryWindowFocusedContext } from 'vs/workbench/common/contextkeys';
|
||||
import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelVisibleContext, SideBarVisibleContext, FocusedViewContext, InEditorZenModeContext, IsCenteredLayoutContext, MainEditorAreaVisibleContext, IsMainWindowFullscreenContext, PanelPositionContext, IsAuxiliaryWindowFocusedContext } from 'vs/workbench/common/contextkeys';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { ICommandActionTitle } from 'vs/platform/action/common/action';
|
||||
import { mainWindow } from 'vs/base/browser/window';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
|
||||
// Register Icons
|
||||
const menubarIcon = registerIcon('menuBar', Codicon.layoutMenubar, localize('menuBarIcon', "Represents the menu bar"));
|
||||
@@ -1366,7 +1367,7 @@ if (!isMacintosh || !isNative) {
|
||||
}
|
||||
|
||||
ToggleVisibilityActions.push(...[
|
||||
CreateToggleLayoutItem(ToggleActivityBarVisibilityActionId, ContextKeyExpr.equals('config.workbench.activityBar.visible', true), localize('activityBar', "Activity Bar"), { whenA: ContextKeyExpr.equals('config.workbench.sideBar.location', 'left'), iconA: activityBarLeftIcon, iconB: activityBarRightIcon }),
|
||||
CreateToggleLayoutItem(ToggleActivityBarVisibilityActionId, ContextKeyExpr.notEquals('config.workbench.activityBar.location', 'hidden'), localize('activityBar', "Activity Bar"), { whenA: ContextKeyExpr.equals('config.workbench.sideBar.location', 'left'), iconA: activityBarLeftIcon, iconB: activityBarRightIcon }),
|
||||
CreateToggleLayoutItem(ToggleSidebarVisibilityAction.ID, SideBarVisibleContext, localize('sideBar', "Primary Side Bar"), { whenA: ContextKeyExpr.equals('config.workbench.sideBar.location', 'left'), iconA: panelLeftIcon, iconB: panelRightIcon }),
|
||||
CreateToggleLayoutItem(ToggleAuxiliaryBarAction.ID, AuxiliaryBarVisibleContext, localize('secondarySideBar', "Secondary Side Bar"), { whenA: ContextKeyExpr.equals('config.workbench.sideBar.location', 'left'), iconA: panelRightIcon, iconB: panelLeftIcon }),
|
||||
CreateToggleLayoutItem(TogglePanelAction.ID, PanelVisibleContext, localize('panel', "Panel"), panelIcon),
|
||||
@@ -1386,7 +1387,7 @@ const AlignPanelActions: CustomizeLayoutItem[] = [
|
||||
];
|
||||
|
||||
const MiscLayoutOptions: CustomizeLayoutItem[] = [
|
||||
CreateOptionLayoutItem('workbench.action.toggleFullScreen', IsFullscreenContext, localize('fullscreen', "Full Screen"), fullscreenIcon),
|
||||
CreateOptionLayoutItem('workbench.action.toggleFullScreen', IsMainWindowFullscreenContext, localize('fullscreen', "Full Screen"), fullscreenIcon),
|
||||
CreateOptionLayoutItem('workbench.action.toggleZenMode', InEditorZenModeContext, localize('zenMode', "Zen Mode"), zenModeIcon),
|
||||
CreateOptionLayoutItem('workbench.action.toggleCenteredLayout', IsCenteredLayoutContext, localize('centeredLayout', "Centered Layout"), centerLayoutIcon),
|
||||
];
|
||||
@@ -1422,7 +1423,7 @@ registerAction2(class CustomizeLayoutAction extends Action2 {
|
||||
});
|
||||
}
|
||||
|
||||
getItems(contextKeyService: IContextKeyService): QuickPickItem[] {
|
||||
getItems(contextKeyService: IContextKeyService, keybindingService: IKeybindingService): QuickPickItem[] {
|
||||
const toQuickPickItem = (item: CustomizeLayoutItem): IQuickPickItem => {
|
||||
const toggled = item.active.evaluate(contextKeyService.getContext(null));
|
||||
let label = item.useButtons ?
|
||||
@@ -1448,6 +1449,7 @@ registerAction2(class CustomizeLayoutAction extends Action2 {
|
||||
id: item.id,
|
||||
label,
|
||||
ariaLabel,
|
||||
keybinding: keybindingService.lookupKeybinding(item.id, contextKeyService),
|
||||
buttons: !item.useButtons ? undefined : [
|
||||
{
|
||||
alwaysVisible: false,
|
||||
@@ -1491,10 +1493,11 @@ registerAction2(class CustomizeLayoutAction extends Action2 {
|
||||
const contextKeyService = accessor.get(IContextKeyService);
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
const keybindingService = accessor.get(IKeybindingService);
|
||||
const quickPick = quickInputService.createQuickPick();
|
||||
|
||||
this._currentQuickPick = quickPick;
|
||||
quickPick.items = this.getItems(contextKeyService);
|
||||
quickPick.items = this.getItems(contextKeyService, keybindingService);
|
||||
quickPick.ignoreFocusOut = true;
|
||||
quickPick.hideInput = true;
|
||||
quickPick.title = localize('customizeLayoutQuickPickTitle', "Customize Layout");
|
||||
@@ -1520,7 +1523,7 @@ registerAction2(class CustomizeLayoutAction extends Action2 {
|
||||
let selectedItem: CustomizeLayoutItem | undefined = undefined;
|
||||
disposables.add(contextKeyService.onDidChangeContext(changeEvent => {
|
||||
if (changeEvent.affectsSome(LayoutContextKeySet)) {
|
||||
quickPick.items = this.getItems(contextKeyService);
|
||||
quickPick.items = this.getItems(contextKeyService, keybindingService);
|
||||
if (selectedItem) {
|
||||
quickPick.activeItems = quickPick.items.filter(item => (item as CustomizeLayoutItem).id === selectedItem?.id) as IQuickPickItem[];
|
||||
}
|
||||
@@ -1554,7 +1557,7 @@ registerAction2(class CustomizeLayoutAction extends Action2 {
|
||||
};
|
||||
|
||||
// Reset all layout options
|
||||
resetSetting('workbench.activityBar.visible');
|
||||
resetSetting('workbench.activityBar.location');
|
||||
resetSetting('workbench.sideBar.location');
|
||||
resetSetting('workbench.statusBar.visible');
|
||||
resetSetting('workbench.panel.defaultLocation');
|
||||
|
||||
@@ -8,7 +8,7 @@ import { IWindowOpenable } from 'vs/platform/window/common/window';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { MenuRegistry, MenuId, Action2, registerAction2, IAction2Options } from 'vs/platform/actions/common/actions';
|
||||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { IsAuxiliaryWindowFocusedContext, IsFullscreenContext } from 'vs/workbench/common/contextkeys';
|
||||
import { IsMainWindowFullscreenContext } from 'vs/workbench/common/contextkeys';
|
||||
import { IsMacNativeContext, IsDevelopmentContext, IsWebContext, IsIOSContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
@@ -35,7 +35,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isFolderBackupInfo, isWorkspaceBackupInfo } from 'vs/platform/backup/common/backup';
|
||||
import { getActiveElement, getActiveWindow } from 'vs/base/browser/dom';
|
||||
import { mainWindow } from 'vs/base/browser/window';
|
||||
|
||||
export const inRecentFilesPickerContextKey = 'inRecentFilesPicker';
|
||||
|
||||
@@ -298,7 +297,7 @@ class ToggleFullScreenAction extends Action2 {
|
||||
}
|
||||
},
|
||||
precondition: IsIOSContext.toNegated(),
|
||||
toggled: IsFullscreenContext,
|
||||
toggled: IsMainWindowFullscreenContext,
|
||||
menu: [{
|
||||
id: MenuId.MenubarAppearanceMenu,
|
||||
group: '1_toggle_view',
|
||||
@@ -323,7 +322,6 @@ export class ReloadWindowAction extends Action2 {
|
||||
id: ReloadWindowAction.ID,
|
||||
title: { value: localize('reloadWindow', "Reload Window"), original: 'Reload Window' },
|
||||
category: Categories.Developer,
|
||||
precondition: IsAuxiliaryWindowFocusedContext.toNegated(),
|
||||
f1: true,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
@@ -336,9 +334,7 @@ export class ReloadWindowAction extends Action2 {
|
||||
override async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const hostService = accessor.get(IHostService);
|
||||
|
||||
if (getActiveWindow() === mainWindow) {
|
||||
return hostService.reload(); // only supported for main window
|
||||
}
|
||||
return hostService.reload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, MainEditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds, TitleBarVisibleContext, TitleBarStyleContext, MultipleEditorGroupsContext, IsAuxiliaryWindowFocusedContext, ActiveCompareEditorOriginalWriteableContext } from 'vs/workbench/common/contextkeys';
|
||||
import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, MainEditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds, TitleBarVisibleContext, TitleBarStyleContext, MultipleEditorGroupsContext, IsAuxiliaryWindowFocusedContext, ActiveCompareEditorOriginalWriteableContext } from 'vs/workbench/common/contextkeys';
|
||||
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { trackFocus, addDisposableListener, EventType, onDidRegisterWindow, getActiveWindow } from 'vs/base/browser/dom';
|
||||
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
@@ -28,6 +28,7 @@ import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/
|
||||
import { getTitleBarStyle } from 'vs/platform/window/common/window';
|
||||
import { mainWindow } from 'vs/base/browser/window';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { isFullscreen, onDidChangeFullscreen } from 'vs/base/browser/browser';
|
||||
|
||||
export class WorkbenchContextKeysHandler extends Disposable {
|
||||
private inputFocusedContext: IContextKey<boolean>;
|
||||
@@ -68,7 +69,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
private temporaryWorkspaceContext: IContextKey<boolean>;
|
||||
|
||||
private inZenModeContext: IContextKey<boolean>;
|
||||
private isFullscreenContext: IContextKey<boolean>;
|
||||
private isMainWindowFullscreenContext: IContextKey<boolean>;
|
||||
private isAuxiliaryWindowFocusedContext: IContextKey<boolean>;
|
||||
private isCenteredLayoutContext: IContextKey<boolean>;
|
||||
private sideBarVisibleContext: IContextKey<boolean>;
|
||||
@@ -188,7 +189,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
this.updateSplitEditorsVerticallyContext();
|
||||
|
||||
// Window
|
||||
this.isFullscreenContext = IsFullscreenContext.bindTo(this.contextKeyService);
|
||||
this.isMainWindowFullscreenContext = IsMainWindowFullscreenContext.bindTo(this.contextKeyService);
|
||||
this.isAuxiliaryWindowFocusedContext = IsAuxiliaryWindowFocusedContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Zen Mode
|
||||
@@ -227,7 +228,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.editorGroupService.mainPart.whenReady.then(() => {
|
||||
this.editorGroupService.whenReady.then(() => {
|
||||
this.updateEditorAreaContextKeys();
|
||||
this.updateEditorContextKeys();
|
||||
});
|
||||
@@ -260,7 +261,11 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
|
||||
this._register(this.layoutService.onDidChangeZenMode(enabled => this.inZenModeContext.set(enabled)));
|
||||
this._register(this.layoutService.onDidChangeActiveContainer(() => this.isAuxiliaryWindowFocusedContext.set(this.layoutService.activeContainer !== this.layoutService.mainContainer)));
|
||||
this._register(this.layoutService.onDidChangeFullscreen(fullscreen => this.isFullscreenContext.set(fullscreen)));
|
||||
this._register(onDidChangeFullscreen(windowId => {
|
||||
if (windowId === mainWindow.vscodeWindowId) {
|
||||
this.isMainWindowFullscreenContext.set(isFullscreen(mainWindow));
|
||||
}
|
||||
}));
|
||||
this._register(this.layoutService.onDidChangeCenteredLayout(centered => this.isCenteredLayoutContext.set(centered)));
|
||||
this._register(this.layoutService.onDidChangePanelPosition(position => this.panelPositionContext.set(position)));
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ import { mainWindow } from 'vs/base/browser/window';
|
||||
|
||||
interface ILayoutRuntimeState {
|
||||
activeContainerId: number;
|
||||
fullscreen: boolean;
|
||||
mainWindowFullscreen: boolean;
|
||||
readonly maximized: Set<number>;
|
||||
hasFocus: boolean;
|
||||
mainWindowBorder: boolean;
|
||||
@@ -125,9 +125,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
private readonly _onDidChangeZenMode = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeZenMode = this._onDidChangeZenMode.event;
|
||||
|
||||
private readonly _onDidChangeFullscreen = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeFullscreen = this._onDidChangeFullscreen.event;
|
||||
|
||||
private readonly _onDidChangeCenteredLayout = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeCenteredLayout = this._onDidChangeCenteredLayout.event;
|
||||
|
||||
@@ -333,7 +330,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// Wait to register these listeners after the editor group service
|
||||
// is ready to avoid conflicts on startup
|
||||
this.editorGroupService.mainPart.whenRestored.then(() => {
|
||||
this.editorGroupService.whenRestored.then(() => {
|
||||
|
||||
// Restore main editor part on any editor change in main part
|
||||
this._register(this.mainPartEditorService.onDidVisibleEditorsChange(showEditorIfHidden));
|
||||
@@ -358,7 +355,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}));
|
||||
|
||||
// Fullscreen changes
|
||||
this._register(onDidChangeFullscreen(() => this.onFullscreenChanged()));
|
||||
this._register(onDidChangeFullscreen(windowId => this.onFullscreenChanged(windowId)));
|
||||
|
||||
// Group changes
|
||||
this._register(this.editorGroupService.mainPart.onDidAddGroup(() => this.centerMainEditorLayout(this.stateModel.getRuntimeValue(LayoutStateKeys.EDITOR_CENTERED))));
|
||||
@@ -408,7 +405,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}
|
||||
|
||||
// The menu bar toggles the title bar in full screen for toggle and classic settings
|
||||
else if (this.state.runtime.fullscreen && (menuBarVisibility === 'toggle' || menuBarVisibility === 'classic')) {
|
||||
else if (this.state.runtime.mainWindowFullscreen && (menuBarVisibility === 'toggle' || menuBarVisibility === 'classic')) {
|
||||
this.workbenchGrid.setViewVisible(this.titleBarPartView, this.shouldShowTitleBar());
|
||||
}
|
||||
|
||||
@@ -431,11 +428,15 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this._onDidLayoutContainer.fire({ container, dimension });
|
||||
}
|
||||
|
||||
private onFullscreenChanged(): void {
|
||||
this.state.runtime.fullscreen = isFullscreen();
|
||||
private onFullscreenChanged(windowId: number): void {
|
||||
if (windowId !== mainWindow.vscodeWindowId) {
|
||||
return; // ignore all but main window
|
||||
}
|
||||
|
||||
this.state.runtime.mainWindowFullscreen = isFullscreen(mainWindow);
|
||||
|
||||
// Apply as CSS class
|
||||
if (this.state.runtime.fullscreen) {
|
||||
if (this.state.runtime.mainWindowFullscreen) {
|
||||
this.mainContainer.classList.add(LayoutClasses.FULLSCREEN);
|
||||
} else {
|
||||
this.mainContainer.classList.remove(LayoutClasses.FULLSCREEN);
|
||||
@@ -448,9 +449,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}
|
||||
|
||||
// Change edge snapping accordingly
|
||||
this.workbenchGrid.edgeSnapping = this.state.runtime.fullscreen;
|
||||
this.workbenchGrid.edgeSnapping = this.state.runtime.mainWindowFullscreen;
|
||||
|
||||
// Changing fullscreen state of the window has an impact
|
||||
// Changing fullscreen state of the main window has an impact
|
||||
// on custom title bar visibility, so we need to update
|
||||
if (getTitleBarStyle(this.configurationService) === 'custom') {
|
||||
|
||||
@@ -459,8 +460,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
this.updateWindowsBorder(true);
|
||||
}
|
||||
|
||||
this._onDidChangeFullscreen.fire(this.state.runtime.fullscreen);
|
||||
}
|
||||
|
||||
private onActiveWindowChanged(): void {
|
||||
@@ -494,7 +493,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.updateMenubarVisibility(!!skipLayout);
|
||||
|
||||
// Centered Layout
|
||||
this.editorGroupService.mainPart.whenRestored.then(() => {
|
||||
this.editorGroupService.whenRestored.then(() => {
|
||||
this.centerMainEditorLayout(this.stateModel.getRuntimeValue(LayoutStateKeys.EDITOR_CENTERED), skipLayout);
|
||||
});
|
||||
}
|
||||
@@ -554,7 +553,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
const containerWindowId = getWindowId(getWindow(container));
|
||||
|
||||
let windowBorder = false;
|
||||
if (!this.state.runtime.fullscreen && !this.state.runtime.maximized.has(containerWindowId) && (activeBorder || inactiveBorder)) {
|
||||
if (!this.state.runtime.mainWindowFullscreen && !this.state.runtime.maximized.has(containerWindowId) && (activeBorder || inactiveBorder)) {
|
||||
windowBorder = true;
|
||||
|
||||
// If the inactive color is missing, fallback to the active one
|
||||
@@ -629,7 +628,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
// Layout Runtime State
|
||||
const layoutRuntimeState: ILayoutRuntimeState = {
|
||||
activeContainerId: this.getActiveContainerId(),
|
||||
fullscreen: isFullscreen(),
|
||||
mainWindowFullscreen: isFullscreen(mainWindow),
|
||||
hasFocus: this.hostService.hasFocus,
|
||||
maximized: new Set<number>(),
|
||||
mainWindowBorder: false,
|
||||
@@ -781,7 +780,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// Empty workbench configured to open untitled file if empty
|
||||
else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.configurationService.getValue('workbench.startupEditor') === 'newUntitledFile') {
|
||||
if (this.editorGroupService.mainPart.hasRestorableState) {
|
||||
if (this.editorGroupService.hasRestorableState) {
|
||||
return []; // do not open any empty untitled file if we restored groups/editors from previous session
|
||||
}
|
||||
|
||||
@@ -854,7 +853,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
mark('code/willRestoreEditors');
|
||||
|
||||
// first ensure the editor part is ready
|
||||
await this.editorGroupService.mainPart.whenReady;
|
||||
await this.editorGroupService.whenReady;
|
||||
mark('code/restoreEditors/editorGroupsReady');
|
||||
|
||||
// apply editor layout if any
|
||||
@@ -910,7 +909,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
layoutRestoredPromises.push(
|
||||
Promise.all([
|
||||
openEditorsPromise?.finally(() => mark('code/restoreEditors/editorsOpened')),
|
||||
this.editorGroupService.mainPart.whenRestored.finally(() => mark('code/restoreEditors/editorGroupsRestored'))
|
||||
this.editorGroupService.whenRestored.finally(() => mark('code/restoreEditors/editorGroupsRestored'))
|
||||
]).finally(() => {
|
||||
// the `code/didRestoreEditors` perf mark is specifically
|
||||
// for when visible editors have resolved, so we only mark
|
||||
@@ -1237,23 +1236,23 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// macOS desktop does not need a title bar when full screen
|
||||
if (isMacintosh && isNative) {
|
||||
return !this.state.runtime.fullscreen;
|
||||
return !this.state.runtime.mainWindowFullscreen;
|
||||
}
|
||||
|
||||
// non-fullscreen native must show the title bar
|
||||
if (isNative && !this.state.runtime.fullscreen) {
|
||||
if (isNative && !this.state.runtime.mainWindowFullscreen) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if WCO is visible, we have to show the title bar
|
||||
if (isWCOEnabled() && !this.state.runtime.fullscreen) {
|
||||
if (isWCOEnabled() && !this.state.runtime.mainWindowFullscreen) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// remaining behavior is based on menubar visibility
|
||||
switch (getMenuBarVisibility(this.configurationService)) {
|
||||
case 'classic':
|
||||
return !this.state.runtime.fullscreen || this.state.runtime.menuBar.toggled;
|
||||
return !this.state.runtime.mainWindowFullscreen || this.state.runtime.menuBar.toggled;
|
||||
case 'compact':
|
||||
case 'hidden':
|
||||
return false;
|
||||
@@ -1262,7 +1261,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
case 'visible':
|
||||
return true;
|
||||
default:
|
||||
return isWeb ? false : !this.state.runtime.fullscreen || this.state.runtime.menuBar.toggled;
|
||||
return isWeb ? false : !this.state.runtime.mainWindowFullscreen || this.state.runtime.menuBar.toggled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1274,6 +1273,15 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.focusPart(Parts.EDITOR_PART, getWindow(this.activeContainer));
|
||||
}
|
||||
|
||||
private focusPanelOrEditor(): void {
|
||||
const activePanel = this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel);
|
||||
if ((this.hasFocus(Parts.PANEL_PART) || !this.isVisible(Parts.EDITOR_PART)) && activePanel) {
|
||||
activePanel.focus(); // prefer panel if it has focus or editor is hidden
|
||||
} else {
|
||||
this.focus(); // otherwise focus editor
|
||||
}
|
||||
}
|
||||
|
||||
getMaximumEditorDimensions(container: HTMLElement): IDimension {
|
||||
const targetWindow = getWindow(container);
|
||||
const containerDimension = this.getContainerDimension(container);
|
||||
@@ -1327,17 +1335,17 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// Check if zen mode transitioned to full screen and if now we are out of zen mode
|
||||
// -> we need to go out of full screen (same goes for the centered editor layout)
|
||||
let toggleFullScreen = false;
|
||||
let toggleMainWindowFullScreen = false;
|
||||
const config = getZenModeConfiguration(this.configurationService);
|
||||
const zenModeExitInfo = this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_EXIT_INFO);
|
||||
|
||||
// Zen Mode Active
|
||||
if (this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE)) {
|
||||
|
||||
toggleFullScreen = !this.state.runtime.fullscreen && config.fullScreen && !isIOS;
|
||||
toggleMainWindowFullScreen = !this.state.runtime.mainWindowFullscreen && config.fullScreen && !isIOS;
|
||||
|
||||
if (!restoring) {
|
||||
zenModeExitInfo.transitionedToFullScreen = toggleFullScreen;
|
||||
zenModeExitInfo.transitionedToFullScreen = toggleMainWindowFullScreen;
|
||||
zenModeExitInfo.transitionedToCenteredEditorLayout = !this.isMainEditorLayoutCentered() && config.centerLayout;
|
||||
zenModeExitInfo.handleNotificationsDoNotDisturbMode = this.notificationService.getFilter() === NotificationsFilter.OFF;
|
||||
zenModeExitInfo.wasVisible.sideBar = this.isVisible(Parts.SIDEBAR_PART);
|
||||
@@ -1452,15 +1460,15 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
this.focus();
|
||||
|
||||
toggleFullScreen = zenModeExitInfo.transitionedToFullScreen && this.state.runtime.fullscreen;
|
||||
toggleMainWindowFullScreen = zenModeExitInfo.transitionedToFullScreen && this.state.runtime.mainWindowFullscreen;
|
||||
}
|
||||
|
||||
if (!skipLayout) {
|
||||
this.layout();
|
||||
}
|
||||
|
||||
if (toggleFullScreen) {
|
||||
this.hostService.toggleFullScreen(getActiveWindow());
|
||||
if (toggleMainWindowFullScreen) {
|
||||
this.hostService.toggleFullScreen(mainWindow);
|
||||
}
|
||||
|
||||
// Event
|
||||
@@ -1522,7 +1530,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.mainContainer.prepend(workbenchGrid.element);
|
||||
this.mainContainer.setAttribute('role', 'application');
|
||||
this.workbenchGrid = workbenchGrid;
|
||||
this.workbenchGrid.edgeSnapping = this.state.runtime.fullscreen;
|
||||
this.workbenchGrid.edgeSnapping = this.state.runtime.mainWindowFullscreen;
|
||||
|
||||
for (const part of [titleBar, editorPart, activityBar, panelPart, sideBar, statusBar, auxiliaryBarPart, bannerPart]) {
|
||||
this._register(part.onDidVisibilityChange((visible) => {
|
||||
@@ -1729,7 +1737,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
!this.isVisible(Parts.PANEL_PART) ? LayoutClasses.PANEL_HIDDEN : undefined,
|
||||
!this.isVisible(Parts.AUXILIARYBAR_PART) ? LayoutClasses.AUXILIARYBAR_HIDDEN : undefined,
|
||||
!this.isVisible(Parts.STATUSBAR_PART) ? LayoutClasses.STATUSBAR_HIDDEN : undefined,
|
||||
this.state.runtime.fullscreen ? LayoutClasses.FULLSCREEN : undefined
|
||||
this.state.runtime.mainWindowFullscreen ? LayoutClasses.FULLSCREEN : undefined
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1746,14 +1754,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
// If sidebar becomes hidden, also hide the current active Viewlet if any
|
||||
if (hidden && this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar)) {
|
||||
this.paneCompositeService.hideActivePaneComposite(ViewContainerLocation.Sidebar);
|
||||
|
||||
// Pass Focus to Editor or Panel if Sidebar is now hidden
|
||||
const activePanel = this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel);
|
||||
if (this.hasFocus(Parts.PANEL_PART) && activePanel) {
|
||||
activePanel.focus();
|
||||
} else {
|
||||
this.focus();
|
||||
}
|
||||
this.focusPanelOrEditor();
|
||||
}
|
||||
|
||||
// If sidebar becomes visible, show last active Viewlet or default viewlet
|
||||
@@ -1986,14 +1987,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
// If auxiliary bar becomes hidden, also hide the current active pane composite if any
|
||||
if (hidden && this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.AuxiliaryBar)) {
|
||||
this.paneCompositeService.hideActivePaneComposite(ViewContainerLocation.AuxiliaryBar);
|
||||
|
||||
// Pass Focus to Editor or Panel if Auxiliary Bar is now hidden
|
||||
const activePanel = this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel);
|
||||
if (this.hasFocus(Parts.PANEL_PART) && activePanel) {
|
||||
activePanel.focus();
|
||||
} else {
|
||||
this.focus();
|
||||
}
|
||||
this.focusPanelOrEditor();
|
||||
}
|
||||
|
||||
// If auxiliary bar becomes visible, show last active pane composite or default pane composite
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ISerializableView, IViewSize } from 'vs/base/browser/ui/grid/grid';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IPartOptions {
|
||||
readonly hasTitle?: boolean;
|
||||
@@ -187,7 +187,7 @@ export interface IMultiWindowPart {
|
||||
readonly element: HTMLElement;
|
||||
}
|
||||
|
||||
export abstract class MultiWindowParts<T extends IMultiWindowPart> extends Disposable {
|
||||
export abstract class MultiWindowParts<T extends IMultiWindowPart> extends Component {
|
||||
|
||||
protected readonly _parts = new Set<T>();
|
||||
get parts() { return Array.from(this._parts); }
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { hide, show } from 'vs/base/browser/dom';
|
||||
import { onDidChangeFullscreen } from 'vs/base/browser/browser';
|
||||
import { detectFullscreen, hide, show } from 'vs/base/browser/dom';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { isNative } from 'vs/base/common/platform';
|
||||
@@ -15,7 +16,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { getTitleBarStyle } from 'vs/platform/window/common/window';
|
||||
import { IEditorGroupView, IEditorPartsView } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
|
||||
import { EditorPart, IEditorPartUIState } from 'vs/workbench/browser/parts/editor/editorPart';
|
||||
import { IAuxiliaryTitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart';
|
||||
import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle';
|
||||
import { IAuxiliaryWindowOpenOptions, IAuxiliaryWindowService } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
|
||||
@@ -27,6 +28,16 @@ import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecy
|
||||
import { IStatusbarService } from 'vs/workbench/services/statusbar/browser/statusbar';
|
||||
import { ITitleService } from 'vs/workbench/services/title/browser/titleService';
|
||||
|
||||
export interface IAuxiliaryEditorPartOpenOptions extends IAuxiliaryWindowOpenOptions {
|
||||
readonly state?: IEditorPartUIState;
|
||||
}
|
||||
|
||||
export interface ICreateAuxiliaryEditorPartResult {
|
||||
readonly part: AuxiliaryEditorPartImpl;
|
||||
readonly instantiationService: IInstantiationService;
|
||||
readonly disposables: DisposableStore;
|
||||
}
|
||||
|
||||
export class AuxiliaryEditorPart {
|
||||
|
||||
private static STATUS_BAR_VISIBILITY = 'workbench.statusBar.visible';
|
||||
@@ -43,7 +54,7 @@ export class AuxiliaryEditorPart {
|
||||
) {
|
||||
}
|
||||
|
||||
async create(label: string, options?: IAuxiliaryWindowOpenOptions): Promise<{ readonly part: AuxiliaryEditorPartImpl; readonly instantiationService: IInstantiationService; readonly disposables: DisposableStore }> {
|
||||
async create(label: string, options?: IAuxiliaryEditorPartOpenOptions): Promise<ICreateAuxiliaryEditorPartResult> {
|
||||
|
||||
function computeEditorPartHeightOffset(): number {
|
||||
let editorPartHeightOffset = 0;
|
||||
@@ -52,7 +63,7 @@ export class AuxiliaryEditorPart {
|
||||
editorPartHeightOffset += statusbarPart.height;
|
||||
}
|
||||
|
||||
if (titlebarPart) {
|
||||
if (titlebarPart && titlebarPartVisible) {
|
||||
editorPartHeightOffset += titlebarPart.height;
|
||||
}
|
||||
|
||||
@@ -89,16 +100,37 @@ export class AuxiliaryEditorPart {
|
||||
editorPartContainer.style.position = 'relative';
|
||||
auxiliaryWindow.container.appendChild(editorPartContainer);
|
||||
|
||||
const editorPart = disposables.add(this.instantiationService.createInstance(AuxiliaryEditorPartImpl, auxiliaryWindow.window.vscodeWindowId, this.editorPartsView, label));
|
||||
const editorPart = disposables.add(this.instantiationService.createInstance(AuxiliaryEditorPartImpl, auxiliaryWindow.window.vscodeWindowId, this.editorPartsView, options?.state, label));
|
||||
disposables.add(this.editorPartsView.registerPart(editorPart));
|
||||
editorPart.create(editorPartContainer, { restorePreviousState: false });
|
||||
editorPart.create(editorPartContainer);
|
||||
|
||||
// Titlebar
|
||||
let titlebarPart: IAuxiliaryTitlebarPart | undefined = undefined;
|
||||
let titlebarPartVisible = false;
|
||||
const useCustomTitle = isNative && getTitleBarStyle(this.configurationService) === 'custom'; // custom title in aux windows only enabled in native
|
||||
if (useCustomTitle) {
|
||||
titlebarPart = disposables.add(this.titleService.createAuxiliaryTitlebarPart(auxiliaryWindow.container, editorPart));
|
||||
titlebarPartVisible = true;
|
||||
|
||||
disposables.add(titlebarPart.onDidChange(() => updateEditorPartHeight(true)));
|
||||
|
||||
disposables.add(onDidChangeFullscreen(windowId => {
|
||||
if (windowId !== auxiliaryWindow.window.vscodeWindowId) {
|
||||
return; // ignore all but our window
|
||||
}
|
||||
|
||||
// Make sure to hide the custom title when we enter
|
||||
// fullscren mode and show it when we lave it.
|
||||
|
||||
const fullscreen = detectFullscreen(auxiliaryWindow.window);
|
||||
const oldTitlebarPartVisible = titlebarPartVisible;
|
||||
titlebarPartVisible = !fullscreen;
|
||||
if (titlebarPart && oldTitlebarPartVisible !== titlebarPartVisible) {
|
||||
titlebarPart.container.style.display = titlebarPartVisible ? '' : 'none';
|
||||
|
||||
updateEditorPartHeight(true);
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
disposables.add(this.instantiationService.createInstance(WindowTitle, auxiliaryWindow.window, editorPart));
|
||||
}
|
||||
@@ -118,7 +150,7 @@ export class AuxiliaryEditorPart {
|
||||
|
||||
// Lifecycle
|
||||
const editorCloseListener = disposables.add(Event.once(editorPart.onWillClose)(() => auxiliaryWindow.window.close()));
|
||||
disposables.add(Event.once(auxiliaryWindow.onWillClose)(() => {
|
||||
disposables.add(Event.once(auxiliaryWindow.onUnload)(() => {
|
||||
if (disposables.isDisposed) {
|
||||
return; // the close happened as part of an earlier dispose call
|
||||
}
|
||||
@@ -132,9 +164,7 @@ export class AuxiliaryEditorPart {
|
||||
// Layout
|
||||
disposables.add(auxiliaryWindow.onDidLayout(dimension => {
|
||||
const titlebarPartHeight = titlebarPart?.height ?? 0;
|
||||
if (titlebarPart) {
|
||||
titlebarPart.layout(dimension.width, titlebarPartHeight, 0, 0);
|
||||
}
|
||||
titlebarPart?.layout(dimension.width, titlebarPartHeight, 0, 0);
|
||||
|
||||
const editorPartHeight = dimension.height - computeEditorPartHeightOffset();
|
||||
editorPart.layout(dimension.width, editorPartHeight, titlebarPartHeight, 0);
|
||||
@@ -167,6 +197,7 @@ class AuxiliaryEditorPartImpl extends EditorPart implements IAuxiliaryEditorPart
|
||||
constructor(
|
||||
readonly windowId: number,
|
||||
editorPartsView: IEditorPartsView,
|
||||
private readonly state: IEditorPartUIState | undefined,
|
||||
groupsLabel: string,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -211,8 +242,12 @@ class AuxiliaryEditorPartImpl extends EditorPart implements IAuxiliaryEditorPart
|
||||
this.doClose(false /* do not merge any groups to main part */);
|
||||
}
|
||||
|
||||
protected override loadState(): IEditorPartUIState | undefined {
|
||||
return this.state;
|
||||
}
|
||||
|
||||
protected override saveState(): void {
|
||||
return; // TODO support auxiliary editor state
|
||||
return; // disabled, auxiliary editor part state is tracked outside
|
||||
}
|
||||
|
||||
close(): void {
|
||||
|
||||
@@ -10,7 +10,8 @@ import { IEditorFactoryRegistry, EditorExtensions } from 'vs/workbench/common/ed
|
||||
import {
|
||||
TextCompareEditorActiveContext, ActiveEditorPinnedContext, EditorGroupEditorsCountContext, ActiveEditorStickyContext, ActiveEditorAvailableEditorIdsContext,
|
||||
EditorPartMultipleEditorGroupsContext, ActiveEditorDirtyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext,
|
||||
EditorTabsVisibleContext, ActiveEditorLastInGroupContext, EditorPartMaximizedEditorGroupContext, MultipleEditorGroupsContext, InEditorZenModeContext, IsAuxiliaryEditorPartContext, ActiveCompareEditorOriginalWriteableContext
|
||||
EditorTabsVisibleContext, ActiveEditorLastInGroupContext, EditorPartMaximizedEditorGroupContext, MultipleEditorGroupsContext, InEditorZenModeContext,
|
||||
IsAuxiliaryEditorPartContext, ActiveCompareEditorOriginalWriteableContext
|
||||
} from 'vs/workbench/common/contextkeys';
|
||||
import { SideBySideEditorInput, SideBySideEditorInputSerializer } from 'vs/workbench/common/editor/sideBySideEditorInput';
|
||||
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
|
||||
@@ -41,13 +42,16 @@ import {
|
||||
ReOpenInTextEditorAction, DuplicateGroupDownAction, DuplicateGroupLeftAction, DuplicateGroupRightAction, DuplicateGroupUpAction, ToggleEditorTypeAction, SplitEditorToAboveGroupAction, SplitEditorToBelowGroupAction,
|
||||
SplitEditorToFirstGroupAction, SplitEditorToLastGroupAction, SplitEditorToLeftGroupAction, SplitEditorToNextGroupAction, SplitEditorToPreviousGroupAction, SplitEditorToRightGroupAction, NavigateForwardInEditsAction,
|
||||
NavigateBackwardsInEditsAction, NavigateForwardInNavigationsAction, NavigateBackwardsInNavigationsAction, NavigatePreviousInNavigationsAction, NavigatePreviousInEditsAction, NavigateToLastNavigationLocationAction,
|
||||
MaximizeGroupHideSidebarAction, MoveEditorToNewWindowAction, CopyEditorToNewindowAction, RestoreEditorsToMainWindowAction, ToggleMaximizeEditorGroupAction, MinimizeOtherGroupsHideSidebarAction, CopyEditorGroupToNewWindowAction, MoveEditorGroupToNewWindowAction, NewEmptyEditorWindowAction
|
||||
MaximizeGroupHideSidebarAction, MoveEditorToNewWindowAction, CopyEditorToNewindowAction, RestoreEditorsToMainWindowAction, ToggleMaximizeEditorGroupAction, MinimizeOtherGroupsHideSidebarAction, CopyEditorGroupToNewWindowAction,
|
||||
MoveEditorGroupToNewWindowAction, NewEmptyEditorWindowAction
|
||||
} from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import {
|
||||
CLOSE_EDITORS_AND_GROUP_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_EDITOR_GROUP_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
CLOSE_PINNED_EDITOR_COMMAND_ID, CLOSE_SAVED_EDITORS_COMMAND_ID, GOTO_NEXT_CHANGE, GOTO_PREVIOUS_CHANGE, KEEP_EDITOR_COMMAND_ID, PIN_EDITOR_COMMAND_ID, SHOW_EDITORS_IN_GROUP, SPLIT_EDITOR_DOWN, SPLIT_EDITOR_LEFT,
|
||||
SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, TOGGLE_DIFF_SIDE_BY_SIDE, TOGGLE_KEEP_EDITORS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, setup as registerEditorCommands, REOPEN_WITH_COMMAND_ID,
|
||||
TOGGLE_LOCK_GROUP_COMMAND_ID, UNLOCK_GROUP_COMMAND_ID, SPLIT_EDITOR_IN_GROUP, JOIN_EDITOR_IN_GROUP, FOCUS_FIRST_SIDE_EDITOR, FOCUS_SECOND_SIDE_EDITOR, TOGGLE_SPLIT_EDITOR_IN_GROUP_LAYOUT, LOCK_GROUP_COMMAND_ID, SPLIT_EDITOR, TOGGLE_MAXIMIZE_EDITOR_GROUP, MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, MOVE_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID, DIFF_SWAP_SIDES
|
||||
TOGGLE_LOCK_GROUP_COMMAND_ID, UNLOCK_GROUP_COMMAND_ID, SPLIT_EDITOR_IN_GROUP, JOIN_EDITOR_IN_GROUP, FOCUS_FIRST_SIDE_EDITOR, FOCUS_SECOND_SIDE_EDITOR, TOGGLE_SPLIT_EDITOR_IN_GROUP_LAYOUT, LOCK_GROUP_COMMAND_ID,
|
||||
SPLIT_EDITOR, TOGGLE_MAXIMIZE_EDITOR_GROUP, MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, MOVE_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID,
|
||||
NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID, DIFF_SWAP_SIDES
|
||||
} from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { inQuickPickContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
@@ -394,10 +398,10 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_ED
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_DOWN, title: localize('splitDown', "Split Down") }, group: '5_split', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_LEFT, title: localize('splitLeft', "Split Left") }, group: '5_split', order: 30 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_RIGHT, title: localize('splitRight', "Split Right") }, group: '5_split', order: 40 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_IN_GROUP, title: localize('splitInGroup', "Split in Group") }, group: '5_split', order: 50, when: ActiveEditorCanSplitInGroupContext });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: JOIN_EDITOR_IN_GROUP, title: localize('joinInGroup', "Join in Group") }, group: '5_split', order: 50, when: SideBySideEditorActiveContext });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, title: localize('moveToNewWindow', "Move into New Window") }, group: '6_new_window', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, title: localize('copyToNewWindow', "Copy into New Window") }, group: '6_new_window', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_IN_GROUP, title: localize('splitInGroup', "Split in Group") }, group: '6_split_in_group', order: 10, when: ActiveEditorCanSplitInGroupContext });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: JOIN_EDITOR_IN_GROUP, title: localize('joinInGroup', "Join in Group") }, group: '6_split_in_group', order: 10, when: SideBySideEditorActiveContext });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, title: localize('moveToNewWindow', "Move into New Window") }, group: '7_new_window', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, title: localize('copyToNewWindow', "Copy into New Window") }, group: '7_new_window', order: 20 });
|
||||
|
||||
// Editor Title Menu
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_DIFF_SIDE_BY_SIDE, title: localize('inlineView', "Inline View"), toggled: ContextKeyExpr.equals('config.diffEditor.renderSideBySide', false) }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') });
|
||||
|
||||
@@ -30,7 +30,7 @@ import { Action2, IAction2Options, MenuId } from 'vs/platform/actions/common/act
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IKeybindingRule, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
|
||||
import { ActiveEditorAvailableEditorIdsContext, ActiveEditorContext, ActiveEditorGroupEmptyContext, AuxiliaryBarVisibleContext, EditorPartMaximizedEditorGroupContext, EditorPartMultipleEditorGroupsContext, IsAuxiliaryWindowFocusedContext, MultipleEditorGroupsContext, SideBarVisibleContext } from 'vs/workbench/common/contextkeys';
|
||||
@@ -2493,6 +2493,7 @@ abstract class BaseMoveCopyEditorToNewWindowAction extends Action2 {
|
||||
constructor(
|
||||
id: string,
|
||||
title: ICommandActionTitle,
|
||||
keybinding: Omit<IKeybindingRule, 'id'> | undefined,
|
||||
private readonly move: boolean
|
||||
) {
|
||||
super({
|
||||
@@ -2500,6 +2501,7 @@ abstract class BaseMoveCopyEditorToNewWindowAction extends Action2 {
|
||||
title,
|
||||
category: Categories.View,
|
||||
precondition: ActiveEditorContext,
|
||||
keybinding,
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
@@ -2532,6 +2534,7 @@ export class MoveEditorToNewWindowAction extends BaseMoveCopyEditorToNewWindowAc
|
||||
mnemonicTitle: localize({ key: 'miMoveEditorToNewWindow', comment: ['&& denotes a mnemonic'] }, "&&Move Editor into New Window"),
|
||||
original: 'Move Editor into New Window'
|
||||
},
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
}
|
||||
@@ -2547,6 +2550,7 @@ export class CopyEditorToNewindowAction extends BaseMoveCopyEditorToNewWindowAct
|
||||
mnemonicTitle: localize({ key: 'miCopyEditorToNewWindow', comment: ['&& denotes a mnemonic'] }, "&&Copy Editor into New Window"),
|
||||
original: 'Copy Editor into New Window'
|
||||
},
|
||||
{ primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyO), weight: KeybindingWeight.WorkbenchContrib },
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import { EditorGroupWatermark } from 'vs/workbench/browser/parts/editor/editorGr
|
||||
import { EditorTitleControl } from 'vs/workbench/browser/parts/editor/editorTitleControl';
|
||||
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
|
||||
import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
|
||||
export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
@@ -154,7 +155,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService,
|
||||
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IEditorResolverService private readonly editorResolverService: IEditorResolverService
|
||||
@IEditorResolverService private readonly editorResolverService: IEditorResolverService,
|
||||
@IHostService private readonly hostService: IHostService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
@@ -507,13 +509,17 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
options.pinned = this.model.isPinned(activeEditor); // preserve pinned state
|
||||
options.sticky = this.model.isSticky(activeEditor); // preserve sticky state
|
||||
options.preserveFocus = true; // handle focus after editor is opened
|
||||
options.preserveFocus = true; // handle focus after editor is restored
|
||||
|
||||
const internalOptions: IInternalEditorOpenOptions = {
|
||||
preserveWindowOrder: true // handle window order after editor is restored
|
||||
};
|
||||
|
||||
const activeElement = getActiveElement();
|
||||
|
||||
// Show active editor (intentionally not using async to keep
|
||||
// `restoreEditors` from executing in same stack)
|
||||
return this.doShowEditor(activeEditor, { active: true, isNew: false /* restored */ }, options).then(() => {
|
||||
return this.doShowEditor(activeEditor, { active: true, isNew: false /* restored */ }, options, internalOptions).then(() => {
|
||||
|
||||
// Set focused now if this is the active group and focus has
|
||||
// not changed meanwhile. This prevents focus from being
|
||||
@@ -1601,6 +1607,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
await this.doOpenEditor(editor);
|
||||
}
|
||||
|
||||
// Ensure our window has focus since we are about to show a dialog
|
||||
await this.hostService.focus(getWindow(this.element));
|
||||
|
||||
// Let editor handle confirmation if implemented
|
||||
if (typeof editor.closeHandler?.confirm === 'function') {
|
||||
confirmation = await editor.closeHandler.confirm([{ editor, groupId: this.id }]);
|
||||
|
||||
@@ -36,7 +36,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { EditorPartMaximizedEditorGroupContext, EditorPartMultipleEditorGroupsContext, IsAuxiliaryEditorPartContext } from 'vs/workbench/common/contextkeys';
|
||||
|
||||
interface IEditorPartUIState {
|
||||
export interface IEditorPartUIState {
|
||||
readonly serializedGrid: ISerializedGrid;
|
||||
readonly activeGroup: GroupIdentifier;
|
||||
readonly mostRecentActiveGroups: GroupIdentifier[];
|
||||
@@ -251,6 +251,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView {
|
||||
return !!this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY];
|
||||
}
|
||||
|
||||
private _willRestoreState = false;
|
||||
get willRestoreState(): boolean { return this._willRestoreState; }
|
||||
|
||||
getGroups(order = GroupsOrder.CREATION_TIME): IEditorGroupView[] {
|
||||
switch (order) {
|
||||
case GroupsOrder.CREATION_TIME:
|
||||
@@ -983,7 +986,8 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView {
|
||||
));
|
||||
|
||||
// Grid control
|
||||
this.doCreateGridControl(options);
|
||||
this._willRestoreState = !options || options.restorePreviousState;
|
||||
this.doCreateGridControl();
|
||||
|
||||
// Centered layout widget
|
||||
this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.gridWidgetView, this.profileMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY], this._partOptions.centeredLayoutFixedWidth));
|
||||
@@ -1142,11 +1146,11 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView {
|
||||
return false;
|
||||
}
|
||||
|
||||
private doCreateGridControl(options?: IEditorPartCreationOptions): void {
|
||||
private doCreateGridControl(): void {
|
||||
|
||||
// Grid Widget (with previous UI state)
|
||||
let restoreError = false;
|
||||
if (!options || options.restorePreviousState) {
|
||||
if (this._willRestoreState) {
|
||||
restoreError = !this.doCreateGridControlWithPreviousState();
|
||||
}
|
||||
|
||||
@@ -1167,7 +1171,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView {
|
||||
}
|
||||
|
||||
private doCreateGridControlWithPreviousState(): boolean {
|
||||
const uiState: IEditorPartUIState = this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY];
|
||||
const uiState: IEditorPartUIState | undefined = this.loadState();
|
||||
if (uiState?.serializedGrid) {
|
||||
try {
|
||||
|
||||
@@ -1176,9 +1180,6 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView {
|
||||
|
||||
// Grid Widget
|
||||
this.doCreateGridControlWithState(uiState.serializedGrid, uiState.activeGroup);
|
||||
|
||||
// Ensure last active group has focus
|
||||
this._activeGroup.focus();
|
||||
} catch (error) {
|
||||
|
||||
// Log error
|
||||
@@ -1312,16 +1313,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView {
|
||||
|
||||
// Persist grid UI state
|
||||
if (this.gridWidget) {
|
||||
const uiState: IEditorPartUIState = {
|
||||
serializedGrid: this.gridWidget.serialize(),
|
||||
activeGroup: this._activeGroup.id,
|
||||
mostRecentActiveGroups: this.mostRecentActiveGroups
|
||||
};
|
||||
|
||||
if (this.isEmpty) {
|
||||
delete this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY];
|
||||
} else {
|
||||
this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] = uiState;
|
||||
this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] = this.createState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1338,6 +1333,18 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView {
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
protected loadState(): IEditorPartUIState | undefined {
|
||||
return this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY];
|
||||
}
|
||||
|
||||
createState(): IEditorPartUIState {
|
||||
return {
|
||||
serializedGrid: this.gridWidget.serialize(),
|
||||
activeGroup: this._activeGroup.id,
|
||||
mostRecentActiveGroups: this.mostRecentActiveGroups
|
||||
};
|
||||
}
|
||||
|
||||
toJSON(): object {
|
||||
return {
|
||||
type: Parts.EDITOR_PART
|
||||
|
||||
@@ -8,14 +8,30 @@ import { EditorGroupLayout, GroupDirection, GroupLocation, GroupOrientation, Gro
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
import { EditorPart, MainEditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
|
||||
import { EditorPart, IEditorPartUIState, MainEditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
|
||||
import { IEditorGroupView, IEditorPartsView } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IAuxiliaryWindowOpenOptions } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { AuxiliaryEditorPart } from 'vs/workbench/browser/parts/editor/auxiliaryEditorPart';
|
||||
import { distinct, firstOrDefault } from 'vs/base/common/arrays';
|
||||
import { AuxiliaryEditorPart, IAuxiliaryEditorPartOpenOptions } from 'vs/workbench/browser/parts/editor/auxiliaryEditorPart';
|
||||
import { MultiWindowParts } from 'vs/workbench/browser/part';
|
||||
import { DeferredPromise } from 'vs/base/common/async';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IRectangle } from 'vs/platform/window/common/window';
|
||||
import { getWindow } from 'vs/base/browser/dom';
|
||||
import { getZoomLevel } from 'vs/base/browser/browser';
|
||||
|
||||
interface IEditorPartsUIState {
|
||||
readonly auxiliary: IAuxiliaryEditorPartState[];
|
||||
readonly mru: number[];
|
||||
}
|
||||
|
||||
interface IAuxiliaryEditorPartState {
|
||||
readonly state: IEditorPartUIState;
|
||||
readonly bounds?: IRectangle;
|
||||
readonly zoomLevel?: number;
|
||||
}
|
||||
|
||||
export class EditorParts extends MultiWindowParts<EditorPart> implements IEditorGroupsService, IEditorPartsView {
|
||||
|
||||
@@ -23,14 +39,18 @@ export class EditorParts extends MultiWindowParts<EditorPart> implements IEditor
|
||||
|
||||
readonly mainPart = this._register(this.createMainEditorPart());
|
||||
|
||||
private readonly mostRecentActiveParts = [this.mainPart];
|
||||
private mostRecentActiveParts = [this.mainPart];
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IThemeService themeService: IThemeService
|
||||
) {
|
||||
super();
|
||||
super('workbench.editorParts', themeService, storageService);
|
||||
|
||||
this._register(this.registerPart(this.mainPart));
|
||||
|
||||
this.restoreParts();
|
||||
}
|
||||
|
||||
protected createMainEditorPart(): MainEditorPart {
|
||||
@@ -42,7 +62,7 @@ export class EditorParts extends MultiWindowParts<EditorPart> implements IEditor
|
||||
private readonly _onDidCreateAuxiliaryEditorPart = this._register(new Emitter<IAuxiliaryEditorPartCreateEvent>());
|
||||
readonly onDidCreateAuxiliaryEditorPart = this._onDidCreateAuxiliaryEditorPart.event;
|
||||
|
||||
async createAuxiliaryEditorPart(options?: IAuxiliaryWindowOpenOptions): Promise<IAuxiliaryEditorPart> {
|
||||
async createAuxiliaryEditorPart(options?: IAuxiliaryEditorPartOpenOptions): Promise<IAuxiliaryEditorPart> {
|
||||
const { part, instantiationService, disposables } = await this.instantiationService.createInstance(AuxiliaryEditorPart, this).create(this.getGroupsLabel(this._parts.size), options);
|
||||
|
||||
// Events
|
||||
@@ -156,6 +176,117 @@ export class EditorParts extends MultiWindowParts<EditorPart> implements IEditor
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Lifecycle / State
|
||||
|
||||
private static readonly EDITOR_PARTS_UI_STATE_STORAGE_KEY = 'editorparts.state';
|
||||
|
||||
private readonly workspaceMemento = this.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE);
|
||||
|
||||
private _isReady = false;
|
||||
get isReady(): boolean { return this._isReady; }
|
||||
|
||||
private readonly whenReadyPromise = new DeferredPromise<void>();
|
||||
readonly whenReady = this.whenReadyPromise.p;
|
||||
|
||||
private readonly whenRestoredPromise = new DeferredPromise<void>();
|
||||
readonly whenRestored = this.whenRestoredPromise.p;
|
||||
|
||||
private async restoreParts(): Promise<void> {
|
||||
|
||||
// Join on the main part being ready to pick
|
||||
// the right moment to begin restoring.
|
||||
// The main part is automatically being created
|
||||
// as part of the overall startup process.
|
||||
await this.mainPart.whenReady;
|
||||
|
||||
// Only attempt to restore auxiliary editor parts
|
||||
// when the main part did restore. It is possible
|
||||
// that restoring was not attempted because specific
|
||||
// editors were opened.
|
||||
if (this.mainPart.willRestoreState) {
|
||||
const uiState: IEditorPartsUIState | undefined = this.workspaceMemento[EditorParts.EDITOR_PARTS_UI_STATE_STORAGE_KEY];
|
||||
if (uiState?.auxiliary.length) {
|
||||
const auxiliaryEditorPartPromises: Promise<IAuxiliaryEditorPart>[] = [];
|
||||
|
||||
// Create auxiliary editor parts
|
||||
for (const auxiliaryEditorPartState of uiState.auxiliary) {
|
||||
auxiliaryEditorPartPromises.push(this.createAuxiliaryEditorPart({
|
||||
bounds: auxiliaryEditorPartState.bounds,
|
||||
state: auxiliaryEditorPartState.state,
|
||||
zoomLevel: auxiliaryEditorPartState.zoomLevel
|
||||
}));
|
||||
}
|
||||
|
||||
// Await creation
|
||||
await Promise.allSettled(auxiliaryEditorPartPromises);
|
||||
|
||||
// Update MRU list
|
||||
if (uiState.mru.length === this.parts.length) {
|
||||
this.mostRecentActiveParts = uiState.mru.map(index => this.parts[index]);
|
||||
} else {
|
||||
this.mostRecentActiveParts = [...this.parts];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Await ready
|
||||
await Promise.allSettled(this.parts.map(part => part.whenReady));
|
||||
|
||||
const mostRecentActivePart = firstOrDefault(this.mostRecentActiveParts);
|
||||
mostRecentActivePart?.activeGroup.focus();
|
||||
|
||||
this._isReady = true;
|
||||
this.whenReadyPromise.complete();
|
||||
|
||||
// Await restored
|
||||
await Promise.allSettled(this.parts.map(part => part.whenRestored));
|
||||
this.whenRestoredPromise.complete();
|
||||
}
|
||||
|
||||
protected override saveState(): void {
|
||||
const uiState: IEditorPartsUIState = {
|
||||
auxiliary: this.parts.filter(part => part !== this.mainPart).map(part => {
|
||||
return {
|
||||
state: part.createState(),
|
||||
bounds: (() => {
|
||||
const auxiliaryWindow = getWindow(part.getContainer());
|
||||
if (auxiliaryWindow) {
|
||||
return {
|
||||
x: auxiliaryWindow.screenX,
|
||||
y: auxiliaryWindow.screenY,
|
||||
width: auxiliaryWindow.outerWidth,
|
||||
height: auxiliaryWindow.outerHeight
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
})(),
|
||||
zoomLevel: (() => {
|
||||
const auxiliaryWindow = getWindow(part.getContainer());
|
||||
if (auxiliaryWindow) {
|
||||
return getZoomLevel(auxiliaryWindow);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
})()
|
||||
};
|
||||
}),
|
||||
mru: this.mostRecentActiveParts.map(part => this.parts.indexOf(part))
|
||||
};
|
||||
|
||||
if (uiState.auxiliary.length === 0) {
|
||||
delete this.workspaceMemento[EditorParts.EDITOR_PARTS_UI_STATE_STORAGE_KEY];
|
||||
} else {
|
||||
this.workspaceMemento[EditorParts.EDITOR_PARTS_UI_STATE_STORAGE_KEY] = uiState;
|
||||
}
|
||||
}
|
||||
|
||||
get hasRestorableState(): boolean {
|
||||
return this.parts.some(part => part.hasRestorableState);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Events
|
||||
|
||||
private readonly _onDidActiveGroupChange = this._register(new Emitter<IEditorGroupView>());
|
||||
|
||||
@@ -460,7 +460,7 @@ export class EditorsObserver extends Disposable {
|
||||
|
||||
private async loadState(): Promise<void> {
|
||||
if (this.editorGroupsContainer === this.editorGroupService.mainPart || this.editorGroupsContainer === this.editorGroupService) {
|
||||
await this.editorGroupService.mainPart.whenReady;
|
||||
await this.editorGroupService.whenReady;
|
||||
}
|
||||
|
||||
// Previous state: Load editors map from persisted state
|
||||
|
||||
@@ -156,7 +156,7 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
|
||||
}
|
||||
|
||||
private onDidEditorPaste(e: IPasteEvent, codeEditor: ICodeEditor): void {
|
||||
if (this.input instanceof UntitledTextEditorInput && this.input.model.hasLanguageSetExplicitly) {
|
||||
if (this.input instanceof UntitledTextEditorInput && this.input.hasLanguageSetExplicitly) {
|
||||
return; // do not override language if it was set explicitly
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
|
||||
if (candidateLanguage && candidateLanguage.id !== PLAINTEXT_LANGUAGE_ID) {
|
||||
if (this.input instanceof UntitledTextEditorInput && candidateLanguage.source === 'event') {
|
||||
// High confidence, set language id at TextEditorModel level to block future auto-detection
|
||||
this.input.model.setLanguageId(candidateLanguage.id);
|
||||
this.input.setLanguageId(candidateLanguage.id);
|
||||
} else {
|
||||
textModel.setLanguage(this.languageService.createById(candidateLanguage.id));
|
||||
}
|
||||
|
||||
@@ -708,9 +708,11 @@ export class StatusbarService extends MultiWindowParts<StatusbarPart> implements
|
||||
readonly mainPart = this._register(this.instantiationService.createInstance(MainStatusbarPart));
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IThemeService themeService: IThemeService
|
||||
) {
|
||||
super();
|
||||
super('workbench.statusBarService', themeService, storageService);
|
||||
|
||||
this._register(this.registerPart(this.mainPart));
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ import { MenuBar, IMenuBarOptions } from 'vs/base/browser/ui/menu/menubar';
|
||||
import { Direction } from 'vs/base/browser/ui/menu/menu';
|
||||
import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { isFullscreen } from 'vs/base/browser/browser';
|
||||
import { isFullscreen, onDidChangeFullscreen } from 'vs/base/browser/browser';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { BrowserFeatures } from 'vs/base/browser/canIUse';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
@@ -398,7 +397,6 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
@IPreferencesService preferencesService: IPreferencesService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IHostService hostService: IHostService,
|
||||
@ICommandService commandService: ICommandService
|
||||
@@ -535,7 +533,7 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
enableMenuBarMnemonics = true;
|
||||
}
|
||||
|
||||
return enableMenuBarMnemonics && (!isWeb || isFullscreen());
|
||||
return enableMenuBarMnemonics && (!isWeb || isFullscreen(mainWindow));
|
||||
}
|
||||
|
||||
private get currentCompactMenuMode(): Direction | undefined {
|
||||
@@ -798,7 +796,11 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
|
||||
// Mnemonics require fullscreen in web
|
||||
if (isWeb) {
|
||||
this._register(this.layoutService.onDidChangeFullscreen(e => this.updateMenubar()));
|
||||
this._register(onDidChangeFullscreen(windowId => {
|
||||
if (windowId === mainWindow.vscodeWindowId) {
|
||||
this.updateMenubar();
|
||||
}
|
||||
}));
|
||||
this._register(this.webNavigationMenu.onDidChange(() => this.updateMenubar()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,9 +81,11 @@ export class BrowserTitleService extends MultiWindowParts<BrowserTitlebarPart> i
|
||||
readonly mainPart = this._register(this.createMainTitlebarPart());
|
||||
|
||||
constructor(
|
||||
@IInstantiationService protected readonly instantiationService: IInstantiationService
|
||||
@IInstantiationService protected readonly instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IThemeService themeService: IThemeService
|
||||
) {
|
||||
super();
|
||||
super('workbench.titleService', themeService, storageService);
|
||||
|
||||
this._register(this.registerPart(this.mainPart));
|
||||
}
|
||||
@@ -168,7 +170,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
|
||||
get minimumHeight(): number {
|
||||
const value = this.isCommandCenterVisible || (isWeb && isWCOEnabled()) ? 35 : 30;
|
||||
|
||||
return value / (this.useCounterZoom ? getZoomFactor() : 1);
|
||||
return value / (this.useCounterZoom ? getZoomFactor(getWindow(this.element)) : 1);
|
||||
}
|
||||
|
||||
get maximumHeight(): number { return this.minimumHeight; }
|
||||
@@ -672,7 +674,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
|
||||
}
|
||||
|
||||
protected onContextMenu(e: MouseEvent, menuId: MenuId): void {
|
||||
const event = new StandardMouseEvent(getWindow(this.rootContainer), e);
|
||||
const event = new StandardMouseEvent(getWindow(this.element), e);
|
||||
|
||||
// Show it
|
||||
this.contextMenuService.showContextMenu({
|
||||
@@ -717,7 +719,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
|
||||
// 1. Shrinking below the window control size (zoom < 1)
|
||||
// 2. No custom items are present in the title bar
|
||||
|
||||
const zoomFactor = getZoomFactor();
|
||||
const zoomFactor = getZoomFactor(getWindow(this.element));
|
||||
|
||||
const noMenubar = this.currentMenubarVisibility === 'hidden' || this.currentMenubarVisibility === 'compact' || (!isWeb && isMacintosh);
|
||||
const noCommandCenter = !this.isCommandCenterVisible;
|
||||
@@ -736,7 +738,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
|
||||
this.lastLayoutDimensions = dimension;
|
||||
|
||||
if (getTitleBarStyle(this.configurationService) === 'custom') {
|
||||
const zoomFactor = getZoomFactor();
|
||||
const zoomFactor = getZoomFactor(getWindow(this.element));
|
||||
|
||||
this.element.style.setProperty('--zoom-factor', zoomFactor.toString());
|
||||
this.rootContainer.classList.toggle('counter-zoom', this.useCounterZoom);
|
||||
|
||||
@@ -113,7 +113,7 @@ export class BrowserMain extends Disposable {
|
||||
private init(): void {
|
||||
|
||||
// Browser config
|
||||
setFullscreen(!!detectFullscreen(mainWindow));
|
||||
setFullscreen(!!detectFullscreen(mainWindow), mainWindow);
|
||||
}
|
||||
|
||||
async open(): Promise<IWorkbench> {
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isSafari, setFullscreen } from 'vs/base/browser/browser';
|
||||
import { addDisposableListener, addDisposableThrottledListener, detectFullscreen, EventHelper, EventType, getActiveWindow, getWindow, getWindows, getWindowsCount, windowOpenNoOpener, windowOpenPopup, windowOpenWithSuccess } from 'vs/base/browser/dom';
|
||||
import { addDisposableListener, detectFullscreen, EventHelper, EventType, getActiveWindow, getWindow, getWindowById, getWindows, getWindowsCount, windowOpenNoOpener, windowOpenPopup, windowOpenWithSuccess } from 'vs/base/browser/dom';
|
||||
import { DomEmitter } from 'vs/base/browser/event';
|
||||
import { HidDeviceData, requestHidDevice, requestSerialPort, requestUsbDevice, SerialPortData, UsbDeviceData } from 'vs/base/browser/deviceAccess';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { matchesScheme, Schemas } from 'vs/base/common/network';
|
||||
import { isIOS, isMacintosh } from 'vs/base/common/platform';
|
||||
import { isIOS } from 'vs/base/common/platform';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -35,11 +35,17 @@ export abstract class BaseWindow extends Disposable {
|
||||
private static TIMEOUT_HANDLES = Number.MIN_SAFE_INTEGER; // try to not compete with the IDs of native `setTimeout`
|
||||
private static readonly TIMEOUT_DISPOSABLES = new Map<number, Set<IDisposable>>();
|
||||
|
||||
constructor(targetWindow: CodeWindow, dom = { getWindowsCount, getWindows } /* for testing */) {
|
||||
constructor(
|
||||
targetWindow: CodeWindow,
|
||||
dom = { getWindowsCount, getWindows }, /* for testing */
|
||||
@IHostService protected readonly hostService: IHostService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.enableWindowFocusOnElementFocus(targetWindow);
|
||||
this.enableMultiWindowAwareTimeout(targetWindow, dom);
|
||||
|
||||
this.registerFullScreenListeners(targetWindow.vscodeWindowId);
|
||||
}
|
||||
|
||||
//#region focus handling in multi-window applications
|
||||
@@ -128,6 +134,16 @@ export abstract class BaseWindow extends Disposable {
|
||||
|
||||
//#endregion
|
||||
|
||||
private registerFullScreenListeners(targetWindowId: number): void {
|
||||
this._register(this.hostService.onDidChangeFullScreen(windowId => {
|
||||
if (windowId === targetWindowId) {
|
||||
const targetWindow = getWindowById(targetWindowId);
|
||||
if (targetWindow) {
|
||||
setFullscreen(!!detectFullscreen(targetWindow.window), targetWindow.window);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export class BrowserWindow extends BaseWindow {
|
||||
@@ -141,9 +157,9 @@ export class BrowserWindow extends BaseWindow {
|
||||
@IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IHostService private readonly hostService: IHostService
|
||||
@IHostService hostService: IHostService
|
||||
) {
|
||||
super(mainWindow);
|
||||
super(mainWindow, undefined, hostService);
|
||||
|
||||
this.registerListeners();
|
||||
this.create();
|
||||
@@ -173,16 +189,6 @@ export class BrowserWindow extends BaseWindow {
|
||||
|
||||
// Prevent default navigation on drop
|
||||
this._register(addDisposableListener(this.layoutService.mainContainer, EventType.DROP, e => EventHelper.stop(e, true)));
|
||||
|
||||
// Fullscreen (Browser)
|
||||
for (const event of [EventType.FULLSCREEN_CHANGE, EventType.WK_FULLSCREEN_CHANGE]) {
|
||||
this._register(addDisposableListener(mainWindow.document, event, () => setFullscreen(!!detectFullscreen(mainWindow))));
|
||||
}
|
||||
|
||||
// Fullscreen (Native)
|
||||
this._register(addDisposableThrottledListener(viewport, EventType.RESIZE, () => {
|
||||
setFullscreen(!!detectFullscreen(mainWindow));
|
||||
}, undefined, isMacintosh ? 2000 /* adjust for macOS animation */ : 800 /* can be throttled */));
|
||||
}
|
||||
|
||||
private onWillShutdown(): void {
|
||||
|
||||
@@ -33,7 +33,7 @@ export const RemoteNameContext = new RawContextKey<string>('remoteName', '', loc
|
||||
export const VirtualWorkspaceContext = new RawContextKey<string>('virtualWorkspace', '', localize('virtualWorkspace', "The scheme of the current workspace is from a virtual file system or an empty string."));
|
||||
export const TemporaryWorkspaceContext = new RawContextKey<boolean>('temporaryWorkspace', false, localize('temporaryWorkspace', "The scheme of the current workspace is from a temporary file system."));
|
||||
|
||||
export const IsFullscreenContext = new RawContextKey<boolean>('isFullscreen', false, localize('isFullscreen', "Whether the window is in fullscreen mode"));
|
||||
export const IsMainWindowFullscreenContext = new RawContextKey<boolean>('isFullscreen', false, localize('isFullscreen', "Whether the main window is in fullscreen mode"));
|
||||
export const IsAuxiliaryWindowFocusedContext = new RawContextKey<boolean>('isAuxiliaryWindowFocusedContext', false, localize('isAuxiliaryWindowFocusedContext', "Whether an auxiliary window is focused"));
|
||||
|
||||
export const HasWebFileSystemAccess = new RawContextKey<boolean>('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access)
|
||||
|
||||
@@ -20,7 +20,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys';
|
||||
import { OpenFolderAction, OpenFileAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import { isMacintosh, isWeb } from 'vs/base/common/platform';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
@@ -62,7 +62,11 @@ export class WelcomeView extends ViewPane {
|
||||
this.debugStartLanguageContext.set(lastSetLanguage);
|
||||
|
||||
const setContextKey = () => {
|
||||
const editorControl = this.editorService.activeTextEditorControl;
|
||||
let editorControl = this.editorService.activeTextEditorControl;
|
||||
if (isDiffEditor(editorControl)) {
|
||||
editorControl = editorControl.getModifiedEditor();
|
||||
}
|
||||
|
||||
if (isCodeEditor(editorControl)) {
|
||||
const model = editorControl.getModel();
|
||||
const language = model ? model.getLanguageId() : undefined;
|
||||
@@ -82,7 +86,11 @@ export class WelcomeView extends ViewPane {
|
||||
this._register(editorService.onDidActiveEditorChange(() => {
|
||||
disposables.clear();
|
||||
|
||||
const editorControl = this.editorService.activeTextEditorControl;
|
||||
let editorControl = this.editorService.activeTextEditorControl;
|
||||
if (isDiffEditor(editorControl)) {
|
||||
editorControl = editorControl.getModifiedEditor();
|
||||
}
|
||||
|
||||
if (isCodeEditor(editorControl)) {
|
||||
disposables.add(editorControl.onDidChangeModelLanguage(setContextKey));
|
||||
}
|
||||
|
||||
@@ -1102,7 +1102,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
||||
if (isUninstalled) {
|
||||
const canRemoveRunningExtension = runningExtension && this.extensionService.canRemoveExtension(runningExtension);
|
||||
const isSameExtensionRunning = runningExtension && (!extension.server || extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)));
|
||||
if (!canRemoveRunningExtension && isSameExtensionRunning) {
|
||||
if (!canRemoveRunningExtension && isSameExtensionRunning && !runningExtension.isUnderDevelopment) {
|
||||
return nls.localize('postUninstallTooltip', "Please reload Visual Studio Code to complete the uninstallation of this extension.");
|
||||
}
|
||||
return undefined;
|
||||
|
||||
@@ -661,7 +661,6 @@ export class OpenActiveFileInEmptyWorkspace extends Action2 {
|
||||
title: { value: OpenActiveFileInEmptyWorkspace.LABEL, original: 'Open Active File in New Empty Workspace' },
|
||||
f1: true,
|
||||
category: Categories.File,
|
||||
keybinding: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyO), weight: KeybindingWeight.WorkbenchContrib },
|
||||
precondition: EmptyWorkspaceSupportContext
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { getWindow } from 'vs/base/browser/dom';
|
||||
import { renderMarkdownAsPlaintext } from 'vs/base/browser/markdownRenderer';
|
||||
import * as aria from 'vs/base/browser/ui/aria/aria';
|
||||
import { Barrier, Queue, raceCancellation, raceCancellationError } from 'vs/base/common/async';
|
||||
@@ -773,9 +774,9 @@ export class InlineChatController implements IEditorContribution {
|
||||
this._ignoreModelContentChanged = true;
|
||||
this._activeSession.wholeRange.trackEdits(editOperations);
|
||||
if (opts) {
|
||||
await this._strategy.makeProgressiveChanges(editOperations, opts);
|
||||
await this._strategy.makeProgressiveChanges(getWindow(this._editor.getContainerDomNode()), editOperations, opts);
|
||||
} else {
|
||||
await this._strategy.makeChanges(editOperations);
|
||||
await this._strategy.makeChanges(getWindow(this._editor.getContainerDomNode()), editOperations);
|
||||
}
|
||||
this._ctxDidEdit.set(this._activeSession.hasChangedText);
|
||||
} finally {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { disposableWindowInterval } from 'vs/base/browser/dom';
|
||||
import { $window } from 'vs/base/browser/window';
|
||||
import { IAction, toAction } from 'vs/base/common/actions';
|
||||
import { coalesceInPlace, equals, tail } from 'vs/base/common/arrays';
|
||||
import { AsyncIterableObject, AsyncIterableSource } from 'vs/base/common/async';
|
||||
@@ -77,9 +76,9 @@ export abstract class EditModeStrategy {
|
||||
|
||||
abstract cancel(): Promise<void>;
|
||||
|
||||
abstract makeProgressiveChanges(edits: ISingleEditOperation[], timings: ProgressingEditsOptions): Promise<void>;
|
||||
abstract makeProgressiveChanges(targetWindow: Window, edits: ISingleEditOperation[], timings: ProgressingEditsOptions): Promise<void>;
|
||||
|
||||
abstract makeChanges(edits: ISingleEditOperation[]): Promise<void>;
|
||||
abstract makeChanges(targetWindow: Window, edits: ISingleEditOperation[]): Promise<void>;
|
||||
|
||||
abstract undoChanges(altVersionId: number): Promise<void>;
|
||||
|
||||
@@ -151,7 +150,7 @@ export class PreviewStrategy extends EditModeStrategy {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
override async makeChanges(_edits: ISingleEditOperation[]): Promise<void> {
|
||||
override async makeChanges(_targetWindow: Window, _edits: ISingleEditOperation[]): Promise<void> {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@@ -244,7 +243,7 @@ export class LivePreviewStrategy extends EditModeStrategy {
|
||||
const targetAltVersion = textModelNSnapshotAltVersion ?? textModelNAltVersion;
|
||||
await undoModelUntil(modelN, targetAltVersion);
|
||||
}
|
||||
override async makeChanges(edits: ISingleEditOperation[]): Promise<void> {
|
||||
override async makeChanges(_targetWindow: Window, edits: ISingleEditOperation[]): Promise<void> {
|
||||
const cursorStateComputerAndInlineDiffCollection: ICursorStateComputer = (undoEdits) => {
|
||||
let last: Position | null = null;
|
||||
for (const edit of undoEdits) {
|
||||
@@ -266,7 +265,7 @@ export class LivePreviewStrategy extends EditModeStrategy {
|
||||
await this._updateDiffZones();
|
||||
}
|
||||
|
||||
override async makeProgressiveChanges(edits: ISingleEditOperation[], opts: ProgressingEditsOptions): Promise<void> {
|
||||
override async makeProgressiveChanges(targetWindow: Window, edits: ISingleEditOperation[], opts: ProgressingEditsOptions): Promise<void> {
|
||||
|
||||
// push undo stop before first edit
|
||||
if (++this._editCount === 1) {
|
||||
@@ -285,7 +284,7 @@ export class LivePreviewStrategy extends EditModeStrategy {
|
||||
const wordCount = countWords(edit.text ?? '');
|
||||
const speed = wordCount / durationInSec;
|
||||
// console.log({ durationInSec, wordCount, speed: wordCount / durationInSec });
|
||||
await performAsyncTextEdit(this._session.textModelN, asProgressiveEdit(edit, speed, opts.token));
|
||||
await performAsyncTextEdit(this._session.textModelN, asProgressiveEdit(targetWindow, edit, speed, opts.token));
|
||||
}
|
||||
|
||||
await renderTask;
|
||||
@@ -444,7 +443,7 @@ export function asAsyncEdit(edit: IIdentifiedSingleEditOperation): AsyncTextEdit
|
||||
} satisfies AsyncTextEdit;
|
||||
}
|
||||
|
||||
export function asProgressiveEdit(edit: IIdentifiedSingleEditOperation, wordsPerSec: number, token: CancellationToken): AsyncTextEdit {
|
||||
export function asProgressiveEdit(targetWindow: Window, edit: IIdentifiedSingleEditOperation, wordsPerSec: number, token: CancellationToken): AsyncTextEdit {
|
||||
|
||||
wordsPerSec = Math.max(10, wordsPerSec);
|
||||
|
||||
@@ -452,7 +451,7 @@ export function asProgressiveEdit(edit: IIdentifiedSingleEditOperation, wordsPer
|
||||
let newText = edit.text ?? '';
|
||||
// const wordCount = countWords(newText);
|
||||
|
||||
const handle = disposableWindowInterval($window, () => {
|
||||
const handle = disposableWindowInterval(targetWindow, () => {
|
||||
|
||||
const r = getNWords(newText, 1);
|
||||
stream.emitOne(r.value);
|
||||
@@ -584,15 +583,15 @@ export class LiveStrategy extends EditModeStrategy {
|
||||
await undoModelUntil(textModelN, altVersionId);
|
||||
}
|
||||
|
||||
override async makeChanges(edits: ISingleEditOperation[]): Promise<void> {
|
||||
return this._makeChanges(edits, undefined);
|
||||
override async makeChanges(targetWindow: Window, edits: ISingleEditOperation[]): Promise<void> {
|
||||
return this._makeChanges(targetWindow, edits, undefined);
|
||||
}
|
||||
|
||||
override async makeProgressiveChanges(edits: ISingleEditOperation[], opts: ProgressingEditsOptions): Promise<void> {
|
||||
return this._makeChanges(edits, opts);
|
||||
override async makeProgressiveChanges(targetWindow: Window, edits: ISingleEditOperation[], opts: ProgressingEditsOptions): Promise<void> {
|
||||
return this._makeChanges(targetWindow, edits, opts);
|
||||
}
|
||||
|
||||
private async _makeChanges(edits: ISingleEditOperation[], opts: ProgressingEditsOptions | undefined): Promise<void> {
|
||||
private async _makeChanges(targetWindow: Window, edits: ISingleEditOperation[], opts: ProgressingEditsOptions | undefined): Promise<void> {
|
||||
|
||||
// push undo stop before first edit
|
||||
if (++this._editCount === 1) {
|
||||
@@ -625,7 +624,7 @@ export class LiveStrategy extends EditModeStrategy {
|
||||
const wordCount = countWords(edit.text ?? '');
|
||||
const speed = wordCount / durationInSec;
|
||||
// console.log({ durationInSec, wordCount, speed: wordCount / durationInSec });
|
||||
await performAsyncTextEdit(this._session.textModelN, asProgressiveEdit(edit, speed, opts.token), progress);
|
||||
await performAsyncTextEdit(this._session.textModelN, asProgressiveEdit(targetWindow, edit, speed, opts.token), progress);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
@@ -77,7 +77,7 @@ export class NotebookEditorWidgetService implements INotebookEditorService {
|
||||
groupListener.set(id, listeners);
|
||||
};
|
||||
this._disposables.add(editorGroupService.onDidAddGroup(onNewGroup));
|
||||
editorGroupService.mainPart.whenReady.then(() => editorGroupService.groups.forEach(onNewGroup));
|
||||
editorGroupService.whenReady.then(() => editorGroupService.groups.forEach(onNewGroup));
|
||||
|
||||
// group removed -> clean up listeners, clean up widgets
|
||||
this._disposables.add(editorGroupService.onDidRemoveGroup(group => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Dimension, h } from 'vs/base/browser/dom';
|
||||
import { Dimension, getWindow, h } from 'vs/base/browser/dom';
|
||||
import { CancelablePromise, Queue, createCancelablePromise, raceCancellationError } from 'vs/base/common/async';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
@@ -468,7 +468,7 @@ class EditStrategy {
|
||||
const wordCount = countWords(edit.text ?? '');
|
||||
const speed = wordCount / durationInSec;
|
||||
// console.log({ durationInSec, wordCount, speed: wordCount / durationInSec });
|
||||
await performAsyncTextEdit(editor.getModel(), asProgressiveEdit(edit, speed, opts.token));
|
||||
await performAsyncTextEdit(editor.getModel(), asProgressiveEdit(getWindow(editor.getContainerDomNode()), edit, speed, opts.token));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ export class PartsSplash {
|
||||
lastIdleSchedule.value = dom.runWhenWindowIdle(mainWindow, () => this._savePartsSplash(), 2500);
|
||||
};
|
||||
lifecycleService.when(LifecyclePhase.Restored).then(() => {
|
||||
Event.any(onDidChangeFullscreen, editorGroupsService.mainPart.onDidLayout, _themeService.onDidColorThemeChange)(savePartsSplashSoon, undefined, this._disposables);
|
||||
Event.any(Event.filter(onDidChangeFullscreen, windowId => windowId === mainWindow.vscodeWindowId), editorGroupsService.mainPart.onDidLayout, _themeService.onDidColorThemeChange)(savePartsSplashSoon, undefined, this._disposables);
|
||||
savePartsSplashSoon();
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ export class PartsSplash {
|
||||
}
|
||||
|
||||
private _shouldSaveLayoutInfo(): boolean {
|
||||
return !isFullscreen() && !this._environmentService.isExtensionDevelopment && !this._didChangeTitleBarStyle;
|
||||
return !isFullscreen(mainWindow) && !this._environmentService.isExtensionDevelopment && !this._didChangeTitleBarStyle;
|
||||
}
|
||||
|
||||
private _removePartsSplash(): void {
|
||||
|
||||
@@ -22,11 +22,12 @@ import { IEditableData } from 'vs/workbench/common/views';
|
||||
import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList';
|
||||
import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal';
|
||||
import { IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfiguration, ITerminalFont, ITerminalProcessExtHostProxy, ITerminalProcessInfo } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
|
||||
import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget';
|
||||
import type { IMarker, ITheme, Terminal as RawXtermTerminal } from '@xterm/xterm';
|
||||
import { ScrollPosition } from 'vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
import { ACTIVE_GROUP_TYPE, AUX_WINDOW_GROUP_TYPE, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
export const ITerminalService = createDecorator<ITerminalService>('terminalService');
|
||||
export const ITerminalEditorService = createDecorator<ITerminalEditorService>('terminalEditorService');
|
||||
@@ -297,7 +298,8 @@ export interface ITerminalService extends ITerminalInstanceHost {
|
||||
|
||||
getActiveOrCreateInstance(options?: { acceptsInput?: boolean }): Promise<ITerminalInstance>;
|
||||
revealActiveTerminal(preserveFocus?: boolean): Promise<void>;
|
||||
moveToEditor(source: ITerminalInstance): void;
|
||||
moveToEditor(source: ITerminalInstance, group?: GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | AUX_WINDOW_GROUP_TYPE): void;
|
||||
moveIntoNewEditor(source: ITerminalInstance): void;
|
||||
moveToTerminalView(source: ITerminalInstance | URI): Promise<void>;
|
||||
getPrimaryBackend(): ITerminalBackend | undefined;
|
||||
|
||||
@@ -419,7 +421,7 @@ export interface ICreateTerminalOptions {
|
||||
}
|
||||
|
||||
export interface TerminalEditorLocation {
|
||||
viewColumn: EditorGroupColumn;
|
||||
viewColumn: GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | AUX_WINDOW_GROUP_TYPE;
|
||||
preserveFocus?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -366,6 +366,14 @@ export function registerTerminalActions() {
|
||||
runAfter: (instances) => instances.at(-1)?.focus()
|
||||
});
|
||||
|
||||
registerContextualInstanceAction({
|
||||
id: TerminalCommandId.MoveIntoNewWindow,
|
||||
title: terminalStrings.moveIntoNewWindow,
|
||||
precondition: sharedWhenClause.terminalAvailable_and_opened,
|
||||
run: (instance, c) => c.service.moveIntoNewEditor(instance),
|
||||
runAfter: (instances) => instances.at(-1)?.focus()
|
||||
});
|
||||
|
||||
registerTerminalAction({
|
||||
id: TerminalCommandId.MoveToTerminalPanel,
|
||||
title: terminalStrings.moveToTerminalPanel,
|
||||
|
||||
@@ -576,6 +576,17 @@ export function setupTerminalMenus(): void {
|
||||
order: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
id: MenuId.TerminalTabContext,
|
||||
item: {
|
||||
command: {
|
||||
id: TerminalCommandId.MoveIntoNewWindow,
|
||||
title: terminalStrings.moveIntoNewWindow.value
|
||||
},
|
||||
group: ContextMenuGroup.Create,
|
||||
order: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
id: MenuId.TerminalTabContext,
|
||||
item: {
|
||||
|
||||
@@ -41,7 +41,7 @@ import { IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalP
|
||||
import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey';
|
||||
import { columnToEditorGroup } from 'vs/workbench/services/editor/common/editorGroupColumn';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ACTIVE_GROUP_TYPE, AUX_WINDOW_GROUP, AUX_WINDOW_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ILifecycleService, ShutdownReason, StartupKind, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
@@ -56,6 +56,7 @@ import { DetachedTerminal } from 'vs/workbench/contrib/terminal/browser/detached
|
||||
import { ITerminalCapabilityImplMap, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities';
|
||||
import { createInstanceCapabilityEventMultiplexer } from 'vs/workbench/contrib/terminal/browser/terminalEvents';
|
||||
import { mainWindow } from 'vs/base/browser/window';
|
||||
import { GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
|
||||
export class TerminalService extends Disposable implements ITerminalService {
|
||||
declare _serviceBrand: undefined;
|
||||
@@ -762,7 +763,7 @@ export class TerminalService extends Disposable implements ITerminalService {
|
||||
return this.instances.some(term => term.processId === remoteTerm.pid);
|
||||
}
|
||||
|
||||
moveToEditor(source: ITerminalInstance): void {
|
||||
moveToEditor(source: ITerminalInstance, group?: GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | AUX_WINDOW_GROUP_TYPE): void {
|
||||
if (source.target === TerminalLocation.Editor) {
|
||||
return;
|
||||
}
|
||||
@@ -771,7 +772,12 @@ export class TerminalService extends Disposable implements ITerminalService {
|
||||
return;
|
||||
}
|
||||
sourceGroup.removeInstance(source);
|
||||
this._terminalEditorService.openEditor(source);
|
||||
this._terminalEditorService.openEditor(source, group ? { viewColumn: group } : undefined);
|
||||
|
||||
}
|
||||
|
||||
moveIntoNewEditor(source: ITerminalInstance): void {
|
||||
this.moveToEditor(source, AUX_WINDOW_GROUP);
|
||||
}
|
||||
|
||||
async moveToTerminalView(source?: ITerminalInstance | URI, target?: ITerminalInstance, side?: 'before' | 'after'): Promise<void> {
|
||||
|
||||
@@ -481,6 +481,7 @@ export const enum TerminalCommandId {
|
||||
DetachSession = 'workbench.action.terminal.detachSession',
|
||||
MoveToEditor = 'workbench.action.terminal.moveToEditor',
|
||||
MoveToTerminalPanel = 'workbench.action.terminal.moveToTerminalPanel',
|
||||
MoveIntoNewWindow = 'workbench.action.terminal.moveIntoNewWindow',
|
||||
SetDimensions = 'workbench.action.terminal.setDimensions',
|
||||
ClearPreviousSessionHistory = 'workbench.action.terminal.clearPreviousSessionHistory',
|
||||
SelectPrevSuggestion = 'workbench.action.terminal.selectPrevSuggestion',
|
||||
|
||||
@@ -37,6 +37,10 @@ export const terminalStrings = {
|
||||
value: localize('moveToEditor', "Move Terminal into Editor Area"),
|
||||
original: 'Move Terminal into Editor Area',
|
||||
},
|
||||
moveIntoNewWindow: {
|
||||
value: localize('moveIntoNewWindow', "Move Terminal into New Window"),
|
||||
original: 'Move Terminal into New Window',
|
||||
},
|
||||
moveToTerminalPanel: {
|
||||
value: localize('workbench.action.terminal.moveToTerminalPanel', "Move Terminal into Panel"),
|
||||
original: 'Move Terminal into Panel'
|
||||
|
||||
@@ -36,7 +36,7 @@ class WebviewPanelContribution extends Disposable implements IWorkbenchContribut
|
||||
super();
|
||||
|
||||
// Add all the initial groups to be listened to
|
||||
this.editorGroupService.mainPart.whenReady.then(() => this.editorGroupService.groups.forEach(group => {
|
||||
this.editorGroupService.whenReady.then(() => this.editorGroupService.groups.forEach(group => {
|
||||
this.registerGroupListener(group);
|
||||
}));
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import 'vs/css!./media/actions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { applyZoom } from 'vs/platform/window/electron-sandbox/window';
|
||||
import { ApplyZoomTarget, applyZoom } from 'vs/platform/window/electron-sandbox/window';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { getZoomLevel } from 'vs/base/browser/browser';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
@@ -23,7 +23,7 @@ import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
||||
import { Action2, IAction2Options, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { getActiveWindow } from 'vs/base/browser/dom';
|
||||
@@ -74,7 +74,7 @@ abstract class BaseZoomAction extends Action2 {
|
||||
super(desc);
|
||||
}
|
||||
|
||||
protected async setConfiguredZoomLevel(accessor: ServicesAccessor, level: number): Promise<void> {
|
||||
protected async setZoomLevel(accessor: ServicesAccessor, level: number, target: ApplyZoomTarget): Promise<void> {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
level = Math.round(level); // when reaching smallest zoom, prevent fractional zoom levels
|
||||
@@ -83,21 +83,23 @@ abstract class BaseZoomAction extends Action2 {
|
||||
return; // https://github.com/microsoft/vscode/issues/48357
|
||||
}
|
||||
|
||||
await configurationService.updateValue(BaseZoomAction.SETTING_KEY, level);
|
||||
if (target === ApplyZoomTarget.ALL_WINDOWS) {
|
||||
await configurationService.updateValue(BaseZoomAction.SETTING_KEY, level);
|
||||
}
|
||||
|
||||
applyZoom(level);
|
||||
applyZoom(level, target);
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoomInAction extends BaseZoomAction {
|
||||
export class ZoomInAllWindowsAction extends BaseZoomAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.zoomIn',
|
||||
title: {
|
||||
value: localize('zoomIn', "Zoom In"),
|
||||
value: localize('zoomIn', "Zoom In (All Windows)"),
|
||||
mnemonicTitle: localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In"),
|
||||
original: 'Zoom In'
|
||||
original: 'Zoom In (All Windows)'
|
||||
},
|
||||
category: Categories.View,
|
||||
f1: true,
|
||||
@@ -115,19 +117,19 @@ export class ZoomInAction extends BaseZoomAction {
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): Promise<void> {
|
||||
return super.setConfiguredZoomLevel(accessor, getZoomLevel() + 1);
|
||||
return super.setZoomLevel(accessor, getZoomLevel(getActiveWindow()) + 1, ApplyZoomTarget.ALL_WINDOWS);
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoomOutAction extends BaseZoomAction {
|
||||
export class ZoomOutAllWindowsAction extends BaseZoomAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.zoomOut',
|
||||
title: {
|
||||
value: localize('zoomOut', "Zoom Out"),
|
||||
value: localize('zoomOut', "Zoom Out (All Windows)"),
|
||||
mnemonicTitle: localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "&&Zoom Out"),
|
||||
original: 'Zoom Out'
|
||||
original: 'Zoom Out (All Windows)'
|
||||
},
|
||||
category: Categories.View,
|
||||
f1: true,
|
||||
@@ -149,19 +151,19 @@ export class ZoomOutAction extends BaseZoomAction {
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): Promise<void> {
|
||||
return super.setConfiguredZoomLevel(accessor, getZoomLevel() - 1);
|
||||
return super.setZoomLevel(accessor, getZoomLevel(getActiveWindow()) - 1, ApplyZoomTarget.ALL_WINDOWS);
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoomResetAction extends BaseZoomAction {
|
||||
export class ZoomResetAllWindowsAction extends BaseZoomAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.zoomReset',
|
||||
title: {
|
||||
value: localize('zoomReset', "Reset Zoom"),
|
||||
value: localize('zoomReset', "Reset Zoom (All Windows)"),
|
||||
mnemonicTitle: localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom"),
|
||||
original: 'Reset Zoom'
|
||||
original: 'Reset Zoom (All Windows)'
|
||||
},
|
||||
category: Categories.View,
|
||||
f1: true,
|
||||
@@ -178,7 +180,82 @@ export class ZoomResetAction extends BaseZoomAction {
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): Promise<void> {
|
||||
return super.setConfiguredZoomLevel(accessor, 0);
|
||||
return super.setZoomLevel(accessor, 0, ApplyZoomTarget.ALL_WINDOWS);
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoomInActiveWindowAction extends BaseZoomAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.zoomInActiveWindow',
|
||||
title: {
|
||||
value: localize('zoomInActiveWindow', "Zoom In (Active Window)"),
|
||||
original: 'Zoom In (Active Window)'
|
||||
},
|
||||
category: Categories.View,
|
||||
f1: true,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.Equal),
|
||||
secondary: [KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Equal), KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.NumpadAdd)]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): Promise<void> {
|
||||
return super.setZoomLevel(accessor, getZoomLevel(getActiveWindow()) + 1, ApplyZoomTarget.ACTIVE_WINDOW);
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoomOutActiveWindowAction extends BaseZoomAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.zoomOutActiveWindow',
|
||||
title: {
|
||||
value: localize('zoomOutActiveWindow', "Zoom Out (Active Window)"),
|
||||
original: 'Zoom Out (Active Window)'
|
||||
},
|
||||
category: Categories.View,
|
||||
f1: true,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.Minus),
|
||||
secondary: [KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Minus), KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.NumpadSubtract)],
|
||||
linux: {
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.Minus),
|
||||
secondary: [KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.NumpadSubtract)]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): Promise<void> {
|
||||
return super.setZoomLevel(accessor, getZoomLevel(getActiveWindow()) - 1, ApplyZoomTarget.ACTIVE_WINDOW);
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoomResetActiveWindowAction extends BaseZoomAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.zoomResetActiveWindow',
|
||||
title: {
|
||||
value: localize('zoomResetActiveWindow', "Reset Zoom (Active Window)"),
|
||||
original: 'Reset Zoom (Active Window)'
|
||||
},
|
||||
category: Categories.View,
|
||||
f1: true,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.Numpad0)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): Promise<void> {
|
||||
return super.setZoomLevel(accessor, 0, ApplyZoomTarget.ACTIVE_WINDOW);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { ConfigureRuntimeArgumentsAction, ToggleDevToolsAction, ReloadWindowWithExtensionsDisabledAction, OpenUserDataFolderAction } from 'vs/workbench/electron-sandbox/actions/developerActions';
|
||||
import { ZoomResetAction, ZoomOutAction, ZoomInAction, CloseWindowAction, SwitchWindowAction, QuickSwitchWindowAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-sandbox/actions/windowActions';
|
||||
import { ZoomResetAllWindowsAction, ZoomOutAllWindowsAction, ZoomInAllWindowsAction, CloseWindowAction, SwitchWindowAction, QuickSwitchWindowAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler, ZoomInActiveWindowAction, ZoomOutActiveWindowAction, ZoomResetActiveWindowAction } from 'vs/workbench/electron-sandbox/actions/windowActions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
@@ -32,9 +32,13 @@ import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from
|
||||
(function registerActions(): void {
|
||||
|
||||
// Actions: Zoom
|
||||
registerAction2(ZoomInAction);
|
||||
registerAction2(ZoomOutAction);
|
||||
registerAction2(ZoomResetAction);
|
||||
registerAction2(ZoomInAllWindowsAction);
|
||||
registerAction2(ZoomOutAllWindowsAction);
|
||||
registerAction2(ZoomResetAllWindowsAction);
|
||||
|
||||
registerAction2(ZoomInActiveWindowAction);
|
||||
registerAction2(ZoomOutActiveWindowAction);
|
||||
registerAction2(ZoomResetActiveWindowAction);
|
||||
|
||||
// Actions: Window
|
||||
registerAction2(SwitchWindowAction);
|
||||
|
||||
@@ -78,7 +78,7 @@ export class DesktopMain extends Disposable {
|
||||
this.reviveUris();
|
||||
|
||||
// Apply fullscreen early if configured
|
||||
setFullscreen(!!this.configuration.fullscreen);
|
||||
setFullscreen(!!this.configuration.fullscreen, mainWindow);
|
||||
}
|
||||
|
||||
private reviveUris() {
|
||||
@@ -116,8 +116,10 @@ export class DesktopMain extends Disposable {
|
||||
// and before the workbench is created to prevent flickering.
|
||||
// We also need to respect that zoom level can be configured per
|
||||
// workspace, so we need the resolved configuration service.
|
||||
// Finally, it is possible for the window to have a custom
|
||||
// zoom level that is not derived from settings.
|
||||
// (fixes https://github.com/microsoft/vscode/issues/187982)
|
||||
this.applyConfiguredWindowZoomLevel(services.configurationService);
|
||||
this.applyWindowZoomLevel(services.configurationService);
|
||||
|
||||
// Create Workbench
|
||||
const workbench = new Workbench(mainWindow.document.body, { extraClasses: this.getExtraClasses() }, services.serviceCollection, services.logService);
|
||||
@@ -132,11 +134,16 @@ export class DesktopMain extends Disposable {
|
||||
this._register(instantiationService.createInstance(NativeWindow));
|
||||
}
|
||||
|
||||
private applyConfiguredWindowZoomLevel(configurationService: IConfigurationService) {
|
||||
const windowConfig = configurationService.getValue<IWindowsConfiguration>();
|
||||
const windowZoomLevel = typeof windowConfig.window?.zoomLevel === 'number' ? windowConfig.window.zoomLevel : 0;
|
||||
private applyWindowZoomLevel(configurationService: IConfigurationService) {
|
||||
let zoomLevel: number | undefined = undefined;
|
||||
if (this.configuration.isCustomZoomLevel && typeof this.configuration.zoomLevel === 'number') {
|
||||
zoomLevel = this.configuration.zoomLevel;
|
||||
} else {
|
||||
const windowConfig = configurationService.getValue<IWindowsConfiguration>();
|
||||
zoomLevel = typeof windowConfig.window?.zoomLevel === 'number' ? windowConfig.window.zoomLevel : 0;
|
||||
}
|
||||
|
||||
applyZoom(windowZoomLevel);
|
||||
applyZoom(zoomLevel, mainWindow);
|
||||
}
|
||||
|
||||
private getExtraClasses(): string[] {
|
||||
|
||||
@@ -38,7 +38,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart {
|
||||
return super.minimumHeight;
|
||||
}
|
||||
|
||||
return (this.isCommandCenterVisible ? 35 : this.macTitlebarSize) / (this.useCounterZoom ? getZoomFactor() : 1);
|
||||
return (this.isCommandCenterVisible ? 35 : this.macTitlebarSize) / (this.useCounterZoom ? getZoomFactor(getWindow(this.element)) : 1);
|
||||
}
|
||||
override get maximumHeight(): number { return this.minimumHeight; }
|
||||
|
||||
@@ -201,7 +201,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart {
|
||||
return;
|
||||
}
|
||||
|
||||
const zoomFactor = getZoomFactor();
|
||||
const zoomFactor = getZoomFactor(getWindow(this.element));
|
||||
this.onContextMenu(new MouseEvent('mouseup', { clientX: x / zoomFactor, clientY: y / zoomFactor }), MenuId.TitleBarContext);
|
||||
}));
|
||||
}
|
||||
@@ -261,7 +261,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart {
|
||||
// so that they can have the traffic lights rendered at the proper offset.
|
||||
// Ref https://github.com/microsoft/vscode/issues/159862
|
||||
|
||||
const newHeight = (height > 0 || this.bigSurOrNewer) ? Math.round(height * getZoomFactor()) : this.macTitlebarSize;
|
||||
const newHeight = (height > 0 || this.bigSurOrNewer) ? Math.round(height * getZoomFactor(getWindow(this.element))) : this.macTitlebarSize;
|
||||
if (newHeight !== this.cachedWindowControlHeight) {
|
||||
this.cachedWindowControlHeight = newHeight;
|
||||
this.nativeHostService.updateWindowControls({
|
||||
|
||||
@@ -7,17 +7,17 @@ import { localize } from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
import { EventType, EventHelper, addDisposableListener, ModifierKeyEmitter, getActiveElement, hasWindow, getWindow, getWindowById, getWindowId } from 'vs/base/browser/dom';
|
||||
import { EventType, EventHelper, addDisposableListener, ModifierKeyEmitter, getActiveElement, hasWindow, getWindow, getWindowById, getWindowId, getWindows } from 'vs/base/browser/dom';
|
||||
import { Separator, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { EditorResourceAccessor, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors, IResourceDiffEditorInput, IUntypedEditorInput, IEditorPane, isResourceEditorInput, IResourceMergeEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { WindowMinimumSize, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest } from 'vs/platform/window/common/window';
|
||||
import { WindowMinimumSize, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest, IWindowSettings } from 'vs/platform/window/common/window';
|
||||
import { ITitleService } from 'vs/workbench/services/title/browser/titleService';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { applyZoom } from 'vs/platform/window/electron-sandbox/window';
|
||||
import { setFullscreen, getZoomLevel } from 'vs/base/browser/browser';
|
||||
import { ApplyZoomTarget, applyZoom } from 'vs/platform/window/electron-sandbox/window';
|
||||
import { setFullscreen, getZoomLevel, onDidChangeZoomLevel } from 'vs/base/browser/browser';
|
||||
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { ipcRenderer, process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
@@ -72,6 +72,7 @@ import { IUtilityProcessWorkerWorkbenchService } from 'vs/workbench/services/uti
|
||||
import { registerWindowDriver } from 'vs/workbench/services/driver/electron-sandbox/driver';
|
||||
import { mainWindow } from 'vs/base/browser/window';
|
||||
import { BaseWindow } from 'vs/workbench/browser/window';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
|
||||
export class NativeWindow extends BaseWindow {
|
||||
|
||||
@@ -124,9 +125,10 @@ export class NativeWindow extends BaseWindow {
|
||||
@IBannerService private readonly bannerService: IBannerService,
|
||||
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
|
||||
@IPreferencesService private readonly preferencesService: IPreferencesService,
|
||||
@IUtilityProcessWorkerWorkbenchService private readonly utilityProcessWorkerWorkbenchService: IUtilityProcessWorkerWorkbenchService
|
||||
@IUtilityProcessWorkerWorkbenchService private readonly utilityProcessWorkerWorkbenchService: IUtilityProcessWorkerWorkbenchService,
|
||||
@IHostService hostService: IHostService
|
||||
) {
|
||||
super(mainWindow);
|
||||
super(mainWindow, undefined, hostService);
|
||||
|
||||
this.mainPartEditorService = editorService.createScoped('main', this._store);
|
||||
|
||||
@@ -248,8 +250,8 @@ export class NativeWindow extends BaseWindow {
|
||||
});
|
||||
|
||||
// Fullscreen Events
|
||||
ipcRenderer.on('vscode:enterFullScreen', async () => { setFullscreen(true); });
|
||||
ipcRenderer.on('vscode:leaveFullScreen', async () => { setFullscreen(false); });
|
||||
ipcRenderer.on('vscode:enterFullScreen', async () => { setFullscreen(true, mainWindow); });
|
||||
ipcRenderer.on('vscode:leaveFullScreen', async () => { setFullscreen(false, mainWindow); });
|
||||
|
||||
// Proxy Login Dialog
|
||||
ipcRenderer.on('vscode:openProxyAuthenticationDialog', async (event: unknown, payload: { authInfo: AuthInfo; username?: string; password?: string; replyChannel: string }) => {
|
||||
@@ -337,6 +339,22 @@ export class NativeWindow extends BaseWindow {
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(onDidChangeZoomLevel(targetWindowId => {
|
||||
if (targetWindowId !== mainWindow.vscodeWindowId) {
|
||||
return; // only update our own window
|
||||
}
|
||||
|
||||
const configuredWindowZoomLevel = this.configurationService.getValue<IWindowSettings | undefined>('window')?.zoomLevel;
|
||||
const currentWindowZoomLevel = getZoomLevel(mainWindow);
|
||||
|
||||
let notifyZoomLevel: number | undefined = undefined;
|
||||
if (configuredWindowZoomLevel !== currentWindowZoomLevel) {
|
||||
notifyZoomLevel = currentWindowZoomLevel;
|
||||
}
|
||||
|
||||
ipcRenderer.invoke('vscode:notifyZoomLevel', notifyZoomLevel);
|
||||
}));
|
||||
|
||||
// Listen to visible editor changes (debounced in case a new editor opens immediately after)
|
||||
this._register(Event.debounce(this.editorService.onDidVisibleEditorsChange, () => undefined, 0, undefined, undefined, undefined, this._store)(() => this.maybeCloseWindow()));
|
||||
|
||||
@@ -624,8 +642,16 @@ export class NativeWindow extends BaseWindow {
|
||||
const windowConfig = this.configurationService.getValue<IWindowsConfiguration>();
|
||||
const windowZoomLevel = typeof windowConfig.window?.zoomLevel === 'number' ? windowConfig.window.zoomLevel : 0;
|
||||
|
||||
if (getZoomLevel() !== windowZoomLevel) {
|
||||
applyZoom(windowZoomLevel);
|
||||
let applyZoomLevel = false;
|
||||
for (const { window } of getWindows()) {
|
||||
if (getZoomLevel(window) !== windowZoomLevel) {
|
||||
applyZoomLevel = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (applyZoomLevel) {
|
||||
applyZoom(windowZoomLevel, ApplyZoomTarget.ALL_WINDOWS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import { BaseWindow } from 'vs/workbench/browser/window';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
|
||||
export const IAuxiliaryWindowService = createDecorator<IAuxiliaryWindowService>('auxiliaryWindowService');
|
||||
|
||||
@@ -31,6 +32,7 @@ export interface IAuxiliaryWindowOpenEvent {
|
||||
|
||||
export interface IAuxiliaryWindowOpenOptions {
|
||||
readonly bounds?: Partial<IRectangle>;
|
||||
readonly zoomLevel?: number;
|
||||
}
|
||||
|
||||
export interface IAuxiliaryWindowService {
|
||||
@@ -45,7 +47,10 @@ export interface IAuxiliaryWindowService {
|
||||
export interface IAuxiliaryWindow extends IDisposable {
|
||||
|
||||
readonly onDidLayout: Event<Dimension>;
|
||||
readonly onWillClose: Event<void>;
|
||||
|
||||
readonly onBeforeUnload: Event<void>;
|
||||
readonly onUnload: Event<void>;
|
||||
|
||||
readonly whenStylesHaveLoaded: Promise<void>;
|
||||
|
||||
readonly window: CodeWindow;
|
||||
@@ -59,8 +64,11 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow {
|
||||
private readonly _onDidLayout = this._register(new Emitter<Dimension>());
|
||||
readonly onDidLayout = this._onDidLayout.event;
|
||||
|
||||
private readonly _onWillClose = this._register(new Emitter<void>());
|
||||
readonly onWillClose = this._onWillClose.event;
|
||||
private readonly _onBeforeUnload = this._register(new Emitter<void>());
|
||||
readonly onBeforeUnload = this._onBeforeUnload.event;
|
||||
|
||||
private readonly _onUnload = this._register(new Emitter<void>());
|
||||
readonly onUnload = this._onUnload.event;
|
||||
|
||||
private readonly _onWillDispose = this._register(new Emitter<void>());
|
||||
readonly onWillDispose = this._onWillDispose.event;
|
||||
@@ -72,16 +80,17 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow {
|
||||
readonly container: HTMLElement,
|
||||
stylesHaveLoaded: Barrier,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IHostService hostService: IHostService
|
||||
) {
|
||||
super(window);
|
||||
super(window, undefined, hostService);
|
||||
|
||||
this.whenStylesHaveLoaded = stylesHaveLoaded.wait().then(() => { });
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(addDisposableListener(this.window, EventType.BEFORE_UNLOAD, (e: BeforeUnloadEvent) => this.onBeforeUnload(e)));
|
||||
this._register(addDisposableListener(this.window, EventType.UNLOAD, () => this._onWillClose.fire()));
|
||||
this._register(addDisposableListener(this.window, EventType.BEFORE_UNLOAD, (e: BeforeUnloadEvent) => this.handleBeforeUnload(e)));
|
||||
this._register(addDisposableListener(this.window, EventType.UNLOAD, () => this.handleUnload()));
|
||||
|
||||
this._register(addDisposableListener(this.window, 'unhandledrejection', e => {
|
||||
onUnexpectedError(e.reason);
|
||||
@@ -108,7 +117,12 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow {
|
||||
}
|
||||
}
|
||||
|
||||
private onBeforeUnload(e: BeforeUnloadEvent): void {
|
||||
private handleBeforeUnload(e: BeforeUnloadEvent): void {
|
||||
|
||||
// Event
|
||||
this._onBeforeUnload.fire();
|
||||
|
||||
// Check for confirm before close setting
|
||||
const confirmBeforeCloseSetting = this.configurationService.getValue<'always' | 'never' | 'keyboardOnly'>('window.confirmBeforeClose');
|
||||
const confirmBeforeClose = confirmBeforeCloseSetting === 'always' || (confirmBeforeCloseSetting === 'keyboardOnly' && ModifierKeyEmitter.getInstance().isModifierPressed);
|
||||
if (confirmBeforeClose) {
|
||||
@@ -121,6 +135,12 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow {
|
||||
e.returnValue = localize('lifecycleVeto', "Changes that you made may not be saved. Please check press 'Cancel' and try again.");
|
||||
}
|
||||
|
||||
private handleUnload(): void {
|
||||
|
||||
// Event
|
||||
this._onUnload.fire();
|
||||
}
|
||||
|
||||
layout(): void {
|
||||
this._onDidLayout.fire(getClientArea(this.window.document.body, this.container));
|
||||
}
|
||||
@@ -153,7 +173,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IHostService protected readonly hostService: IHostService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -171,7 +192,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
|
||||
ensureCodeWindow(targetWindow, resolvedWindowId);
|
||||
|
||||
const containerDisposables = new DisposableStore();
|
||||
const { container, stylesLoaded } = this.createContainer(targetWindow, containerDisposables);
|
||||
const { container, stylesLoaded } = this.createContainer(targetWindow, containerDisposables, options);
|
||||
|
||||
const auxiliaryWindow = this.createAuxiliaryWindow(targetWindow, container, stylesLoaded);
|
||||
|
||||
@@ -208,7 +229,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
|
||||
}
|
||||
|
||||
protected createAuxiliaryWindow(targetWindow: CodeWindow, container: HTMLElement, stylesLoaded: Barrier): AuxiliaryWindow {
|
||||
return new AuxiliaryWindow(targetWindow, container, stylesLoaded, this.configurationService);
|
||||
return new AuxiliaryWindow(targetWindow, container, stylesLoaded, this.configurationService, this.hostService);
|
||||
}
|
||||
|
||||
private async openWindow(options?: IAuxiliaryWindowOpenOptions): Promise<Window | undefined> {
|
||||
@@ -224,8 +245,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
|
||||
const height = Math.max(options?.bounds?.height ?? BrowserAuxiliaryWindowService.DEFAULT_SIZE.height, WindowMinimumSize.HEIGHT);
|
||||
|
||||
let newWindowBounds: IRectangle = {
|
||||
x: options?.bounds?.x ?? (activeWindowBounds.x + activeWindowBounds.width / 2 - width / 2),
|
||||
y: options?.bounds?.y ?? (activeWindowBounds.y + activeWindowBounds.height / 2 - height / 2),
|
||||
x: options?.bounds?.x ?? Math.max(activeWindowBounds.x + activeWindowBounds.width / 2 - width / 2, 0),
|
||||
y: options?.bounds?.y ?? Math.max(activeWindowBounds.y + activeWindowBounds.height / 2 - height / 2, 0),
|
||||
width,
|
||||
height
|
||||
};
|
||||
@@ -263,7 +284,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
|
||||
return BrowserAuxiliaryWindowService.WINDOW_IDS++;
|
||||
}
|
||||
|
||||
protected createContainer(auxiliaryWindow: CodeWindow, disposables: DisposableStore): { stylesLoaded: Barrier; container: HTMLElement } {
|
||||
protected createContainer(auxiliaryWindow: CodeWindow, disposables: DisposableStore, options?: IAuxiliaryWindowOpenOptions): { stylesLoaded: Barrier; container: HTMLElement } {
|
||||
this.patchMethods(auxiliaryWindow);
|
||||
|
||||
this.applyMeta(auxiliaryWindow);
|
||||
|
||||
+20
-10
@@ -5,7 +5,7 @@
|
||||
|
||||
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { AuxiliaryWindow, BrowserAuxiliaryWindowService, IAuxiliaryWindowService } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
|
||||
import { AuxiliaryWindow, BrowserAuxiliaryWindowService, IAuxiliaryWindowOpenOptions, IAuxiliaryWindowService } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
|
||||
import { ISandboxGlobals } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWindowsConfiguration } from 'vs/platform/window/common/window';
|
||||
@@ -20,6 +20,8 @@ import { ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { applyZoom } from 'vs/platform/window/electron-sandbox/window';
|
||||
|
||||
type NativeCodeWindow = CodeWindow & {
|
||||
readonly vscode: ISandboxGlobals;
|
||||
@@ -35,9 +37,10 @@ export class NativeAuxiliaryWindow extends AuxiliaryWindow {
|
||||
stylesHaveLoaded: Barrier,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@INativeHostService private readonly nativeHostService: INativeHostService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IHostService hostService: IHostService
|
||||
) {
|
||||
super(window, container, stylesHaveLoaded, configurationService);
|
||||
super(window, container, stylesHaveLoaded, configurationService, hostService);
|
||||
}
|
||||
|
||||
protected override async confirmBeforeClose(e: BeforeUnloadEvent): Promise<void> {
|
||||
@@ -65,9 +68,10 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService
|
||||
@IDialogService dialogService: IDialogService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IHostService hostService: IHostService
|
||||
) {
|
||||
super(layoutService, dialogService, configurationService, telemetryService);
|
||||
super(layoutService, dialogService, configurationService, telemetryService, hostService);
|
||||
}
|
||||
|
||||
protected override async resolveWindowId(auxiliaryWindow: NativeCodeWindow): Promise<number> {
|
||||
@@ -78,12 +82,18 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService
|
||||
return windowId;
|
||||
}
|
||||
|
||||
protected override createContainer(auxiliaryWindow: NativeCodeWindow, disposables: DisposableStore) {
|
||||
protected override createContainer(auxiliaryWindow: NativeCodeWindow, disposables: DisposableStore, options?: IAuxiliaryWindowOpenOptions) {
|
||||
|
||||
// Zoom level
|
||||
const windowConfig = this.configurationService.getValue<IWindowsConfiguration>();
|
||||
const windowZoomLevel = typeof windowConfig.window?.zoomLevel === 'number' ? windowConfig.window.zoomLevel : 0;
|
||||
auxiliaryWindow.vscode.webFrame.setZoomLevel(windowZoomLevel);
|
||||
let windowZoomLevel: number;
|
||||
if (typeof options?.zoomLevel === 'number') {
|
||||
windowZoomLevel = options.zoomLevel;
|
||||
} else {
|
||||
const windowConfig = this.configurationService.getValue<IWindowsConfiguration>();
|
||||
windowZoomLevel = typeof windowConfig.window?.zoomLevel === 'number' ? windowConfig.window.zoomLevel : 0;
|
||||
}
|
||||
|
||||
applyZoom(windowZoomLevel, auxiliaryWindow);
|
||||
|
||||
return super.createContainer(auxiliaryWindow, disposables);
|
||||
}
|
||||
@@ -110,7 +120,7 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService
|
||||
}
|
||||
|
||||
protected override createAuxiliaryWindow(targetWindow: CodeWindow, container: HTMLElement, stylesHaveLoaded: Barrier,): AuxiliaryWindow {
|
||||
return new NativeAuxiliaryWindow(targetWindow, container, stylesHaveLoaded, this.configurationService, this.nativeHostService, this.instantiationService);
|
||||
return new NativeAuxiliaryWindow(targetWindow, container, stylesHaveLoaded, this.configurationService, this.nativeHostService, this.instantiationService, this.hostService);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService
|
||||
let x: number | undefined;
|
||||
let y: number | undefined;
|
||||
|
||||
let zoom = getZoomFactor();
|
||||
let zoom = getZoomFactor(anchor instanceof HTMLElement ? dom.getWindow(anchor) : dom.getActiveWindow());
|
||||
if (anchor instanceof HTMLElement) {
|
||||
const elementPosition = dom.getDomNodePagePosition(anchor);
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
||||
|
||||
// Editor & group changes
|
||||
if (this.editorGroupsContainer === this.editorGroupService.mainPart || this.editorGroupsContainer === this.editorGroupService) {
|
||||
this.editorGroupService.mainPart.whenReady.then(() => this.onEditorGroupsReady());
|
||||
this.editorGroupService.whenReady.then(() => this.onEditorGroupsReady());
|
||||
} else {
|
||||
this.onEditorGroupsReady();
|
||||
}
|
||||
|
||||
@@ -221,6 +221,42 @@ export interface IEditorGroupsContainer {
|
||||
*/
|
||||
readonly onDidChangeGroupMaximized: Event<boolean>;
|
||||
|
||||
/**
|
||||
* A property that indicates when groups have been created
|
||||
* and are ready to be used in the editor part.
|
||||
*/
|
||||
readonly isReady: boolean;
|
||||
|
||||
/**
|
||||
* A promise that resolves when groups have been created
|
||||
* and are ready to be used in the editor part.
|
||||
*
|
||||
* Await this promise to safely work on the editor groups model
|
||||
* (for example, install editor group listeners).
|
||||
*
|
||||
* Use the `whenRestored` property to await visible editors
|
||||
* having fully resolved.
|
||||
*/
|
||||
readonly whenReady: Promise<void>;
|
||||
|
||||
/**
|
||||
* A promise that resolves when groups have been restored in
|
||||
* the editor part.
|
||||
*
|
||||
* For groups with active editor, the promise will resolve
|
||||
* when the visible editor has finished to resolve.
|
||||
*
|
||||
* Use the `whenReady` property to not await editors to
|
||||
* resolve.
|
||||
*/
|
||||
readonly whenRestored: Promise<void>;
|
||||
|
||||
/**
|
||||
* Find out if the editor part has UI state to restore
|
||||
* from a previous session.
|
||||
*/
|
||||
readonly hasRestorableState: boolean;
|
||||
|
||||
/**
|
||||
* An active group is the default location for new editors to open.
|
||||
*/
|
||||
@@ -412,42 +448,6 @@ export interface IEditorPart extends IEditorGroupsContainer {
|
||||
*/
|
||||
readonly contentDimension: IDimension;
|
||||
|
||||
/**
|
||||
* A property that indicates when groups have been created
|
||||
* and are ready to be used in the editor part.
|
||||
*/
|
||||
readonly isReady: boolean;
|
||||
|
||||
/**
|
||||
* A promise that resolves when groups have been created
|
||||
* and are ready to be used in the editor part.
|
||||
*
|
||||
* Await this promise to safely work on the editor groups model
|
||||
* (for example, install editor group listeners).
|
||||
*
|
||||
* Use the `whenRestored` property to await visible editors
|
||||
* having fully resolved.
|
||||
*/
|
||||
readonly whenReady: Promise<void>;
|
||||
|
||||
/**
|
||||
* A promise that resolves when groups have been restored in
|
||||
* the editor part.
|
||||
*
|
||||
* For groups with active editor, the promise will resolve
|
||||
* when the visible editor has finished to resolve.
|
||||
*
|
||||
* Use the `whenReady` property to not await editors to
|
||||
* resolve.
|
||||
*/
|
||||
readonly whenRestored: Promise<void>;
|
||||
|
||||
/**
|
||||
* Find out if the editor part has UI state to restore
|
||||
* from a previous session.
|
||||
*/
|
||||
readonly hasRestorableState: boolean;
|
||||
|
||||
/**
|
||||
* Find out if an editor group is currently maximized.
|
||||
*/
|
||||
|
||||
@@ -939,11 +939,11 @@ export class HistoryService extends Disposable implements IHistoryService {
|
||||
// We want to seed history from opened editors
|
||||
// too as well as previous stored state, so we
|
||||
// need to wait for the editor groups being ready
|
||||
if (this.editorGroupService.mainPart.isReady) {
|
||||
if (this.editorGroupService.isReady) {
|
||||
this.loadHistory();
|
||||
} else {
|
||||
(async () => {
|
||||
await this.editorGroupService.mainPart.whenReady;
|
||||
await this.editorGroupService.whenReady;
|
||||
|
||||
this.loadHistory();
|
||||
})();
|
||||
|
||||
@@ -15,7 +15,7 @@ import { whenEditorClosed } from 'vs/workbench/browser/editor';
|
||||
import { IWorkspace, IWorkspaceProvider } from 'vs/workbench/browser/web.api';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ILabelService, Verbosity } from 'vs/platform/label/common/label';
|
||||
import { ModifierKeyEmitter, disposableWindowInterval, getActiveDocument, getWindowId, onDidRegisterWindow, trackFocus } from 'vs/base/browser/dom';
|
||||
import { EventType, ModifierKeyEmitter, addDisposableListener, addDisposableThrottledListener, disposableWindowInterval, getActiveDocument, getWindowId, onDidRegisterWindow, trackFocus } from 'vs/base/browser/dom';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
@@ -39,6 +39,7 @@ import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { mainWindow, isAuxiliaryWindow } from 'vs/base/browser/window';
|
||||
import { isIOS, isMacintosh } from 'vs/base/common/platform';
|
||||
|
||||
enum HostShutdownReason {
|
||||
|
||||
@@ -204,6 +205,26 @@ export class BrowserHostService extends Disposable implements IHostService {
|
||||
return Event.latch(emitter.event, undefined, this._store);
|
||||
}
|
||||
|
||||
@memoize
|
||||
get onDidChangeFullScreen(): Event<number> {
|
||||
const emitter = this._register(new Emitter<number>());
|
||||
|
||||
this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposables }) => {
|
||||
const windowId = getWindowId(window);
|
||||
const viewport = isIOS && window.visualViewport ? window.visualViewport /** Visual viewport */ : window /** Layout viewport */;
|
||||
|
||||
// Fullscreen (Browser)
|
||||
for (const event of [EventType.FULLSCREEN_CHANGE, EventType.WK_FULLSCREEN_CHANGE]) {
|
||||
disposables.add(addDisposableListener(window.document, event, () => emitter.fire(windowId)));
|
||||
}
|
||||
|
||||
// Fullscreen (Native)
|
||||
disposables.add(addDisposableThrottledListener(viewport, EventType.RESIZE, () => emitter.fire(windowId), undefined, isMacintosh ? 2000 /* adjust for macOS animation */ : 800 /* can be throttled */));
|
||||
}, { window: mainWindow, disposables: this._store }));
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
|
||||
openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void>;
|
||||
openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise<void> {
|
||||
|
||||
@@ -65,6 +65,12 @@ export interface IHostService {
|
||||
*/
|
||||
readonly onDidChangeActiveWindow: Event<number>;
|
||||
|
||||
/**
|
||||
* Emitted when the window with the given identifier changes
|
||||
* its fullscreen state.
|
||||
*/
|
||||
readonly onDidChangeFullScreen: Event<number>;
|
||||
|
||||
/**
|
||||
* Opens an empty window. The optional parameter allows to define if
|
||||
* a new window should open or the existing one change to an empty.
|
||||
|
||||
@@ -94,6 +94,8 @@ class WorkbenchHostService extends Disposable implements IHostService {
|
||||
return Event.latch(emitter.event, undefined, this._store);
|
||||
}
|
||||
|
||||
readonly onDidChangeFullScreen = Event.filter(this.nativeHostService.onDidChangeWindowFullScreen, id => hasWindow(id), this._store);
|
||||
|
||||
openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
|
||||
openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void>;
|
||||
openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise<void> {
|
||||
|
||||
@@ -27,6 +27,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
|
||||
import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IIssueDataProvider, IIssueUriRequestHandler, IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
|
||||
import { mainWindow } from 'vs/base/browser/window';
|
||||
|
||||
export class NativeIssueService implements IWorkbenchIssueService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
@@ -144,7 +145,7 @@ export class NativeIssueService implements IWorkbenchIssueService {
|
||||
const theme = this.themeService.getColorTheme();
|
||||
const issueReporterData: IssueReporterData = Object.assign({
|
||||
styles: getIssueReporterStyles(theme),
|
||||
zoomLevel: getZoomLevel(),
|
||||
zoomLevel: getZoomLevel(mainWindow),
|
||||
enabledExtensions: extensionData,
|
||||
experiments: experiments?.join('\n'),
|
||||
restrictedMode: !this.workspaceTrustManagementService.isWorkspaceTrusted(),
|
||||
@@ -158,7 +159,7 @@ export class NativeIssueService implements IWorkbenchIssueService {
|
||||
const theme = this.themeService.getColorTheme();
|
||||
const data: ProcessExplorerData = {
|
||||
pid: this.environmentService.mainPid,
|
||||
zoomLevel: getZoomLevel(),
|
||||
zoomLevel: getZoomLevel(mainWindow),
|
||||
styles: {
|
||||
backgroundColor: getColor(theme, editorBackground),
|
||||
color: getColor(theme, editorForeground),
|
||||
|
||||
@@ -241,14 +241,18 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
|
||||
|
||||
this._register(Event.runAndSubscribe(dom.onDidRegisterWindow, ({ window, disposables }) => disposables.add(this._registerKeyListeners(window)), { window: mainWindow, disposables: this._store }));
|
||||
|
||||
this._register(browser.onDidChangeFullscreen(() => {
|
||||
this._register(browser.onDidChangeFullscreen(windowId => {
|
||||
if (windowId !== mainWindow.vscodeWindowId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const keyboard: IKeyboard | null = (<INavigatorWithKeyboard>navigator).keyboard;
|
||||
|
||||
if (BrowserFeatures.keyboard === KeyboardSupport.None) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (browser.isFullscreen()) {
|
||||
if (browser.isFullscreen(mainWindow)) {
|
||||
keyboard?.lock(['Escape']);
|
||||
} else {
|
||||
keyboard?.unlock();
|
||||
@@ -461,7 +465,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BrowserFeatures.keyboard === KeyboardSupport.FullScreen && browser.isFullscreen()) {
|
||||
if (BrowserFeatures.keyboard === KeyboardSupport.FullScreen && browser.isFullscreen(mainWindow)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,11 +116,6 @@ export interface IWorkbenchLayoutService extends ILayoutService {
|
||||
*/
|
||||
readonly onDidChangeZenMode: Event<boolean>;
|
||||
|
||||
/**
|
||||
* Emits when fullscreen is enabled or disabled.
|
||||
*/
|
||||
readonly onDidChangeFullscreen: Event<boolean>;
|
||||
|
||||
/**
|
||||
* Emits when the target window is maximized or unmaximized.
|
||||
*/
|
||||
|
||||
@@ -129,14 +129,14 @@ suite('TextEditorService', () => {
|
||||
// Untyped Input (untitled with file path)
|
||||
input = disposables.add(service.createTextEditor({ resource: URI.file('/some/path.txt'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } }));
|
||||
assert(input instanceof UntitledTextEditorInput);
|
||||
assert.ok((input as UntitledTextEditorInput).model.hasAssociatedFilePath);
|
||||
assert.ok((input as UntitledTextEditorInput).hasAssociatedFilePath);
|
||||
|
||||
// Untyped Input (untitled with untitled resource)
|
||||
untypedInput = { resource: URI.parse('untitled://Untitled-1'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } };
|
||||
assert.ok(isUntitledResourceEditorInput(untypedInput));
|
||||
input = disposables.add(service.createTextEditor(untypedInput));
|
||||
assert(input instanceof UntitledTextEditorInput);
|
||||
assert.ok(!(input as UntitledTextEditorInput).model.hasAssociatedFilePath);
|
||||
assert.ok(!(input as UntitledTextEditorInput).hasAssociatedFilePath);
|
||||
|
||||
// Untyped input (untitled with custom resource, but forceUntitled)
|
||||
untypedInput = { resource: URI.file('/fake'), forceUntitled: true };
|
||||
@@ -149,7 +149,7 @@ suite('TextEditorService', () => {
|
||||
|
||||
input = disposables.add(service.createTextEditor({ resource: URI.parse('untitled-custom://some/path'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } }));
|
||||
assert(input instanceof UntitledTextEditorInput);
|
||||
assert.ok((input as UntitledTextEditorInput).model.hasAssociatedFilePath);
|
||||
assert.ok((input as UntitledTextEditorInput).hasAssociatedFilePath);
|
||||
|
||||
provider.dispose();
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ export class BrowserHostColorSchemeService extends Disposable implements IHostCo
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
addMatchMediaChangeListener('(prefers-color-scheme: dark)', () => {
|
||||
addMatchMediaChangeListener(mainWindow, '(prefers-color-scheme: dark)', () => {
|
||||
this._onDidSchemeChangeEvent.fire();
|
||||
});
|
||||
addMatchMediaChangeListener('(forced-colors: active)', () => {
|
||||
addMatchMediaChangeListener(mainWindow, '(forced-colors: active)', () => {
|
||||
this._onDidSchemeChangeEvent.fire();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export class UntitledTextEditorInputSerializer implements IEditorSerializer {
|
||||
const untitledTextEditorInput = editorInput as UntitledTextEditorInput;
|
||||
|
||||
let resource = untitledTextEditorInput.resource;
|
||||
if (untitledTextEditorInput.model.hasAssociatedFilePath) {
|
||||
if (untitledTextEditorInput.hasAssociatedFilePath) {
|
||||
resource = toLocalResource(resource, this.environmentService.remoteAuthority, this.pathService.defaultUriScheme); // untitled with associated file path use the local schema
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export class UntitledTextEditorInputSerializer implements IEditorSerializer {
|
||||
const languageIdCandidate = untitledTextEditorInput.getLanguageId();
|
||||
if (languageIdCandidate !== PLAINTEXT_LANGUAGE_ID) {
|
||||
languageId = languageIdCandidate;
|
||||
} else if (untitledTextEditorInput.model.hasLanguageSetExplicitly) {
|
||||
} else if (untitledTextEditorInput.hasLanguageSetExplicitly) {
|
||||
languageId = languageIdCandidate;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { dispose, IReference } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, dispose, IReference } from 'vs/base/common/lifecycle';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
|
||||
|
||||
/**
|
||||
@@ -37,10 +37,11 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp
|
||||
}
|
||||
|
||||
private modelResolve: Promise<void> | undefined = undefined;
|
||||
private readonly modelDisposables = this._register(new DisposableStore());
|
||||
private cachedUntitledTextEditorModelReference: IReference<IUntitledTextEditorModel> | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
readonly model: IUntitledTextEditorModel,
|
||||
protected model: IUntitledTextEditorModel,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@ILabelService labelService: ILabelService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@@ -54,16 +55,31 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp
|
||||
super(model.resource, undefined, editorService, textFileService, labelService, fileService, filesConfigurationService, textResourceConfigurationService);
|
||||
|
||||
this.registerModelListeners(model);
|
||||
|
||||
this._register(this.textFileService.untitled.onDidCreate(model => this.onDidCreateUntitledModel(model)));
|
||||
}
|
||||
|
||||
private registerModelListeners(model: IUntitledTextEditorModel): void {
|
||||
this.modelDisposables.clear();
|
||||
|
||||
// re-emit some events from the model
|
||||
this._register(model.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
|
||||
this._register(model.onDidChangeName(() => this._onDidChangeLabel.fire()));
|
||||
this.modelDisposables.add(model.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
|
||||
this.modelDisposables.add(model.onDidChangeName(() => this._onDidChangeLabel.fire()));
|
||||
|
||||
// a reverted untitled text editor model renders this input disposed
|
||||
this._register(model.onDidRevert(() => this.dispose()));
|
||||
this.modelDisposables.add(model.onDidRevert(() => this.dispose()));
|
||||
}
|
||||
|
||||
private onDidCreateUntitledModel(model: IUntitledTextEditorModel): void {
|
||||
if (isEqual(model.resource, this.model.resource) && model !== this.model) {
|
||||
|
||||
// Ensure that we keep our model up to date with
|
||||
// the actual model from the service so that we
|
||||
// never get out of sync with the truth.
|
||||
|
||||
this.model = model;
|
||||
this.registerModelListeners(model);
|
||||
}
|
||||
}
|
||||
|
||||
override getName(): string {
|
||||
@@ -116,6 +132,10 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp
|
||||
return this.model.setEncoding(encoding);
|
||||
}
|
||||
|
||||
get hasLanguageSetExplicitly() { return this.model.hasLanguageSetExplicitly; }
|
||||
|
||||
get hasAssociatedFilePath() { return this.model.hasAssociatedFilePath; }
|
||||
|
||||
setLanguageId(languageId: string, source?: string): void {
|
||||
this.model.setLanguageId(languageId, source);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,11 @@ export interface IUntitledTextEditorModelManager {
|
||||
*/
|
||||
readonly onDidChangeLabel: Event<IUntitledTextEditorModel>;
|
||||
|
||||
/**
|
||||
* Events for when untitled text editor models are created.
|
||||
*/
|
||||
readonly onDidCreate: Event<IUntitledTextEditorModel>;
|
||||
|
||||
/**
|
||||
* Events for when untitled text editors are about to be disposed.
|
||||
*/
|
||||
@@ -143,6 +148,9 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe
|
||||
private readonly _onDidChangeEncoding = this._register(new Emitter<IUntitledTextEditorModel>());
|
||||
readonly onDidChangeEncoding = this._onDidChangeEncoding.event;
|
||||
|
||||
private readonly _onDidCreate = this._register(new Emitter<IUntitledTextEditorModel>());
|
||||
readonly onDidCreate = this._onDidCreate.event;
|
||||
|
||||
private readonly _onWillDispose = this._register(new Emitter<IUntitledTextEditorModel>());
|
||||
readonly onWillDispose = this._onWillDispose.event;
|
||||
|
||||
@@ -267,6 +275,9 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe
|
||||
// Add to cache
|
||||
this.mapResourceToModel.set(model.resource, model);
|
||||
|
||||
// Emit as event
|
||||
this._onDidCreate.fire(model);
|
||||
|
||||
// If the model is dirty right from the beginning,
|
||||
// make sure to emit this as an event
|
||||
if (model.isDirty()) {
|
||||
|
||||
@@ -26,6 +26,10 @@ import { timeout } from 'vs/base/common/async';
|
||||
|
||||
suite('Untitled text editors', () => {
|
||||
|
||||
class TestUntitledTextEditorInput extends UntitledTextEditorInput {
|
||||
getModel() { return this.model; }
|
||||
}
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
let instantiationService: IInstantiationService;
|
||||
let accessor: TestServiceAccessor;
|
||||
@@ -44,11 +48,19 @@ suite('Untitled text editors', () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const workingCopyService = accessor.workingCopyService;
|
||||
|
||||
const input1 = instantiationService.createInstance(UntitledTextEditorInput, service.create());
|
||||
const events: IUntitledTextEditorModel[] = [];
|
||||
disposables.add(service.onDidCreate(model => {
|
||||
events.push(model);
|
||||
}));
|
||||
|
||||
const input1 = instantiationService.createInstance(TestUntitledTextEditorInput, service.create());
|
||||
await input1.resolve();
|
||||
assert.strictEqual(service.get(input1.resource), input1.model);
|
||||
assert.strictEqual(service.get(input1.resource), input1.getModel());
|
||||
assert.ok(!accessor.untitledTextEditorService.isUntitledWithAssociatedResource(input1.resource));
|
||||
|
||||
assert.strictEqual(events.length, 1);
|
||||
assert.strictEqual(events[0].resource.toString(), input1.getModel().resource.toString());
|
||||
|
||||
assert.ok(service.get(input1.resource));
|
||||
assert.ok(!service.get(URI.file('testing')));
|
||||
|
||||
@@ -59,16 +71,16 @@ suite('Untitled text editors', () => {
|
||||
assert.ok(!input1.hasCapability(EditorInputCapabilities.RequiresTrust));
|
||||
assert.ok(!input1.hasCapability(EditorInputCapabilities.Scratchpad));
|
||||
|
||||
const input2 = instantiationService.createInstance(UntitledTextEditorInput, service.create());
|
||||
assert.strictEqual(service.get(input2.resource), input2.model);
|
||||
const input2 = instantiationService.createInstance(TestUntitledTextEditorInput, service.create());
|
||||
assert.strictEqual(service.get(input2.resource), input2.getModel());
|
||||
|
||||
// toUntyped()
|
||||
const untypedInput = input1.toUntyped({ preserveViewState: 0 });
|
||||
assert.strictEqual(untypedInput.forceUntitled, true);
|
||||
|
||||
// get()
|
||||
assert.strictEqual(service.get(input1.resource), input1.model);
|
||||
assert.strictEqual(service.get(input2.resource), input2.model);
|
||||
assert.strictEqual(service.get(input1.resource), input1.getModel());
|
||||
assert.strictEqual(service.get(input2.resource), input2.getModel());
|
||||
|
||||
// revert()
|
||||
await input1.revert(0);
|
||||
@@ -80,6 +92,9 @@ suite('Untitled text editors', () => {
|
||||
assert.strictEqual(await service.resolve({ untitledResource: input2.resource }), model);
|
||||
assert.ok(service.get(model.resource));
|
||||
|
||||
assert.strictEqual(events.length, 2);
|
||||
assert.strictEqual(events[1].resource.toString(), input2.resource.toString());
|
||||
|
||||
assert.ok(!input2.isDirty());
|
||||
|
||||
const resourcePromise = awaitDidChangeDirty(accessor.untitledTextEditorService);
|
||||
@@ -214,10 +229,10 @@ suite('Untitled text editors', () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const workingCopyService = accessor.workingCopyService;
|
||||
|
||||
const untitled = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create({ initialValue: 'Hello World' })));
|
||||
const untitled = disposables.add(instantiationService.createInstance(TestUntitledTextEditorInput, service.create({ initialValue: 'Hello World' })));
|
||||
assert.ok(untitled.isDirty());
|
||||
|
||||
const backup = (await untitled.model.backup(CancellationToken.None)).content;
|
||||
const backup = (await untitled.getModel().backup(CancellationToken.None)).content;
|
||||
if (isReadableStream(backup)) {
|
||||
const value = await streamToBuffer(backup as VSBufferReadableStream);
|
||||
assert.strictEqual(value.toString(), 'Hello World');
|
||||
@@ -307,9 +322,9 @@ suite('Untitled text editors', () => {
|
||||
const model = disposables.add(service.create());
|
||||
const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, model));
|
||||
|
||||
assert.ok(!input.model.hasLanguageSetExplicitly);
|
||||
assert.ok(!input.hasLanguageSetExplicitly);
|
||||
input.setLanguageId(PLAINTEXT_LANGUAGE_ID);
|
||||
assert.ok(input.model.hasLanguageSetExplicitly);
|
||||
assert.ok(input.hasLanguageSetExplicitly);
|
||||
|
||||
assert.strictEqual(input.getLanguageId(), PLAINTEXT_LANGUAGE_ID);
|
||||
});
|
||||
@@ -327,9 +342,9 @@ suite('Untitled text editors', () => {
|
||||
const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, model));
|
||||
disposables.add(await input.resolve());
|
||||
|
||||
assert.ok(!input.model.hasLanguageSetExplicitly);
|
||||
assert.ok(!input.hasLanguageSetExplicitly);
|
||||
model.textEditorModel!.setLanguage(accessor.languageService.createById(language));
|
||||
assert.ok(input.model.hasLanguageSetExplicitly);
|
||||
assert.ok(input.hasLanguageSetExplicitly);
|
||||
|
||||
assert.strictEqual(model.getLanguageId(), language);
|
||||
});
|
||||
@@ -346,12 +361,12 @@ suite('Untitled text editors', () => {
|
||||
const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, model));
|
||||
await input.resolve();
|
||||
|
||||
assert.ok(!input.model.hasLanguageSetExplicitly);
|
||||
assert.ok(!input.hasLanguageSetExplicitly);
|
||||
model.textEditorModel!.setLanguage(
|
||||
accessor.languageService.createById(language),
|
||||
// This is really what this is testing
|
||||
LanguageDetectionLanguageEventSource);
|
||||
assert.ok(!input.model.hasLanguageSetExplicitly);
|
||||
assert.ok(!input.hasLanguageSetExplicitly);
|
||||
|
||||
assert.strictEqual(model.getLanguageId(), language);
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
import { BaseWindow } from 'vs/workbench/browser/window';
|
||||
import { TestHostService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
|
||||
suite('Window', () => {
|
||||
|
||||
@@ -18,7 +19,7 @@ suite('Window', () => {
|
||||
class TestWindow extends BaseWindow {
|
||||
|
||||
constructor(window: CodeWindow, dom: { getWindowsCount: () => number; getWindows: () => Iterable<IRegisteredCodeWindow> }) {
|
||||
super(window, dom);
|
||||
super(window, dom, new TestHostService());
|
||||
}
|
||||
|
||||
protected override enableWindowFocusOnElementFocus(): void { }
|
||||
|
||||
@@ -605,7 +605,6 @@ export class TestLayoutService implements IWorkbenchLayoutService {
|
||||
|
||||
onDidChangeZenMode: Event<boolean> = Event.None;
|
||||
onDidChangeCenteredLayout: Event<boolean> = Event.None;
|
||||
onDidChangeFullscreen: Event<boolean> = Event.None;
|
||||
onDidChangeWindowMaximized: Event<{ windowId: number; maximized: boolean }> = Event.None;
|
||||
onDidChangePanelPosition: Event<string> = Event.None;
|
||||
onDidChangePanelAlignment: Event<PanelAlignment> = Event.None;
|
||||
@@ -1492,6 +1491,8 @@ export class TestHostService implements IHostService {
|
||||
private _onDidChangeWindow = new Emitter<number>();
|
||||
readonly onDidChangeActiveWindow = this._onDidChangeWindow.event;
|
||||
|
||||
readonly onDidChangeFullScreen: Event<number> = Event.None;
|
||||
|
||||
setFocus(focus: boolean) {
|
||||
this._hasFocus = focus;
|
||||
this._onDidChangeFocus.fire(this._hasFocus);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user