From 7c2b2aa67e4e00ff1ebc24f3f28c179a8d304f6e Mon Sep 17 00:00:00 2001 From: aamunger Date: Tue, 14 Feb 2023 10:43:27 -0800 Subject: [PATCH 1/4] allow non-api command instead --- .../contrib/notebook/browser/view/renderers/backLayerWebView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index bfbb1132eaf38..d839497f19e2f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -675,7 +675,7 @@ export class BackLayerWebView extends Themable { 'github-issues.authNow', 'workbench.extensions.search', 'workbench.action.openSettings', - 'notebook.selectKernel', + '_notebook.selectKernel', ], }); return; From e8a66223fe033d2466804bfafde0f00e2a6eb7a1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Feb 2023 21:15:07 +0100 Subject: [PATCH 2/4] HTML files default to save as .txt, but only if line 1 is not blank (fix #174048) (#174395) --- .../textfile/browser/textFileService.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 88f8842da6fdc..a78fc891cf9c1 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -591,13 +591,18 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // of untitled model if it is a valid path name and // figure out the file extension from the mode if any. + let nameCandidate: string; if (await this.pathService.hasValidBasename(joinPath(defaultFilePath, model.name), model.name)) { - const languageId = model.getLanguageId(); - if (languageId && languageId !== PLAINTEXT_LANGUAGE_ID) { - suggestedFilename = this.suggestFilename(languageId, model.name); - } else { - suggestedFilename = model.name; - } + nameCandidate = model.name; + } else { + nameCandidate = basename(resource); + } + + const languageId = model.getLanguageId(); + if (languageId && languageId !== PLAINTEXT_LANGUAGE_ID) { + suggestedFilename = this.suggestFilename(languageId, nameCandidate); + } else { + suggestedFilename = nameCandidate; } } } From 4119dea5ff126d137f75863b8e2c1c73662111ff Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 14 Feb 2023 13:34:11 -0700 Subject: [PATCH 3/4] Polish explorer tooltip rendering (#174085) * Place explorer tooltip rendering behind a setting * Implement windows hover behavior * Pass the actual mouse event rather than a synthetic click * Remove explorer setting * Remove more code that was added for the setting * Fix bad merge * Address PR comments --- .../files/browser/media/explorerviewlet.css | 8 +++ .../files/browser/views/explorerViewer.ts | 70 +++++++++++++++++-- .../workbench/services/hover/browser/hover.ts | 6 +- .../services/hover/browser/hoverService.ts | 5 ++ 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index 89e5008d3c429..ba3fc0686f6d5 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -9,6 +9,14 @@ height: 100%; } +.explorer-item-hover { + /* -- Must set important as hover overrides the cursor -- */ + cursor: pointer !important; + padding-left: 6px; + height: 22px; + font-size: 13px; +} + .explorer-folders-view .monaco-list-row { padding-left: 4px; /* align top level twistie with `Explorer` title label */ } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 65a9ad1461435..709f03e594836 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -48,7 +48,7 @@ import { ITreeCompressionDelegate } from 'vs/base/browser/ui/tree/asyncDataTree' import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; import { ILabelService } from 'vs/platform/label/common/label'; -import { isNumber } from 'vs/base/common/types'; +import { isNumber, isStringArray } from 'vs/base/common/types'; import { IEditableData } from 'vs/workbench/common/views'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -62,6 +62,9 @@ import { ResourceSet } from 'vs/base/common/map'; import { TernarySearchTree } from 'vs/base/common/ternarySearchTree'; import { defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { timeout } from 'vs/base/common/async'; +import { IHoverDelegate, IHoverDelegateOptions, IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; +import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -275,6 +278,64 @@ export class FilesRenderer implements ICompressibleTreeRenderer(); readonly onDidChangeActiveDescendant = this._onDidChangeActiveDescendant.event; + private readonly hoverDelegate = new class implements IHoverDelegate { + + readonly placement = 'element'; + + get delay() { + return this.configurationService.getValue('workbench.hover.delay'); + } + + constructor( + private readonly configurationService: IConfigurationService, + private readonly hoverService: IHoverService + ) { } + + showHover(options: IHoverDelegateOptions, focus?: boolean): IHoverWidget | undefined { + let element: HTMLElement; + if (options.target instanceof HTMLElement) { + element = options.target; + } else { + element = options.target.targetElements[0]; + } + + const row = element.closest('.monaco-tl-row') as HTMLElement | undefined; + + const child = element.querySelector('div.monaco-icon-label-container') as Element | undefined; + const childOfChild = child?.querySelector('span.monaco-icon-name-container') as HTMLElement | undefined; + let overflowed = false; + if (childOfChild && child) { + const width = child.clientWidth; + const childWidth = childOfChild.offsetWidth; + // Check if element is overflowing its parent container + overflowed = width <= childWidth; + } + + const hasDecoration = element.classList.toString().includes('monaco-decoration-iconBadge'); + // If it's overflowing or has a decoration show the tooltip + overflowed = overflowed || hasDecoration; + + const indentGuideElement = row?.querySelector('.monaco-tl-indent') as HTMLElement | undefined; + if (!indentGuideElement) { + return; + } + + return overflowed ? this.hoverService.showHover({ + ...options, + target: indentGuideElement, + compact: true, + additionalClasses: ['explorer-item-hover'], + skipFadeInAnimation: true, + showPointer: false, + onClick: (e) => { + this.hoverService.hideHover(); + element.dispatchEvent(new MouseEvent(e.type, { ...e, bubbles: true })); + }, + hoverPosition: HoverPosition.RIGHT, + }, focus) : undefined; + } + }(this.configurationService, this.hoverService); + constructor( container: HTMLElement, private labels: ResourceLabels, @@ -285,7 +346,8 @@ export class FilesRenderer implements ICompressibleTreeRenderer(); @@ -317,8 +379,7 @@ export class FilesRenderer implements ICompressibleTreeRenderer { try { if (templateData.currentContext) { @@ -417,6 +478,7 @@ export class FilesRenderer implements ICompressibleTreeRenderer('hoverService'); @@ -129,6 +128,11 @@ export interface IHoverOptions { trapFocus?: boolean; /** + * A callback which will be executed when the hover is clicked + */ + onClick?(e: MouseEvent): void; + + /* * The container to pass to {@link IContextViewProvider.showContextView} which renders the hover * in. This is particularly useful for more natural tab focusing behavior, where the hover is * created as the next tab index after the element being hovered and/or to workaround the diff --git a/src/vs/workbench/services/hover/browser/hoverService.ts b/src/vs/workbench/services/hover/browser/hoverService.ts index c6fbd3a8d87c8..ab51a888eb3d2 100644 --- a/src/vs/workbench/services/hover/browser/hoverService.ts +++ b/src/vs/workbench/services/hover/browser/hoverService.ts @@ -64,6 +64,11 @@ export class HoverService implements IHoverService { options.container ); hover.onRequestLayout(() => provider.layout()); + if (options.onClick) { + hoverDisposables.add(addDisposableListener(hover.domNode, EventType.CLICK, e => { + options.onClick!(e); + })); + } if ('targetElements' in options.target) { for (const element of options.target.targetElements) { hoverDisposables.add(addDisposableListener(element, EventType.CLICK, () => this.hideHover())); From a69701a464e40df587ff62056e28d116fed0241d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Feb 2023 00:45:31 +0100 Subject: [PATCH 4/4] profile indicator (#174402) --- .../parts/activitybar/activitybarActions.ts | 31 ++++++++++++++++++- .../activitybar/media/activityaction.css | 30 +++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 25e258e3f312d..1aeaf05b8a8b6 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -5,10 +5,11 @@ import 'vs/css!./media/activityaction'; import { localize } from 'vs/nls'; -import { EventType, addDisposableListener, EventHelper } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, EventHelper, append, $, clearNode, hide, show } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { Action, IAction, Separator, SubmenuAction, toAction } from 'vs/base/common/actions'; +import { Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IMenuService, MenuId, IMenu, registerAction2, Action2, IAction2Options } from 'vs/platform/actions/common/actions'; @@ -344,6 +345,9 @@ export interface IProfileActivity extends IActivity { export class GlobalActivityActionViewItem extends MenuActivityActionViewItem { + private profileBadge: HTMLElement | undefined; + private profileBadgeContent: HTMLElement | undefined; + constructor( action: ActivityAction, contextMenuActionsProvider: () => IAction[], @@ -360,6 +364,31 @@ export class GlobalActivityActionViewItem extends MenuActivityActionViewItem { @IKeybindingService keybindingService: IKeybindingService, ) { super(MenuId.GlobalActivity, action, contextMenuActionsProvider, true, colors, activityHoverOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); + this._register(Event.any(this.userDataProfileService.onDidUpdateCurrentProfile, this.userDataProfileService.onDidChangeCurrentProfile)(() => this.updateProfileBadge())); + } + + override render(container: HTMLElement): void { + super.render(container); + + this.profileBadge = append(container, $('.profile-badge')); + this.profileBadgeContent = append(this.profileBadge, $('.profile-badge-content')); + this.updateProfileBadge(); + } + + protected updateProfileBadge(): void { + if (!this.profileBadge || !this.profileBadgeContent) { + return; + } + + clearNode(this.profileBadgeContent); + hide(this.profileBadge); + + if (this.userDataProfileService.currentProfile.isDefault) { + return; + } + + this.profileBadgeContent.textContent = this.userDataProfileService.currentProfile.name.substring(0, 2).toUpperCase(); + show(this.profileBadge); } protected override computeTitle(): string { diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index 5f52051112bb3..1303822108e7f 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -128,10 +128,10 @@ outline: none; } +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .profile-badge, .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .active-item-indicator, .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .badge { position: absolute; - z-index: 1; top: 0; bottom: 0; margin: auto; @@ -141,6 +141,15 @@ height: 100%; } +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .active-item-indicator, +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .badge { + z-index: 2; +} + +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .profile-badge { + z-index: 1; +} + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .active-item-indicator { pointer-events: none; } @@ -163,6 +172,24 @@ text-align: center; } +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .profile-badge .profile-badge-content { + position: absolute; + font-weight: 600; + font-size: 11px; + line-height: 10px; + top: 28px; + right: 24px; + padding: 1px; + border-radius: 3px; + background-color: var(--vscode-activityBar-background); + color: var(--vscode-activityBar-inactiveForeground); +} + +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:active .profile-badge-content, +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .profile-badge-content, +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:hover .profile-badge-content { + color: var(--vscode-activityBar-foreground); +} .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .badge .codicon.badge-content { font-size: 12px; @@ -178,6 +205,7 @@ /* Right aligned */ +.monaco-workbench .activitybar.right>.content :not(.monaco-menu)>.monaco-action-bar .profile-badge, .monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .badge { left: auto; right: 0;