diff --git a/extensions/notebook/package.json b/extensions/notebook/package.json index 92243b400e..7825bedff5 100644 --- a/extensions/notebook/package.json +++ b/extensions/notebook/package.json @@ -131,8 +131,8 @@ "command": "jupyter.cmd.managePackages", "title": "%title.managePackages%", "icon": { - "dark": "resources/dark/manage_inverse.svg", - "light": "resources/light/manage.svg" + "dark": "resources/dark/packages_inverse.svg", + "light": "resources/light/packages.svg" } }, { diff --git a/extensions/notebook/resources/dark/packages_inverse.svg b/extensions/notebook/resources/dark/packages_inverse.svg new file mode 100644 index 0000000000..a46fb203d4 --- /dev/null +++ b/extensions/notebook/resources/dark/packages_inverse.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/notebook/resources/light/packages.svg b/extensions/notebook/resources/light/packages.svg new file mode 100644 index 0000000000..5b75cda4c0 --- /dev/null +++ b/extensions/notebook/resources/light/packages.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/sql/base/browser/ui/buttonMenu/buttonMenu.css b/src/sql/base/browser/ui/buttonMenu/buttonMenu.css new file mode 100644 index 0000000000..3036c3afa3 --- /dev/null +++ b/src/sql/base/browser/ui/buttonMenu/buttonMenu.css @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.button-menu .in-preview .monaco-dropdown { + height: auto; + padding: 0; +} +.button-menu.masked-pseudo-after.dropdown-arrow:after { + right: -2px; + width: 20px; +} diff --git a/src/sql/base/browser/ui/buttonMenu/buttonMenu.ts b/src/sql/base/browser/ui/buttonMenu/buttonMenu.ts new file mode 100644 index 0000000000..81016ff455 --- /dev/null +++ b/src/sql/base/browser/ui/buttonMenu/buttonMenu.ts @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./buttonMenu'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { BaseActionViewItem, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { append, $, addClasses } from 'vs/base/browser/dom'; +import { IDropdownMenuOptions, DropdownMenu, IActionProvider, IContextMenuProvider, ILabelRenderer } from 'vs/base/browser/ui/dropdown/dropdown'; + +export class DropdownMenuActionViewItem extends BaseActionViewItem { + private menuActionsOrProvider: ReadonlyArray | IActionProvider; + private dropdownMenu: DropdownMenu | undefined; + private menuLabel?: string | undefined; + private contextMenuProvider: IContextMenuProvider; + private actionViewItemProvider?: IActionViewItemProvider; + private keybindings?: (action: IAction) => ResolvedKeybinding | undefined; + private cssClass: string | undefined; + private anchorAlignmentProvider: (() => AnchorAlignment) | undefined; + + constructor( + action: IAction, + menuActionsOrProvider: ReadonlyArray | IActionProvider, + contextMenuProvider: IContextMenuProvider, + actionViewItemProvider: IActionViewItemProvider | undefined, + actionRunner: IActionRunner, + keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, + cssClass: string | undefined, + menuLabel: string | undefined, + anchorAlignmentProvider?: () => AnchorAlignment) { + + super(null, action); + + this.menuActionsOrProvider = menuActionsOrProvider; + this.contextMenuProvider = contextMenuProvider; + this.actionViewItemProvider = actionViewItemProvider; + this.actionRunner = actionRunner; + this.keybindings = keybindings; + this.cssClass = cssClass; + this.menuLabel = menuLabel; + this.anchorAlignmentProvider = anchorAlignmentProvider; + } + + render(container: HTMLElement): void { + const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => { + this.element = append(el, $('a.action-label.button-menu')); + if (this.cssClass) { + addClasses(this.element, this.cssClass); + } + if (this.menuLabel) { + this.element.innerText = this.menuLabel; + } + + this.element.tabIndex = 0; + this.element.setAttribute('role', 'button'); + this.element.setAttribute('aria-haspopup', 'true'); + this.element.title = this._action.label || ''; + + return null; + }; + + const options: IDropdownMenuOptions = { + contextMenuProvider: this.contextMenuProvider, + labelRenderer: labelRenderer + }; + + // Render the DropdownMenu around a simple action to toggle it + if (Array.isArray(this.menuActionsOrProvider)) { + options.actions = this.menuActionsOrProvider; + } else { + options.actionProvider = this.menuActionsOrProvider as IActionProvider; + } + + this.dropdownMenu = this._register(new DropdownMenu(container, options)); + this.dropdownMenu.menuOptions = { + actionViewItemProvider: this.actionViewItemProvider, + actionRunner: this.actionRunner, + getKeyBinding: this.keybindings, + context: this._context + }; + + if (this.anchorAlignmentProvider) { + const that = this; + + this.dropdownMenu.menuOptions = { + ...this.dropdownMenu.menuOptions, + get anchorAlignment(): AnchorAlignment { + return that.anchorAlignmentProvider!(); + } + }; + } + } + + setActionContext(newContext: unknown): void { + super.setActionContext(newContext); + + if (this.dropdownMenu) { + if (this.dropdownMenu.menuOptions) { + this.dropdownMenu.menuOptions.context = newContext; + } else { + this.dropdownMenu.menuOptions = { context: newContext }; + } + } + } + + show(): void { + this.dropdownMenu?.show(); + } +} diff --git a/src/sql/base/browser/ui/selectBox/media/selectBox.css b/src/sql/base/browser/ui/selectBox/media/selectBox.css index 9263b51613..c8bb8fc244 100644 --- a/src/sql/base/browser/ui/selectBox/media/selectBox.css +++ b/src/sql/base/browser/ui/selectBox/media/selectBox.css @@ -15,6 +15,10 @@ } .monaco-select-box { - padding: 2px 8px; - padding: 0 22px 0 6px !important; /* I don't like this but for now its fine */ + padding: 0 22px 0 6px; +} + +.monaco-inputbox > .wrapper > .input, +.monaco-inputbox > .wrapper > .mirror { + padding-left: 6px !important; } diff --git a/src/sql/base/browser/ui/taskbar/media/taskbar.css b/src/sql/base/browser/ui/taskbar/media/taskbar.css index 3fcd52d63e..acec293244 100644 --- a/src/sql/base/browser/ui/taskbar/media/taskbar.css +++ b/src/sql/base/browser/ui/taskbar/media/taskbar.css @@ -26,6 +26,7 @@ } .carbon-taskbar.monaco-toolbar .monaco-action-bar.animated .actions-container { + box-sizing: border-box; justify-content: flex-start; padding-left: 15px; flex-wrap: wrap; diff --git a/src/sql/media/icons/action-collapse.svg b/src/sql/media/icons/action-collapse.svg new file mode 100644 index 0000000000..955f68a2af --- /dev/null +++ b/src/sql/media/icons/action-collapse.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/sql/media/icons/action-expand.svg b/src/sql/media/icons/action-expand.svg new file mode 100644 index 0000000000..6cf95b4505 --- /dev/null +++ b/src/sql/media/icons/action-expand.svg @@ -0,0 +1,5 @@ + + opac_command_icons_bv + + + diff --git a/src/sql/media/icons/clear.svg b/src/sql/media/icons/clear.svg new file mode 100644 index 0000000000..ad97fac74d --- /dev/null +++ b/src/sql/media/icons/clear.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/sql/media/icons/code.svg b/src/sql/media/icons/code.svg new file mode 100644 index 0000000000..083e3729dc --- /dev/null +++ b/src/sql/media/icons/code.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/sql/media/icons/common-icons.css b/src/sql/media/icons/common-icons.css index 467b2c3429..f85aae7cc2 100644 --- a/src/sql/media/icons/common-icons.css +++ b/src/sql/media/icons/common-icons.css @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ .vs .codicon.settings { - background-image: url('settings.svg'); + background-image: url("settings.svg"); } .vs-dark .codicon.settings, .hc-black .codicon.settings { - background-image: url('settings_inverse.svg'); + background-image: url("settings_inverse.svg"); } .vs .codicon.backup, @@ -123,21 +123,21 @@ .vs .codicon.scriptToClipboard, .vs-dark .codicon.scriptToClipboard, .hc-black .codicon.scriptToClipboard { - background-image: url('script_to_clipboard.svg'); + background-image: url("script_to_clipboard.svg"); background-repeat: no-repeat; background-position: 2px center; } .vs .codicon.close, .vs .codicon.remove { - background: url('close.svg') center center no-repeat !important; + background: url("close.svg") center center no-repeat !important; } .vs-dark .codicon.close, .hc-black .codicon.close, .vs-dark .codicon.remove, .hc-black .codicon.remove { - background: url('close_inverse.svg') center center no-repeat !important; + background: url("close_inverse.svg") center center no-repeat !important; } .vs .codicon.filter { @@ -181,20 +181,20 @@ .hc-black .codicon.toggle-more, .vs-dark .codicon.toggle-more { - background: url('ellipsis-inverse.svg') center center no-repeat; + background: url("ellipsis-inverse.svg") center center no-repeat; } .vs .codicon.toggle-more { - background: url('ellipsis.svg') center center no-repeat; + background: url("ellipsis.svg") center center no-repeat; } .hc-black .codicon.new, .vs-dark .codicon.new { - background: url('new_inverse.svg') center center no-repeat; + background: url("new_inverse.svg") center center no-repeat; } .vs .codicon.new { - background: url('new.svg') center center no-repeat; + background: url("new.svg") center center no-repeat; } .vs .codicon.new-query, @@ -206,7 +206,7 @@ .vs .codicon.configure-dashboard, .hc-black .codicon.configure-dashboard, .vs-dark .codicon.configure-dashboard { - background: url('configuredashboard.svg') center center no-repeat; + background: url("configuredashboard.svg") center center no-repeat; } .vs .codicon.edit, @@ -217,95 +217,212 @@ .hc-black .codicon.pin, .vs-dark .codicon.pin { - background: url('pin_inverse.svg') center center no-repeat; + background: url("pin_inverse.svg") center center no-repeat; } .vs .codicon.pin { - background: url('pin.svg') center center no-repeat; + background: url("pin.svg") center center no-repeat; } .hc-black .codicon.unpin, .vs-dark .codicon.unpin { - background: url('unpin_inverse.svg') center center no-repeat; + background: url("unpin_inverse.svg") center center no-repeat; } .vs .codicon.unpin { - background: url('unpin.svg') center center no-repeat; + background: url("unpin.svg") center center no-repeat; } .vs .sql.codicon.pause { - background-image: url('pause.svg') + background-image: url("pause.svg"); } .vs-dark .sql.codicon.pause, .hc-black .sql.codicon.pause { - background-image: url('pause_inverse.svg') + background-image: url("pause_inverse.svg"); } .vs .sql.codicon.continue { - background-image: url('continue.svg') + background-image: url("continue.svg"); } .vs-dark .sql.codicon.continue, .hc-black .sql.codicon.continue { - background-image: url('continue_inverse.svg') + background-image: url("continue_inverse.svg"); } .vs .sql.codicon.checked { - background-image: url('check.svg') + background-image: url("check.svg"); } .vs-dark .sql.codicon.checked, .hc-black .sql.codicon.checked { - background-image: url('check_inverse.svg') + background-image: url("check_inverse.svg"); } .vs .sql.codicon.start { - background-image: url('start.svg') + background-image: url("start.svg"); } .vs-dark .sql.codicon.start, .hc-black .sql.codicon.start { - background-image: url('start_inverse.svg') + background-image: url("start_inverse.svg"); } .vs .sql.codicon.stop { - background-image: url('stop.svg') + background-image: url("stop.svg"); } .vs-dark .sql.codicon.stop, .hc-black .sql.codicon.stop { - background-image: url('stop_inverse.svg') + background-image: url("stop_inverse.svg"); } /* Notebook cells */ .codicon.toolbarIconRunInactive { - background-image: url('execute_cell_grey.svg'); + background-image: url("execute_cell_grey.svg"); } .codicon.toolbarIconRun { - background-image: url('execute_cell.svg'); + background-image: url("execute_cell.svg"); } .codicon.toolbarIconRunError { - background-image: url('execute_cell_error.svg'); + background-image: url("execute_cell_error.svg"); } .codicon.toolbarIconStop { - background-image: url('stop_cell_solidanimation.svg'); + background-image: url("stop_cell_solidanimation.svg"); } .vs-dark .codicon.toolbarIconRunInactive { - background-image: url('execute_cell_dark.svg'); + background-image: url("execute_cell_dark.svg"); } .vs-dark .codicon.toolbarIconRun { - background-image: url('execute_cell_white.svg'); + background-image: url("execute_cell_white.svg"); } .hc-black .codicon.toolbarIconRunInactive { - background-image: url('execute_cell_hc.svg'); + background-image: url("execute_cell_hc.svg"); } .hc-black .codicon.toolbarIconRun { - background-image: url('execute_cell_orange_hc.svg'); + background-image: url("execute_cell_orange_hc.svg"); } .vs-dark .codicon.toolbarIconStop, .hc-black .codicon.toolbarIconStop { - background-image: url('stop_cell_solidanimation_inverse.svg'); + background-image: url("stop_cell_solidanimation_inverse.svg"); +} + +/* Icons as masked elements for easy theme switching. +Includes non-masked style declarations. */ +.masked-icon { + display: inline-block; + height: 20px; + width: 20px; + -webkit-mask-position: center; + -webkit-mask-repeat: no-repeat; + mask-position: center; + mask-repeat: no-repeat; + -webkit-mask-size: 50% 100%; + mask-size: 50% 100%; +} + +.codicon.bold { + -webkit-mask-image: url("toolbar-bold.svg"); + mask-image: url("toolbar-bold.svg"); +} +.codicon.italic { + -webkit-mask-image: url("toolbar-italic.svg"); + mask-image: url("toolbar-italic.svg"); +} +.codicon.underline { + -webkit-mask-image: url("toolbar-underline.svg"); + mask-image: url("toolbar-underline.svg"); +} +.codicon.highlight { + -webkit-mask-image: url("toolbar-highlight.svg"); + mask-image: url("toolbar-highlight.svg"); +} +.codicon.code { + -webkit-mask-image: url("toolbar-code.svg"); + mask-image: url("toolbar-code.svg"); +} +.codicon.insert-link { + -webkit-mask-image: url("toolbar-link.svg"); + mask-image: url("toolbar-link.svg"); +} +.codicon.list { + -webkit-mask-image: url("toolbar-list.svg"); + mask-image: url("toolbar-list.svg"); +} +.codicon.ordered-list { + -webkit-mask-image: url("toolbar-ordered-list.svg"); + mask-image: url("toolbar-ordered-list.svg"); +} +.codicon.insert-image { + -webkit-mask-image: url("toolbar-image.svg"); + mask-image: url("toolbar-image.svg"); +} +.codicon.split-toggle-on { + -webkit-mask-image: url("toolbar-preview-toggle-on.svg"); + mask-image: url("toolbar-preview-toggle-on.svg"); +} +.codicon.split-toggle-off { + -webkit-mask-image: url("toolbar-preview-toggle-off.svg"); + mask-image: url("toolbar-preview-toggle-off.svg"); +} +.codicon.code { + -webkit-mask-image: url("code.svg"); + mask-image: url("code.svg"); +} +.codicon.markdown { + -webkit-mask-image: url("markdown.svg"); + mask-image: url("markdown.svg"); +} + +.codicon:not(.masked-icon).icon-expand-cells { + background-image: url("action-expand.svg"); +} +.codicon.masked-icon.icon-expand-cells { + background-image: none; + -webkit-mask-image: url("action-expand.svg"); + mask-image: url("action-expand.svg"); +} + +.codicon:not(.masked-icon).icon-collapse-cells { + background-image: url("action-collapse.svg"); +} +.codicon.masked-icon.icon-collapse-cells { + -webkit-mask-image: url("action-collapse.svg"); + mask-image: url("action-collapse.svg"); +} + +.codicon:not(.masked-icon).icon-clear-results { + background-image: url("clear.svg"); +} +.codicon.masked-icon.icon-clear-results { + -webkit-mask-image: url("clear.svg"); + mask-image: url("clear.svg"); +} + +.codicon:not(.masked-icon).icon-shield { + background-image: url("shield.svg"); +} +.codicon.masked-icon.icon-shield { + -webkit-mask-image: url("shield.svg"); + mask-image: url("shield.svg"); +} + +.codicon:not(.masked-icon).icon-shield-x { + background-image: url("shield-x.svg"); +} +.codicon.masked-icon.icon-shield-x { + -webkit-mask-image: url("shield-x.svg"); + mask-image: url("shield-x.svg"); +} + +.codicon:not(.masked-icon).packages { + background-image: url("packages.svg"); +} +.codicon.masked-icon.packages { + background-image: none; + -webkit-mask-image: url("packages.svg"); + mask-image: url("packages.svg"); } .codicon.arrow-up { @@ -316,78 +433,95 @@ background-image: url("chevron_up_inverse.svg"); } -.codicon.arrow-down { +.codicon:not(.masked-icon).arrow-down { background-image: url("chevron_down.svg"); } -.vs-dark .codicon.arrow-down, -.hc-black .codicon.arrow-down { +.vs-dark .codicon:not(.masked-icon).arrow-down, +.hc-black .codicon:not(.masked-icon).arrow-down { background-image: url("chevron_down_inverse.svg"); } - -/* Icons as masked elements for easy theme switching */ -.codicon.bold { - -webkit-mask-image: url('toolbar-bold.svg'); - mask-image: url('toolbar-bold.svg'); -} -.codicon.italic { - -webkit-mask-image: url('toolbar-italic.svg'); - mask-image: url('toolbar-italic.svg'); -} -.codicon.highlight { - -webkit-mask-image: url('toolbar-highlight.svg'); - mask-image: url('toolbar-highlight.svg'); -} -.codicon.code { - -webkit-mask-image: url('toolbar-code.svg'); - mask-image: url('toolbar-code.svg'); -} -.codicon.insert-link { - -webkit-mask-image: url('toolbar-link.svg'); - mask-image: url('toolbar-link.svg'); -} -.codicon.list { - -webkit-mask-image: url('toolbar-list.svg'); - mask-image: url('toolbar-list.svg'); -} -.codicon.ordered-list { - -webkit-mask-image: url('toolbar-ordered-list.svg'); - mask-image: url('toolbar-ordered-list.svg'); -} -.codicon.insert-image { - -webkit-mask-image: url('toolbar-image.svg'); - mask-image: url('toolbar-image.svg'); -} -.codicon.split-toggle-on { - -webkit-mask-image: url('toolbar-preview-toggle-on.svg'); - mask-image: url('toolbar-preview-toggle-on.svg'); -} -.codicon.split-toggle-off { - -webkit-mask-image: url('toolbar-preview-toggle-off.svg'); - mask-image: url('toolbar-preview-toggle-off.svg'); +.codicon.masked-icon.arrow-down { + background-image: none; + -webkit-mask-image: url("chevron_down.svg"); + mask-image: url("chevron_down.svg"); +} + +.vs .codicon.new-blue { + background-image: url("new-blue.svg"); +} +.vs .codicon.start-outline { + background-image: url("start-outline.svg"); +} + +/* Masked element inside pseudo element */ +.masked-pseudo { + background-image: none !important; +} +.masked-pseudo:before, +.masked-pseudo-after:after { + content: ""; + display: block; + position: absolute; + -webkit-mask-position: center; + -webkit-mask-repeat: no-repeat; + mask-position: center; + mask-repeat: no-repeat; + -webkit-mask-size: 50% 100%; + mask-size: 50% 100%; +} +.masked-pseudo:before { + height: 23px; + left: 0; + top: 2px; + width: 30px; +} +.masked-pseudo-after:after { + height: 23px; + right: 0; + top: 2px; + width: 30px; +} +.masked-pseudo-after.dropdown-arrow:after { + background-image: none; + -webkit-mask-image: url("chevron_down.svg"); + mask-image: url("chevron_down.svg"); +} +.masked-pseudo.add-new:before { + -webkit-mask-image: url("new.svg"); + mask-image: url("new.svg"); +} +.masked-pseudo.start-outline:before { + -webkit-mask-image: url("start-outline.svg"); + mask-image: url("start-outline.svg"); +} + +.masked-pseudo.code:before { + -webkit-mask-image: url("code.svg"); + mask-image: url("code.svg"); +} +.masked-pseudo.markdown:before { + -webkit-mask-image: url("markdown.svg"); + mask-image: url("markdown.svg"); } -/* Cell toolbar icons */ .cell-tool-close { - background-image: url('close-blue.svg'); + background-image: url("close-blue.svg"); } .cell-tool-edit { - background-image: url('edit.svg'); -} -.cell-tool-add { - background-image: url('new-blue.svg'); + background-image: url("edit.svg"); } .cell-tool-move-up { - background-image: url('down-arrow-blue.svg'); + background-image: url("down-arrow-blue.svg"); transform: scale(-1); } .cell-tool-move-down { - background-image: url('down-arrow-blue.svg'); + background-image: url("down-arrow-blue.svg"); } .cell-tool-delete { - background-image: url('garbage-can-blue.svg'); + background-image: url("garbage-can-blue.svg"); } .cell-tool-more { - background-image: url('ellipsis-blue.svg'); + background-image: url("ellipsis-blue.svg"); } .database-colored.codicon { diff --git a/src/sql/media/icons/markdown.svg b/src/sql/media/icons/markdown.svg new file mode 100644 index 0000000000..ce246b60cb --- /dev/null +++ b/src/sql/media/icons/markdown.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/sql/media/icons/packages.svg b/src/sql/media/icons/packages.svg new file mode 100644 index 0000000000..5b75cda4c0 --- /dev/null +++ b/src/sql/media/icons/packages.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/sql/media/icons/shield-x.svg b/src/sql/media/icons/shield-x.svg new file mode 100644 index 0000000000..9ae38feedf --- /dev/null +++ b/src/sql/media/icons/shield-x.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/sql/media/icons/shield.svg b/src/sql/media/icons/shield.svg new file mode 100644 index 0000000000..a458fa9c64 --- /dev/null +++ b/src/sql/media/icons/shield.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/sql/media/icons/start-outline.svg b/src/sql/media/icons/start-outline.svg new file mode 100644 index 0000000000..3464243804 --- /dev/null +++ b/src/sql/media/icons/start-outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/sql/media/icons/toolbar-underline.svg b/src/sql/media/icons/toolbar-underline.svg new file mode 100644 index 0000000000..0b3dc338a7 --- /dev/null +++ b/src/sql/media/icons/toolbar-underline.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/sql/platform/theme/common/colorRegistry.ts b/src/sql/platform/theme/common/colorRegistry.ts index 296055663c..15ea876d9e 100644 --- a/src/sql/platform/theme/common/colorRegistry.ts +++ b/src/sql/platform/theme/common/colorRegistry.ts @@ -42,19 +42,26 @@ export const gradientTwo = registerColor('gradientTwo', { light: gradientTwoColo export const gradientBackground = registerColor('gradientBackground', { light: '#fff', dark: 'transparent', hc: 'transparent' }, nls.localize('gradientBackground', "The background color for the banner image gradient")); // --- Notebook Colors +export const notebookToolbarIcon = registerColor('notebook.notebookToolbarIcon', { light: '#0078D4', dark: '#3AA0F3', hc: '#FFFFFF' }, nls.localize('notebook.notebookToolbarIcon', "Notebook: Main toolbar icons")); +export const notebookToolbarSelectBorder = registerColor('notebook.notebookToolbarSelectBorder', { light: '#A5A5A5', dark: '#8A8886', hc: '#2B56F2' }, nls.localize('notebook.notebookToolbarSelectBorder', "Notebook: Main toolbar select box border")); +export const notebookToolbarSelectBackground = registerColor('notebook.notebookToolbarSelectBackground', { light: '#FFFFFF', dark: '#1B1A19', hc: '#000000' }, nls.localize('notebook.notebookToolbarSelectBackground', "Notebook: Main toolbar select box background")); +export const notebookToolbarLines = registerColor('notebook.notebookToolbarLines', { light: '#D6D6D6', dark: '#323130', hc: '#2B56F2' }, nls.localize('notebook.notebookToolbarLines', "Notebook: Main toolbar bottom border and separator")); +export const dropdownArrow = registerColor('notebook.dropdownArrow', { light: '#A5A5A5', dark: '#FFFFFF', hc: '#FFFFFF' }, nls.localize('notebook.dropdownArrow', "Notebook: Main toolbar dropdown arrow")); +export const buttonMenuArrow = registerColor('notebook.buttonMenuArrow', { light: '#000000', dark: '#FFFFFF', hc: '#FFFFFF' }, nls.localize('notebook.buttonMenuArrow', "Notebook: Main toolbar custom buttonMenu dropdown arrow")); + export const toolbarBackground = registerColor('notebook.toolbarBackground', { light: '#F5F5F5', dark: '#252423', hc: '#000000' }, nls.localize('notebook.toolbarBackground', "Notebook: Markdown toolbar background")); -export const toolbarIcon = registerColor('notebook.toolbarIcon', { light: '#323130', dark: '#FFFFFe', hc: '#FFFFFe' }, nls.localize('notebook.toolbarIcon', "Notebook: Markdown toolbar icons")); +export const toolbarIcon = registerColor('notebook.toolbarIcon', { light: '#323130', dark: '#FFFFFF', hc: '#FFFFFF' }, nls.localize('notebook.toolbarIcon', "Notebook: Markdown toolbar icons")); export const toolbarBottomBorder = registerColor('notebook.toolbarBottomBorder', { light: '#D4D4D4', dark: '#323130', hc: '#E86E58' }, nls.localize('notebook.toolbarBottomBorder', "Notebook: Markdown toolbar bottom border")); // Notebook: All cells -export const cellBorder = registerColor('notebook.cellBorder', { light: '#0078D4', dark: '#0078D4', hc: '#E86E58' }, nls.localize('notebook.cellBorder', "Notebook: Active cell border")); +export const cellBorder = registerColor('notebook.cellBorder', { light: '#0078D4', dark: '#3AA0F3', hc: '#E86E58' }, nls.localize('notebook.cellBorder', "Notebook: Active cell border")); // Notebook: Markdown cell -export const markdownEditorBackground = registerColor('notebook.markdownEditorBackground', { light: '#FFFFFe', dark: '#1B1A19', hc: '#000000' }, nls.localize('notebook.markdownEditorBackground', "Notebook: Markdown editor background")); +export const markdownEditorBackground = registerColor('notebook.markdownEditorBackground', { light: '#FFFFFF', dark: '#1B1A19', hc: '#000000' }, nls.localize('notebook.markdownEditorBackground', "Notebook: Markdown editor background")); export const splitBorder = registerColor('notebook.splitBorder', { light: '#E6E6E6', dark: '#323130', hc: '#872412' }, nls.localize('notebook.splitBorder', "Notebook: Border between Markdown editor and preview")); // Notebook: Code cell export const codeEditorBackground = registerColor('notebook.codeEditorBackground', { light: '#F5F5F5', dark: '#333333', hc: '#000000' }, nls.localize('notebook.codeEditorBackground', "Notebook: Code editor background")); -export const codeEditorBackgroundActive = registerColor('notebook.codeEditorBackgroundActive', { light: '#FFFFFe', dark: null, hc: null }, nls.localize('notebook.codeEditorBackgroundActive', "Notebook: Code editor background of active cell")); -export const codeEditorLineNumber = registerColor('notebook.codeEditorLineNumber', { light: '#A19F9D', dark: '#A19F9D', hc: '#FFFFFe' }, nls.localize('notebook.codeEditorLineNumber', "Notebook: Code editor line numbers")); -export const codeEditorToolbarIcon = registerColor('notebook.codeEditorToolbarIcon', { light: '#999999', dark: '#A19F9D', hc: '#FFFFFe' }, nls.localize('notebook.codeEditorToolbarIcon', "Notebook: Code editor toolbar icons")); +export const codeEditorBackgroundActive = registerColor('notebook.codeEditorBackgroundActive', { light: '#FFFFFF', dark: null, hc: null }, nls.localize('notebook.codeEditorBackgroundActive', "Notebook: Code editor background of active cell")); +export const codeEditorLineNumber = registerColor('notebook.codeEditorLineNumber', { light: '#A19F9D', dark: '#A19F9D', hc: '#FFFFFF' }, nls.localize('notebook.codeEditorLineNumber', "Notebook: Code editor line numbers")); +export const codeEditorToolbarIcon = registerColor('notebook.codeEditorToolbarIcon', { light: '#999999', dark: '#A19F9D', hc: '#FFFFFF' }, nls.localize('notebook.codeEditorToolbarIcon', "Notebook: Code editor toolbar icons")); export const codeEditorToolbarBackground = registerColor('notebook.codeEditorToolbarBackground', { light: '#EEEEEE', dark: '#333333', hc: '#000000' }, nls.localize('notebook.codeEditorToolbarBackground', "Notebook: Code editor toolbar background")); export const codeEditorToolbarBorder = registerColor('notebook.codeEditorToolbarBorder', { light: '#C8C6C4', dark: '#333333', hc: '#000000' }, nls.localize('notebook.codeEditorToolbarBorder', "Notebook: Code editor toolbar right border")); diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.ts index ed9cf102ab..52e8c2633a 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component.ts @@ -21,6 +21,7 @@ export class MarkdownToolbarComponent { public buttonBold = localize('buttonBold', "Bold"); public buttonItalic = localize('buttonItalic', "Italic"); + public buttonUnderline = localize('buttonUnderline', "Underline"); public buttonHighlight = localize('buttonHighlight', "Highlight"); public buttonCode = localize('buttonCode', "Code"); public buttonLink = localize('buttonLink', "Link"); @@ -43,6 +44,7 @@ export class MarkdownToolbarComponent { private initActionBar() { let boldButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.boldText', '', 'bold', this.buttonBold, this.cellModel, MarkdownButtonType.BOLD); let italicButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.italicText', '', 'italic', this.buttonItalic, this.cellModel, MarkdownButtonType.ITALIC); + let underlineButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.underlineText', '', 'underline', this.buttonUnderline, this.cellModel, MarkdownButtonType.UNDERLINE); let highlightButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.highlightText', '', 'highlight', this.buttonHighlight, this.cellModel, MarkdownButtonType.HIGHLIGHT); let codeButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.codeText', '', 'code', this.buttonCode, this.cellModel, MarkdownButtonType.CODE); let linkButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.linkText', '', 'insert-link', this.buttonLink, this.cellModel, MarkdownButtonType.LINK); @@ -56,6 +58,7 @@ export class MarkdownToolbarComponent { this._actionBar.setContent([ { action: boldButton }, { action: italicButton }, + { action: underlineButton }, { action: highlightButton }, { action: codeButton }, { action: linkButton }, diff --git a/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts b/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts index ba371a20ae..c6b5998fca 100644 --- a/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts +++ b/src/sql/workbench/contrib/notebook/browser/markdownToolbarActions.ts @@ -119,6 +119,8 @@ export class MarkdownTextTransformer { return '**'; case MarkdownButtonType.ITALIC: return '_'; + case MarkdownButtonType.UNDERLINE: + return ''; case MarkdownButtonType.CODE: return '```\n'; case MarkdownButtonType.LINK: @@ -142,6 +144,8 @@ export class MarkdownTextTransformer { return '**'; case MarkdownButtonType.ITALIC: return '_'; + case MarkdownButtonType.UNDERLINE: + return ''; case MarkdownButtonType.CODE: return '\n```'; case MarkdownButtonType.LINK: @@ -370,6 +374,7 @@ export class MarkdownTextTransformer { export enum MarkdownButtonType { BOLD, ITALIC, + UNDERLINE, CODE, HIGHLIGHT, LINK, diff --git a/src/sql/workbench/contrib/notebook/browser/notebook.component.ts b/src/sql/workbench/contrib/notebook/browser/notebook.component.ts index 59b3090076..7997546a05 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebook.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebook.component.ts @@ -31,7 +31,8 @@ import * as notebookUtils from 'sql/workbench/services/notebook/browser/models/n import { Deferred } from 'sql/base/common/promise'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; -import { KernelsDropdown, AttachToDropdown, AddCellAction, TrustedAction, RunAllCellsAction, ClearAllOutputsAction, CollapseCellsAction } from 'sql/workbench/contrib/notebook/browser/notebookActions'; +import { AddCellAction, KernelsDropdown, AttachToDropdown, TrustedAction, RunAllCellsAction, ClearAllOutputsAction, CollapseCellsAction } from 'sql/workbench/contrib/notebook/browser/notebookActions'; +import { DropdownMenuActionViewItem } from 'sql/base/browser/ui/buttonMenu/buttonMenu'; import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes'; import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; @@ -56,6 +57,7 @@ import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/not import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export const NOTEBOOK_SELECTOR: string = 'notebook-component'; @@ -83,6 +85,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe private _scrollTop: number; private _navProvider: INavigationProvider; private navigationResult: nb.NavigationResult; + public previewFeaturesEnabled: boolean = false; constructor( @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef, @@ -103,11 +106,15 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe @Inject(ITextFileService) private textFileService: ITextFileService, @Inject(ILogService) private readonly logService: ILogService, @Inject(ICommandService) private commandService: ICommandService, - @Inject(IAdsTelemetryService) private adstelemetryService: IAdsTelemetryService + @Inject(IAdsTelemetryService) private adstelemetryService: IAdsTelemetryService, + @Inject(IConfigurationService) private _configurationService: IConfigurationService ) { super(); this.updateProfile(); this.isLoading = true; + this._register(this._configurationService.onDidChangeConfiguration(e => { + this.previewFeaturesEnabled = this._configurationService.getValue('workbench.enablePreviewFeatures'); + })); } private updateProfile(): void { @@ -395,44 +402,114 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe } protected initActionBar(): void { - let kernelContainer = document.createElement('div'); - let kernelDropdown = this.instantiationService.createInstance(KernelsDropdown, kernelContainer, this.contextViewService, this.modelReady); - kernelDropdown.render(kernelContainer); - attachSelectBoxStyler(kernelDropdown, this.themeService); + this.previewFeaturesEnabled = this._configurationService.getValue('workbench.enablePreviewFeatures'); - let attachToContainer = document.createElement('div'); - let attachToDropdown = new AttachToDropdown(attachToContainer, this.contextViewService, this.modelReady, - this.connectionManagementService, this.connectionDialogService, this.notificationService, this.capabilitiesService); - attachToDropdown.render(attachToContainer); - attachSelectBoxStyler(attachToDropdown, this.themeService); + if (this.previewFeaturesEnabled) { + let kernelContainer = document.createElement('li'); + let kernelDropdown = this.instantiationService.createInstance(KernelsDropdown, kernelContainer, this.contextViewService, this.modelReady); + kernelDropdown.render(kernelContainer); + attachSelectBoxStyler(kernelDropdown, this.themeService); - let addCodeCellButton = new AddCellAction('notebook.AddCodeCell', localize('code', "Code"), 'notebook-button icon-add'); - addCodeCellButton.cellType = CellTypes.Code; + let attachToContainer = document.createElement('li'); + let attachToDropdown = new AttachToDropdown(attachToContainer, this.contextViewService, this.modelReady, + this.connectionManagementService, this.connectionDialogService, this.notificationService, this.capabilitiesService); + attachToDropdown.render(attachToContainer); + attachSelectBoxStyler(attachToDropdown, this.themeService); - let addTextCellButton = new AddCellAction('notebook.AddTextCell', localize('text', "Text"), 'notebook-button icon-add'); - addTextCellButton.cellType = CellTypes.Markdown; + let spacerElement = document.createElement('li'); + spacerElement.style.marginLeft = 'auto'; - this._runAllCellsAction = this.instantiationService.createInstance(RunAllCellsAction, 'notebook.runAllCells', localize('runAll', "Run Cells"), 'notebook-button icon-run-cells'); - let clearResultsButton = new ClearAllOutputsAction('notebook.ClearAllOutputs', localize('clearResults', "Clear Results"), 'notebook-button icon-clear-results'); + let addCodeCellButton = new AddCellAction('notebook.AddCodeCell', localize('codePreview', "Code cell"), 'notebook-button masked-pseudo code'); + addCodeCellButton.cellType = CellTypes.Code; - this._trustedAction = this.instantiationService.createInstance(TrustedAction, 'notebook.Trusted'); - this._trustedAction.enabled = false; + let addTextCellButton = new AddCellAction('notebook.AddTextCell', localize('textPreview', "Markdown cell"), 'notebook-button masked-pseudo markdown'); + addTextCellButton.cellType = CellTypes.Markdown; - let collapseCellsAction = this.instantiationService.createInstance(CollapseCellsAction, 'notebook.collapseCells'); - let taskbar = this.toolbar.nativeElement; - this._actionBar = new Taskbar(taskbar, { actionViewItemProvider: action => this.actionItemProvider(action as Action) }); - this._actionBar.context = this; - this._actionBar.setContent([ - { action: addCodeCellButton }, - { action: addTextCellButton }, - { element: kernelContainer }, - { element: attachToContainer }, - { action: this._trustedAction }, - { action: this._runAllCellsAction }, - { action: clearResultsButton }, - { action: collapseCellsAction } - ]); + this._runAllCellsAction = this.instantiationService.createInstance(RunAllCellsAction, 'notebook.runAllCells', localize('runAllPreview', "Run all"), 'notebook-button masked-pseudo start-outline'); + + let collapseCellsAction = this.instantiationService.createInstance(CollapseCellsAction, 'notebook.collapseCells', true); + + let clearResultsButton = new ClearAllOutputsAction('notebook.ClearAllOutputs', true); + + this._trustedAction = this.instantiationService.createInstance(TrustedAction, 'notebook.Trusted', true); + this._trustedAction.enabled = false; + + let taskbar = this.toolbar.nativeElement; + this._actionBar = new Taskbar(taskbar, { actionViewItemProvider: action => this.actionItemProvider(action as Action) }); + this._actionBar.context = this; + taskbar.classList.add('in-preview'); + + let buttonDropdownContainer = DOM.$('li.action-item'); + buttonDropdownContainer.setAttribute('role', 'presentation'); + let dropdownMenuActionViewItem = new DropdownMenuActionViewItem( + addCodeCellButton, + [addCodeCellButton, addTextCellButton], + this.contextMenuService, + undefined, + this._actionBar.actionRunner, + undefined, + 'codicon notebook-button masked-pseudo masked-pseudo-after add-new dropdown-arrow', + localize('addCell', "Cell"), + undefined + ); + dropdownMenuActionViewItem.render(buttonDropdownContainer); + dropdownMenuActionViewItem.setActionContext(this); + + this._actionBar.setContent([ + { element: buttonDropdownContainer }, + { action: this._runAllCellsAction }, + { element: Taskbar.createTaskbarSeparator() }, + { element: attachToContainer }, + { element: kernelContainer }, + { element: spacerElement }, + { action: collapseCellsAction }, + { action: clearResultsButton }, + { action: this._trustedAction }, + ]); + } else { + let kernelContainer = document.createElement('div'); + let kernelDropdown = this.instantiationService.createInstance(KernelsDropdown, kernelContainer, this.contextViewService, this.modelReady); + kernelDropdown.render(kernelContainer); + attachSelectBoxStyler(kernelDropdown, this.themeService); + + let attachToContainer = document.createElement('div'); + let attachToDropdown = new AttachToDropdown(attachToContainer, this.contextViewService, this.modelReady, + this.connectionManagementService, this.connectionDialogService, this.notificationService, this.capabilitiesService); + attachToDropdown.render(attachToContainer); + attachSelectBoxStyler(attachToDropdown, this.themeService); + + let addCodeCellButton = new AddCellAction('notebook.AddCodeCell', localize('code', "Code"), 'notebook-button icon-add'); + addCodeCellButton.cellType = CellTypes.Code; + + let addTextCellButton = new AddCellAction('notebook.AddTextCell', localize('text', "Text"), 'notebook-button icon-add'); + addTextCellButton.cellType = CellTypes.Markdown; + + this._runAllCellsAction = this.instantiationService.createInstance(RunAllCellsAction, 'notebook.runAllCells', localize('runAll', "Run Cells"), 'notebook-button icon-run-cells'); + + let clearResultsButton = new ClearAllOutputsAction('notebook.ClearAllOutputs', false); + + this._trustedAction = this.instantiationService.createInstance(TrustedAction, 'notebook.Trusted', false); + this._trustedAction.enabled = false; + + let collapseCellsAction = this.instantiationService.createInstance(CollapseCellsAction, 'notebook.collapseCells', false); + + let taskbar = this.toolbar.nativeElement; + this._actionBar = new Taskbar(taskbar, { actionViewItemProvider: action => this.actionItemProvider(action as Action) }); + this._actionBar.context = this; + + this._actionBar.setContent([ + { action: addCodeCellButton }, + { action: addTextCellButton }, + { element: kernelContainer }, + { element: attachToContainer }, + { action: this._trustedAction }, + { action: this._runAllCellsAction }, + { action: clearResultsButton }, + { action: collapseCellsAction } + ]); + } + } protected initNavSection(): void { @@ -478,7 +555,12 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe // Check extensions to create ActionItem; otherwise, return undefined // This is similar behavior that exists in MenuItemActionItem if (action instanceof MenuItemAction) { - return new LabeledMenuItemActionItem(action, this.keybindingService, this.contextMenuService, this.notificationService, 'notebook-button'); + + if (action.item.id.includes('jupyter.cmd') && this.previewFeaturesEnabled) { + action.tooltip = action.label; + action.label = ''; + } + return new LabeledMenuItemActionItem(action, this.keybindingService, this.contextMenuService, this.notificationService, 'notebook-button fixed-width'); } return undefined; } diff --git a/src/sql/workbench/contrib/notebook/browser/notebook.css b/src/sql/workbench/contrib/notebook/browser/notebook.css index f5caf031b3..e55d22747d 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebook.css +++ b/src/sql/workbench/contrib/notebook/browser/notebook.css @@ -6,6 +6,10 @@ margin-top: 5px; } +.notebookEditor .taskbarSeparator { + margin: 0 16px 0 -8px; +} + .notebookEditor .editor-toolbar { border-bottom-style: solid; border-width: 0px 0px 1px 0px; @@ -44,6 +48,52 @@ font-size: 13px; height: 21px; } +.notebookEditor .in-preview .actions-container .action-item .notebook-button { + display: flex; + background-size: 16px; +} + +.notebookEditor + .in-preview + .actions-container + .action-item + .notebook-button.masked-pseudo { + padding-left: 30px; +} +.notebookEditor + .in-preview + .actions-container + .action-item + .notebook-button.masked-icon { + margin-right: 0; + padding-left: 18px; + width: 16px; +} +.notebookEditor .in-preview .actions-container .action-item:last-child { + margin-right: 8px; +} +.notebookEditor + .in-preview + .actions-container + .action-item:last-child + .notebook-button { + margin-right: 0; +} +.notebookEditor + .in-preview + .actions-container + .action-item:last-child + .notebook-button.fixed-width { + margin-left: 8px; + margin-right: -28px; +} +.notebookEditor + .in-preview + .actions-container + .action-item + .notebook-button.fixed-width { + width: 34px; +} .notebookEditor .labelOnLeftContainer { min-width: 100px; @@ -54,68 +104,89 @@ vertical-align: bottom; } -.notebookEditor .notebook-button.icon-add { +.notebookEditor .in-preview .labelOnLeftContainer { + margin-right: 14px; +} + +/* non-preview */ +.notebookEditor :not(.in-preview) .notebook-button.icon-add { background-image: url("./media/light/add.svg"); } -.vs-dark .notebookEditor .notebook-button.icon-add, -.hc-black .notebookEditor .notebook-button.icon-add { +.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-add, +.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-add { background-image: url("./media/dark/add_inverse.svg"); } -.notebookEditor .notebook-button.icon-run-cells { +.notebookEditor :not(.in-preview) .notebook-button.icon-run-cells { background-image: url("./media/light/run_cells.svg"); } -.vs-dark .notebookEditor .notebook-button.icon-run-cells, -.hc-black .notebookEditor .notebook-button.icon-run-cells { +.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-run-cells, +.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-run-cells { background-image: url("./media/dark/run_cells_inverse.svg"); } -.notebookEditor .notebook-button.icon-trusted { +.notebookEditor :not(.in-preview) .notebook-button.icon-trusted { background-image: url("./media/light/trusted.svg"); } -.vs-dark .notebookEditor .notebook-button.icon-trusted, -.hc-black .notebookEditor .notebook-button.icon-trusted { +.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-trusted, +.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-trusted { background-image: url("./media/dark/trusted_inverse.svg"); } - -.notebookEditor .notebook-button.icon-notTrusted { +.notebookEditor :not(.in-preview) .notebook-button.icon-notTrusted { background-image: url("./media/light/nottrusted.svg"); } -.vs-dark .notebookEditor .notebook-button.icon-notTrusted, -.hc-black .notebookEditor .notebook-button.icon-notTrusted { +.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-notTrusted, +.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-notTrusted { background-image: url("./media/dark/nottrusted_inverse.svg"); } -.notebookEditor .notebook-button.icon-show-cells { +.notebookEditor :not(.in-preview) .notebook-button.icon-show-cells { background-image: url("./media/light/show_code.svg"); } -.vs-dark .notebookEditor .notebook-button.icon-show-cells, -.hc-black .notebookEditor .notebook-button.icon-show-cells { +.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-show-cells, +.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-show-cells { background-image: url("./media/dark/show_code_inverse.svg"); } -.notebookEditor .notebook-button.icon-hide-cells { +.notebookEditor :not(.in-preview) .notebook-button.icon-hide-cells { background-image: url("./media/light/hide_code.svg"); } -.vs-dark .notebookEditor .notebook-button.icon-hide-cells, -.hc-black .notebookEditor .notebook-button.icon-hide-cells { +.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-hide-cells, +.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-hide-cells { background-image: url("./media/dark/hide_code_inverse.svg"); } -.notebookEditor .notebook-button.icon-clear-results { +.notebookEditor :not(.in-preview) .notebook-button.icon-clear-results { background-image: url("./media/light/clear_results.svg"); } -.vs-dark .notebookEditor .notebook-button.icon-clear-results, -.hc-black .notebookEditor .notebook-button.icon-clear-results { +.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-clear-results, +.hc-black + .notebookEditor + :not(.in-preview) + .notebook-button.icon-clear-results { background-image: url("./media/dark/clear_results_inverse.svg"); } +/* non-preview */ + +.notebookEditor .in-preview .carbon-taskbar.monaco-toolbar .monaco-select-box { + font-size: inherit; + border-radius: 0; + padding: 3px 22px 3px 6px; +} +.notebookEditor .in-preview .carbon-taskbar .action-item { + margin-right: 0; +} +.notebookEditor .in-preview .labelOnLeftContainer { + display: flex; + align-items: center; +} .moreActions .action-label.codicon.toggle-more { height: 20px; @@ -123,7 +194,7 @@ } .moreActions.actionhidden { - visibility: hidden + visibility: hidden; } .moreActions .monaco-action-bar { margin-left: -12px; @@ -144,12 +215,13 @@ } .monaco-workbench .notebook-action.new-notebook { - background: url('./media/light/new_notebook.svg') center center no-repeat; + background: url("./media/light/new_notebook.svg") center center no-repeat; } .vs-dark .monaco-workbench .notebook-action.new-notebook, .hc-black .monaco-workbench .notebook-action.new-notebook { - background: url('./media/dark/new_notebook_inverse.svg') center center no-repeat; + background: url("./media/dark/new_notebook_inverse.svg") center center + no-repeat; } .notebookEditor .book-nav { @@ -209,7 +281,7 @@ } .notebookEditor .hoverButton:active { - transform:scale(1.05); + transform: scale(1.05); } .notebookEditor .hoverButton .addCodeIcon, diff --git a/src/sql/workbench/contrib/notebook/browser/notebookActions.ts b/src/sql/workbench/contrib/notebook/browser/notebookActions.ts index 4777b31af9..6c2e12fa6a 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookActions.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookActions.ts @@ -31,8 +31,8 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur const msgLoading = localize('loading', "Loading kernels..."); const msgChanging = localize('changing', "Changing kernel..."); -const kernelLabel: string = localize('Kernel', "Kernel: "); -const attachToLabel: string = localize('AttachTo', "Attach To: "); +const attachToLabel: string = localize('AttachTo', "Attach to "); +const kernelLabel: string = localize('Kernel', "Kernel "); const msgLoadingContexts = localize('loadingContexts', "Loading contexts..."); const msgChangeConnection = localize('changeConnection', "Change Connection"); const msgSelectConnection = localize('selectConnection', "Select Connection"); @@ -64,13 +64,50 @@ export class AddCellAction extends Action { } } -// Action to clear outputs of all code cells. -export class ClearAllOutputsAction extends Action { - constructor( - id: string, label: string, cssClass: string - ) { - super(id, label, cssClass); +export interface ITooltipState { + label: string; + baseClass: string; + iconClass: string; + maskedIconClass: string; + shouldToggleTooltip?: boolean; +} +export abstract class TooltipFromLabelAction extends Action { + + constructor(id: string, protected state: ITooltipState) { + super(id, ''); + this.updateLabelAndIcon(); } + + private updateLabelAndIcon() { + if (this.state.shouldToggleTooltip) { + this.tooltip = this.state.label; + } else { + this.label = this.state.label; + } + let classes = this.state.baseClass ? `${this.state.baseClass} ${this.state.iconClass} ` : ''; + if (this.state.shouldToggleTooltip) { + classes += this.state.maskedIconClass; + } + this.class = classes; + } +} +// Action to clear outputs of all code cells. +export class ClearAllOutputsAction extends TooltipFromLabelAction { + private static readonly label = localize('clearResults', "Clear Results"); + private static readonly baseClass = 'notebook-button'; + private static readonly iconClass = 'icon-clear-results'; + private static readonly maskedIconClass = 'masked-icon'; + + constructor(id: string, toggleTooltip: boolean) { + super(id, { + label: ClearAllOutputsAction.label, + baseClass: ClearAllOutputsAction.baseClass, + iconClass: ClearAllOutputsAction.iconClass, + maskedIconClass: ClearAllOutputsAction.maskedIconClass, + shouldToggleTooltip: toggleTooltip + }); + } + public run(context: INotebookEditor): Promise { return context.clearAllOutputs(); } @@ -83,6 +120,7 @@ export interface IToggleableState { toggleOnLabel: string; toggleOffLabel: string; toggleOffClass: string; + maskedIconClass?: string; isOn: boolean; } @@ -99,7 +137,16 @@ export abstract class ToggleableAction extends Action { } else { this.label = this.state.isOn ? this.state.toggleOnLabel : this.state.toggleOffLabel; } - let classes = this.state.baseClass ? `${this.state.baseClass} ` : ''; + + let classes: string = ''; + + if (this.state.shouldToggleTooltip && this.state.maskedIconClass) { + //mask + classes = this.state.baseClass ? `${this.state.baseClass} ${this.state.maskedIconClass} ` : ''; + } else { + //no mask + classes = this.state.baseClass ? `${this.state.baseClass} ` : ''; + } classes += this.state.isOn ? this.state.toggleOnClass : this.state.toggleOffClass; this.class = classes; } @@ -115,20 +162,23 @@ export class TrustedAction extends ToggleableAction { private static readonly trustedLabel = localize('trustLabel', "Trusted"); private static readonly notTrustedLabel = localize('untrustLabel', "Not Trusted"); private static readonly baseClass = 'notebook-button'; + private static readonly previewTrustedCssClass = 'icon-shield'; private static readonly trustedCssClass = 'icon-trusted'; + private static readonly previewNotTrustedCssClass = 'icon-shield-x'; private static readonly notTrustedCssClass = 'icon-notTrusted'; - - // Properties + private static readonly maskedIconClass = 'masked-icon'; constructor( - id: string + id: string, toggleTooltip: boolean ) { super(id, { baseClass: TrustedAction.baseClass, toggleOnLabel: TrustedAction.trustedLabel, - toggleOnClass: TrustedAction.trustedCssClass, + toggleOnClass: toggleTooltip === true ? TrustedAction.previewTrustedCssClass : TrustedAction.trustedCssClass, toggleOffLabel: TrustedAction.notTrustedLabel, - toggleOffClass: TrustedAction.notTrustedCssClass, + toggleOffClass: toggleTooltip === true ? TrustedAction.previewNotTrustedCssClass : TrustedAction.notTrustedCssClass, + maskedIconClass: TrustedAction.maskedIconClass, + shouldToggleTooltip: toggleTooltip, isOn: false }); } @@ -177,16 +227,21 @@ export class CollapseCellsAction extends ToggleableAction { private static readonly collapseCells = localize('collapseAllCells', "Collapse Cells"); private static readonly expandCells = localize('expandAllCells', "Expand Cells"); private static readonly baseClass = 'notebook-button'; + private static readonly previewCollapseCssClass = 'icon-collapse-cells'; private static readonly collapseCssClass = 'icon-hide-cells'; + private static readonly previewExpandCssClass = 'icon-expand-cells'; private static readonly expandCssClass = 'icon-show-cells'; + private static readonly maskedIconClass = 'masked-icon'; - constructor(id: string) { + constructor(id: string, toggleTooltip: boolean) { super(id, { baseClass: CollapseCellsAction.baseClass, toggleOnLabel: CollapseCellsAction.expandCells, - toggleOnClass: CollapseCellsAction.expandCssClass, + toggleOnClass: toggleTooltip === true ? CollapseCellsAction.previewExpandCssClass : CollapseCellsAction.expandCssClass, toggleOffLabel: CollapseCellsAction.collapseCells, - toggleOffClass: CollapseCellsAction.collapseCssClass, + toggleOffClass: toggleTooltip === true ? CollapseCellsAction.previewCollapseCssClass : CollapseCellsAction.collapseCssClass, + maskedIconClass: CollapseCellsAction.maskedIconClass, + shouldToggleTooltip: toggleTooltip, isOn: false }); } diff --git a/src/sql/workbench/contrib/notebook/browser/notebookStyles.ts b/src/sql/workbench/contrib/notebook/browser/notebookStyles.ts index 347f613008..38f32f6bf9 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookStyles.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookStyles.ts @@ -8,7 +8,7 @@ import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/ import { SIDE_BAR_BACKGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND } from 'vs/workbench/common/theme'; import { activeContrastBorder, contrastBorder, buttonBackground, textLinkForeground, textLinkActiveForeground, textPreformatForeground, textBlockQuoteBackground, textBlockQuoteBorder, buttonForeground, editorBackground, lighten } from 'vs/platform/theme/common/colorRegistry'; import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry'; -import { cellBorder, markdownEditorBackground, splitBorder, codeEditorBackground, codeEditorBackgroundActive, codeEditorLineNumber, codeEditorToolbarIcon, codeEditorToolbarBackground, codeEditorToolbarBorder, toolbarBackground, toolbarIcon, toolbarBottomBorder } from 'sql/platform/theme/common/colorRegistry'; +import { cellBorder, notebookToolbarIcon, notebookToolbarLines, buttonMenuArrow, dropdownArrow, markdownEditorBackground, splitBorder, codeEditorBackground, codeEditorBackgroundActive, codeEditorLineNumber, codeEditorToolbarIcon, codeEditorToolbarBackground, codeEditorToolbarBorder, toolbarBackground, toolbarIcon, toolbarBottomBorder } from 'sql/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BareResultsGridInfo, getBareResultsGridInfoStyles } from 'sql/workbench/contrib/query/browser/queryResultsEditor'; @@ -207,6 +207,25 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean, conf ${getBareResultsGridInfoStyles(rawOptions)} }`); + //Notebook toolbar masked icons + const notebookToolbarIconColor = theme.getColor(notebookToolbarIcon); + if (notebookToolbarIconColor) { + collector.addRule(`.notebookEditor .notebook-button.masked-icon { background-color: ${notebookToolbarIconColor};}`); + collector.addRule(`.notebookEditor .notebook-button.masked-pseudo:before { background-color: ${notebookToolbarIconColor};}`); + } + const notebookToolbarLinesColor = theme.getColor(notebookToolbarLines); + if (notebookToolbarLinesColor) { + collector.addRule(`.notebookEditor .editor-toolbar.actionbar-container { border-bottom-color: ${notebookToolbarLinesColor}!important;}`); + collector.addRule(`.notebookEditor .taskbarSeparator { background-color: ${notebookToolbarLinesColor};}`); + } + const dropdownArrowColor = theme.getColor(dropdownArrow); + if (dropdownArrowColor) { + collector.addRule(`.monaco-workbench .notebookEditor .select-container:after { color: ${dropdownArrowColor};}`); + } + const buttonMenuArrowColor = theme.getColor(buttonMenuArrow); + if (buttonMenuArrowColor) { + collector.addRule(`.notebookEditor .notebook-button.masked-pseudo-after:after { background-color: ${buttonMenuArrowColor};}`); + } // Cell border const cellBorderColor = theme.getColor(cellBorder); diff --git a/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts b/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts index 38bf4362bc..7598ea75d7 100644 --- a/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts +++ b/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts @@ -40,7 +40,7 @@ suite('Notebook Actions', function (): void { }); test('Clear All Outputs Action', async function (): Promise { - let action = new ClearAllOutputsAction('TestId', 'TestLabel', 'TestClass'); + let action = new ClearAllOutputsAction('TestId', true); // Normal use case let mockNotebookComponent = TypeMoq.Mock.ofType(NotebookComponentStub); @@ -63,7 +63,7 @@ suite('Notebook Actions', function (): void { let mockNotification = TypeMoq.Mock.ofType(TestNotificationService); mockNotification.setup(n => n.notify(TypeMoq.It.isAny())); - let action = new TrustedAction('TestId'); + let action = new TrustedAction('TestId', true); assert.strictEqual(action.trusted, false, 'Should not be trusted by default'); // Normal use case @@ -105,7 +105,7 @@ suite('Notebook Actions', function (): void { }); test('Collapse Cells Action', async function (): Promise { - let action = new CollapseCellsAction('TestId'); + let action = new CollapseCellsAction('TestId', true); assert.strictEqual(action.isCollapsed, false, 'Should not be collapsed by default'); let context = {