Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move watermark shortcuts closer to letterpress #170403

Merged
merged 2 commits into from Jan 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 6 additions & 14 deletions src/vs/workbench/browser/parts/editor/editorGroupView.ts
Expand Up @@ -11,7 +11,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput';
import { Emitter, Relay } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor, asCSSUrl, IDomNodePagePosition } from 'vs/base/browser/dom';
import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor, IDomNodePagePosition } from 'vs/base/browser/dom';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
Expand Down Expand Up @@ -43,7 +43,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { hash } from 'vs/base/common/hash';
import { getMimeTypes } from 'vs/editor/common/services/languagesAssociations';
import { extname, isEqual } from 'vs/base/common/resources';
import { AppResourcePath, FileAccess, Schemas } from 'vs/base/common/network';
import { Schemas } from 'vs/base/common/network';
import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/editor';
import { IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
Expand All @@ -55,6 +55,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { TrustedTelemetryValue } from 'vs/platform/telemetry/common/telemetryUtils';
import { defaultProgressBarStyles } from 'vs/platform/theme/browser/defaultStyles';
import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash';
import { EditorGroupWatermark } from 'vs/workbench/browser/parts/editor/editorGroupWatermark';

export class EditorGroupView extends Themable implements IEditorGroupView {

Expand Down Expand Up @@ -178,10 +179,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Container context menu
this.createContainerContextMenu();

// Letterpress container
const letterpressContainer = document.createElement('div');
letterpressContainer.classList.add('editor-group-letterpress');
this.element.appendChild(letterpressContainer);
// Watermark & shortcuts
this._register(this.instantiationService.createInstance(EditorGroupWatermark, this.element));

// Progress bar
this.progressBar = this._register(new ProgressBar(this.element, defaultProgressBarStyles));
Expand Down Expand Up @@ -1904,6 +1903,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {

layout(width: number, height: number, top: number, left: number): void {
this.lastLayout = { width, height, top, left };
this.element.classList.toggle('max-height-478px', height <= 478);

// Layout the title area first to receive the size it occupies
const titleAreaSize = this.titleAreaControl.layout({
Expand Down Expand Up @@ -1953,14 +1953,6 @@ export interface EditorReplacement extends IEditorReplacement {

registerThemingParticipant((theme, collector) => {

// Letterpress
const letterpress: AppResourcePath = `vs/workbench/browser/parts/editor/media/letterpress-${theme.type}.svg`;
collector.addRule(`
.monaco-workbench .part.editor > .content .editor-group-container.empty .editor-group-letterpress {
background-image: ${asCSSUrl(FileAccess.asBrowserUri(letterpress))}
}
`);

// Focused Empty Group Border
const focusedEmptyGroupBorder = theme.getColor(EDITOR_GROUP_FOCUSED_EMPTY_BORDER);
if (focusedEmptyGroupBorder) {
Expand Down
178 changes: 178 additions & 0 deletions src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts
@@ -0,0 +1,178 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { isMacintosh, isWeb, OS } from 'vs/base/common/platform';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import * as nls from 'vs/nls';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { append, clearNode, $, h } from 'vs/base/browser/dom';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { defaultKeybindingLabelStyles } from 'vs/platform/theme/browser/defaultStyles';

interface WatermarkEntry {
text: string;
id: string;
mac?: boolean;
when?: ContextKeyExpression;
}

const showCommands: WatermarkEntry = { text: nls.localize('watermark.showCommands', "Show All Commands"), id: 'workbench.action.showCommands' };
const quickAccess: WatermarkEntry = { text: nls.localize('watermark.quickAccess', "Go to File"), id: 'workbench.action.quickOpen' };
const openFileNonMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFile', "Open File"), id: 'workbench.action.files.openFile', mac: false };
const openFolderNonMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFolder', "Open Folder"), id: 'workbench.action.files.openFolder', mac: false };
const openFileOrFolderMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFileFolder', "Open File or Folder"), id: 'workbench.action.files.openFileFolder', mac: true };
const openRecent: WatermarkEntry = { text: nls.localize('watermark.openRecent', "Open Recent"), id: 'workbench.action.openRecent' };
const newUntitledFile: WatermarkEntry = { text: nls.localize('watermark.newUntitledFile', "New Untitled Text File"), id: 'workbench.action.files.newUntitledFile' };
const newUntitledFileMacOnly: WatermarkEntry = Object.assign({ mac: true }, newUntitledFile);
const findInFiles: WatermarkEntry = { text: nls.localize('watermark.findInFiles', "Find in Files"), id: 'workbench.action.findInFiles' };
const toggleTerminal: WatermarkEntry = { text: nls.localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), id: 'workbench.action.terminal.toggleTerminal', when: ContextKeyExpr.equals('terminalProcessSupported', true) };
const startDebugging: WatermarkEntry = { text: nls.localize('watermark.startDebugging', "Start Debugging"), id: 'workbench.action.debug.start', when: ContextKeyExpr.equals('terminalProcessSupported', true) };
const toggleFullscreen: WatermarkEntry = { text: nls.localize({ key: 'watermark.toggleFullscreen', comment: ['toggle is a verb here'] }, "Toggle Full Screen"), id: 'workbench.action.toggleFullScreen', when: ContextKeyExpr.equals('terminalProcessSupported', true).negate() };
const showSettings: WatermarkEntry = { text: nls.localize('watermark.showSettings', "Show Settings"), id: 'workbench.action.openSettings', when: ContextKeyExpr.equals('terminalProcessSupported', true).negate() };

const noFolderEntries = [
showCommands,
openFileNonMacOnly,
openFolderNonMacOnly,
openFileOrFolderMacOnly,
openRecent,
newUntitledFileMacOnly
];

const folderEntries = [
showCommands,
quickAccess,
findInFiles,
startDebugging,
toggleTerminal,
toggleFullscreen,
showSettings
];

export class EditorGroupWatermark extends Disposable {

private readonly shortcuts: HTMLElement;
private transientDisposables = this._register(new DisposableStore());
private enabled: boolean;
private workbenchState: WorkbenchState;

constructor(
container: HTMLElement,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ITelemetryService private readonly telemetryService: ITelemetryService
) {
super();

const elements = h('.editor-group-watermark', [
h('.letterpress'),
h('.shortcuts@shortcuts'),
]);

append(container, elements.root);
this.shortcuts = elements.shortcuts;

this.workbenchState = contextService.getWorkbenchState();
this.enabled = this.configurationService.getValue<boolean>('workbench.tips.enabled');

this.registerListeners();

if (this.enabled) {
this.render();
}
}

private registerListeners(): void {
this.lifecycleService.onDidShutdown(() => this.dispose());

this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('workbench.tips.enabled')) {
const enabled = this.configurationService.getValue<boolean>('workbench.tips.enabled');

if (enabled === this.enabled) {
return;
}

this.enabled = enabled;

if (this.enabled) {
this.render();
} else {
this.clear();
}
}
}));

this._register(this.contextService.onDidChangeWorkbenchState(workbenchState => {
if (!this.enabled || this.workbenchState === workbenchState) {
return;
}

this.workbenchState = workbenchState;
this.render();
}));

const allEntriesWhenClauses = [...noFolderEntries, ...folderEntries].filter(entry => entry.when !== undefined).map(entry => entry.when!);
const allKeys = new Set<string>();
allEntriesWhenClauses.forEach(when => when.keys().forEach(key => allKeys.add(key)));
this._register(this.contextKeyService.onDidChangeContext(e => {
if (e.affectsSome(allKeys)) {
this.render();
}
}));
}

private render(): void {
this.clear();

const box = append(this.shortcuts, $('.watermark-box'));
const folder = this.workbenchState !== WorkbenchState.EMPTY;
const selected = (folder ? folderEntries : noFolderEntries)
.filter(entry => !('when' in entry) || this.contextKeyService.contextMatchesRules(entry.when))
.filter(entry => !('mac' in entry) || entry.mac === (isMacintosh && !isWeb))
.filter(entry => !!CommandsRegistry.getCommand(entry.id));

const update = () => {
clearNode(box);
selected.map(entry => {
const dl = append(box, $('dl'));
const dt = append(dl, $('dt'));
dt.textContent = entry.text;
const dd = append(dl, $('dd'));
const keybinding = new KeybindingLabel(dd, OS, { renderUnboundKeybindings: true, ...defaultKeybindingLabelStyles });
keybinding.set(this.keybindingService.lookupKeybinding(entry.id));
});
};

update();
this.transientDisposables.add(this.keybindingService.onDidUpdateKeybindings(update));

/* __GDPR__
"watermark:open" : {
"owner": "digitarald"
}
*/
this.telemetryService.publicLog('watermark:open');
}

private clear(): void {
clearNode(this.shortcuts);
this.transientDisposables.clear();
}

override dispose(): void {
super.dispose();
this.clear();
}
}
99 changes: 86 additions & 13 deletions src/vs/workbench/browser/parts/editor/media/editorgroupview.css
Expand Up @@ -18,28 +18,101 @@
opacity: 1; /* indicate active/dragged-over group through undimmed state */
}

/* Letterpress */
/* Watermark & shortcuts */

.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-letterpress {
display: none; /* only visible when empty */
.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark {
display: flex;
height: 100%;
max-width: 290px;
margin: auto;
flex-direction: column;
align-items: center;
justify-content: center;
}

.monaco-workbench .part.editor > .content .editor-group-container.empty > .editor-group-letterpress {
display: block;
margin: auto;
.monaco-workbench .part.editor > .content .editor-group-container:not(.empty) > .editor-group-watermark {
display: none;
}

.monaco-workbench .part.editor > .content:not(.empty) .editor-group-container.empty > .editor-group-watermark {
max-width: 200px;
height: calc(100% - 35px);
}

.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .letterpress {
width: 100%;
height: calc(100% - 70px); /* centered below toolbar */
max-width: 260px;
max-height: 100%;
aspect-ratio: 1/1;
background-image: url('./letterpress-light.svg');
background-position-x: center;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: 70% 70%;
}

.monaco-workbench .part.editor > .content.empty .editor-group-container.empty > .editor-group-letterpress {
background-size: 100% 100%; /* larger for empty editor part */
height: 100%; /* no toolbar in this case */
.monaco-workbench.vs-dark .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress {
background-image: url('./letterpress-dark.svg');
}

.monaco-workbench.hc-light .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress {
background-image: url('./letterpress-hcLight.svg');
}

.monaco-workbench.hc-black .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress {
background-image: url('./letterpress-hcDark.svg');
}

.monaco-workbench .part.editor > .content:not(.empty) .editor-group-container > .editor-group-watermark > .shortcuts,
.monaco-workbench .part.editor > .content .editor-group-container.max-height-478px > .editor-group-watermark > .shortcuts {
display: none;
}

.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .shortcuts > .watermark-box {
display: inline-table;
border-collapse: separate;
border-spacing: 11px 17px;
}

.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .shortcuts dl {
display: table-row;
opacity: .8;
cursor: default;
}

.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .shortcuts dt {
text-align: right;
letter-spacing: 0.04em
}

.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .shortcuts dd {
text-align: left;
}

.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .shortcuts dt,
.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .shortcuts dd {
display: table-cell;
vertical-align: middle;
}

.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .shortcuts dt,
.monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .shortcuts dl {
color: rgba(0,0,0,.68);
}

.monaco-workbench.vs-dark .part.editor > .content .editor-group-container .editor-group-watermark > .shortcuts dt,
.monaco-workbench.vs-dark .part.editor > .content .editor-group-container .editor-group-watermark > .shortcuts dl {
color: rgba(255,255,255,.6);
}

.monaco-workbench.hc-black .part.editor > .content .editor-group-container .editor-group-watermark > .shortcuts dt,
.monaco-workbench.hc-light .part.editor > .content .editor-group-container .editor-group-watermark > .shortcuts dt {
color: var(--vscode-editor-foreground);
}
.monaco-workbench.hc-black .part.editor > .content .editor-group-container .editor-group-watermark > .shortcuts dl,
.monaco-workbench.hc-light .part.editor > .content .editor-group-container .editor-group-watermark > .shortcuts dl {
color: var(--vscode-editor-foreground);
opacity: 1;
}


/* Title */

.monaco-workbench .part.editor > .content .editor-group-container > .title {
Expand Down
5 changes: 5 additions & 0 deletions src/vs/workbench/browser/workbench.contribution.ts
Expand Up @@ -469,6 +469,11 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
'default': 'both',
'description': localize('layoutControlType', "Controls whether the layout control in the custom title bar is displayed as a single menu button or with multiple UI toggles."),
},
'workbench.tips.enabled': {
'type': 'boolean',
'default': true,
'description': localize('tips.enabled', "When enabled, will show the watermark tips when no editor is open.")
},
}
});

Expand Down