diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 1c8e92c266ec745e54d1d24c2fd1db125e80bb3e..340c70a9f3ce9be3352dd896a075a8080467d693 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -162,6 +162,9 @@ export const listHoverOutline = registerColor('listHoverOutline', { dark: null, export const pickerGroupForeground = registerColor('pickerGroupForeground', { dark: Color.fromHex('#0097FB').transparent(0.6), light: Color.fromHex('#007ACC').transparent(0.6), hc: Color.white }, nls.localize('pickerGroupForeground', "Quick picker color for grouping labels.")); export const pickerGroupBorder = registerColor('pickerGroupBorder', { dark: '#3F3F46', light: '#CCCEDB', hc: Color.white }, nls.localize('pickerGroupBorder', "Quick picker color for grouping borders.")); +export const buttonBackground = registerColor('buttonBackground', { dark: '#0E639C', light: '#007ACC', hc: null }, nls.localize('buttonBackground', "Button background color.")); +export const buttonHoverBackground = registerColor('buttonHoverBackground', { dark: '#007ACC', light: '#006BB3', hc: null }, nls.localize('buttonHoverBackground', "Button background color when hovering.")); + /** * Editor background color. * Because of bug https://monacotools.visualstudio.com/DefaultCollection/Monaco/_workitems/edit/13254 diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index f5d1b64b22e71600e30a0425e2fd7dc511f0146f..8302780733aed41c9e6df72582f8a92c5b8e677e 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -250,6 +250,38 @@ export const TITLE_BAR_INACTIVE_BACKGROUND = registerColor('titleBarInactiveBack hc: null }, nls.localize('titleBarInactiveBackground', "Title bar background when the window is inactive. Note that this color is currently only supported on macOS.")); +// < --- Notifications --- > + +export const NOTIFICATIONS_FOREGROUND = registerColor('notificationsForeground', { + dark: '#EEEEEE', + light: '#EEEEEE', + hc: '#FFFFFF' +}, nls.localize('notificationsForeground', "Notifications foreground color. Notifications slide in from the top of the window.")); + +export const NOTIFICATIONS_BACKGROUND = registerColor('notificationsBackground', { + dark: '#333333', + light: '#2C2C2C', + hc: '#000000' +}, nls.localize('notificationsBackground', "Notifications background color. Notifications slide in from the top of the window.")); + +export const NOTIFICATIONS_ERROR_BACKGROUND = registerColor('notificationsErrorBackground', { + dark: '#BE1100', + light: '#BE1100', + hc: null +}, nls.localize('notificationsErrorBackground', "Notifications error background color. Notifications slide in from the top of the window.")); + +export const NOTIFICATIONS_WARNING_BACKGROUND = registerColor('notificationsWarningBackground', { + dark: '#B89500', + light: '#B89500', + hc: null +}, nls.localize('notificationsWarningBackground', "Notifications background color. Notifications slide in from the top of the window.")); + +export const NOTIFICATIONS_INFO_BACKGROUND = registerColor('notificationsInfoBackground', { + dark: '#007ACC', + light: '#007ACC', + hc: null +}, nls.localize('notificationsInfoBackground', "Notifications background color. Notifications slide in from the top of the window.")); + /** * Base class for all themable workbench components. */ diff --git a/src/vs/workbench/services/message/browser/media/messageList.css b/src/vs/workbench/services/message/browser/media/messageList.css index 617ce39b8db5e94040f9dbade26ee3466c50ed39..2d49423e053d25663b97e25c01ce61f19a659df7 100644 --- a/src/vs/workbench/services/message/browser/media/messageList.css +++ b/src/vs/workbench/services/message/browser/media/messageList.css @@ -111,20 +111,6 @@ /* TODO@theme */ -.vs .global-message-list, -.vs-dark .global-message-list, -.hc-black .global-message-list { - color: #eee; -} - -.vs .global-message-list { - background-color: #2C2C2C; -} - -.vs-dark .global-message-list { - background-color: #333; -} - .vs .global-message-list { box-shadow: 0 5px 8px #A8A8A8; } @@ -133,60 +119,7 @@ box-shadow: 0 5px 8px #000; } -.hc-black .global-message-list { - background-color: #000; - outline-color: #6FC3DF; -} - -.hc-black .global-message-list li.message-list-entry .message-left-side.severity { - border-color: #6FC3DF; -} - -.hc-black .global-message-list li.message-list-entry .actions-container .message-action .action-button { - border-color: #6FC3DF; -} - .vs .global-message-list li.message-list-entry .actions-container .message-action .action-button:focus, .vs-dark .global-message-list li.message-list-entry .actions-container .message-action .action-button:focus { outline-color: rgba(255, 255, 255, .5); /* buttons have a blue color, so focus indication needs to be different */ -} - -.vs .global-message-list li.message-list-entry .message-left-side.severity.app-error { - background-color: #BE1100; -} - -.vs-dark .global-message-list li.message-list-entry .message-left-side.severity.app-error { - background-color: #BE1100; -} - -.vs .global-message-list li.message-list-entry .message-left-side.severity.app-warning { - background-color: #B89500; -} - -.vs-dark .global-message-list li.message-list-entry .message-left-side.severity.app-warning { - background-color: #B89500; -} - -.vs .global-message-list li.message-list-entry .message-left-side.severity.app-info { - background-color: #007ACC; -} - -.vs-dark .global-message-list li.message-list-entry .message-left-side.severity.app-info { - background-color: #007ACC; -} - -.vs .global-message-list li.message-list-entry .actions-container .message-action { - background-color: #1382CE; -} - -.vs .global-message-list li.message-list-entry .actions-container .message-action:hover { - background-color: #006bb3; -} - -.vs-dark .global-message-list li.message-list-entry .actions-container .message-action { - background-color: #0E639C; -} - -.vs-dark .global-message-list li.message-list-entry .actions-container .message-action:hover { - background-color: #007ACC; } \ No newline at end of file diff --git a/src/vs/workbench/services/message/browser/messageList.ts b/src/vs/workbench/services/message/browser/messageList.ts index d782b0998fa74d1e12a71a582a675c5df9ef2cdd..f0c30d94c4a3427fa3ae2157256d2b512e058d68 100644 --- a/src/vs/workbench/services/message/browser/messageList.ts +++ b/src/vs/workbench/services/message/browser/messageList.ts @@ -19,6 +19,12 @@ import { Action } from 'vs/base/common/actions'; import htmlRenderer = require('vs/base/browser/htmlContentRenderer'); import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; +import { NOTIFICATIONS_FOREGROUND, NOTIFICATIONS_BACKGROUND, NOTIFICATIONS_INFO_BACKGROUND, NOTIFICATIONS_ERROR_BACKGROUND, NOTIFICATIONS_WARNING_BACKGROUND } from "vs/workbench/common/theme"; +import { ITelemetryService } from "vs/platform/telemetry/common/telemetry"; +import { registerThemingParticipant } from "vs/platform/theme/common/themeService"; +import { highContrastBorder, buttonBackground, buttonHoverBackground } from "vs/platform/theme/common/colorRegistry"; +import { IDisposable, dispose } from "vs/base/common/lifecycle"; +import { Color } from "vs/base/common/color"; export enum Severity { Info, @@ -47,32 +53,42 @@ export class IMessageListOptions { maxMessageLength: number; } -export interface IUsageLogger { - publicLog(eventName: string, data?: any): void; -} +const DEFAULT_MESSAGE_LIST_OPTIONS = { + purgeInterval: 10000, + maxMessages: 5, + maxMessageLength: 500 +}; export class MessageList { - - private static DEFAULT_MESSAGE_PURGER_INTERVAL = 10000; - private static DEFAULT_MAX_MESSAGES = 5; - private static DEFAULT_MAX_MESSAGE_LENGTH = 500; - private messages: IMessageEntry[]; private messageListPurger: TPromise; private messageListContainer: Builder; private container: HTMLElement; private options: IMessageListOptions; - private usageLogger: IUsageLogger; private _onMessagesShowing: Emitter; private _onMessagesCleared: Emitter; - constructor(container: HTMLElement, usageLogger?: IUsageLogger, options: IMessageListOptions = { purgeInterval: MessageList.DEFAULT_MESSAGE_PURGER_INTERVAL, maxMessages: MessageList.DEFAULT_MAX_MESSAGES, maxMessageLength: MessageList.DEFAULT_MAX_MESSAGE_LENGTH }) { + private toDispose: IDisposable[]; + + private background = Color.fromHex('#333333'); + private foreground = Color.fromHex('#EEEEEE'); + private outlineBorder: Color; + private buttonBackground = Color.fromHex('#0E639C'); + private infoBackground = Color.fromHex('#007ACC'); + private warningBackground = Color.fromHex('#B89500'); + private errorBackground = Color.fromHex('#BE1100'); + + constructor( + container: HTMLElement, + private telemetryService: ITelemetryService, + options: IMessageListOptions = DEFAULT_MESSAGE_LIST_OPTIONS + ) { + this.toDispose = []; this.messages = []; this.messageListPurger = null; this.container = container; - this.usageLogger = usageLogger; this.options = options; this._onMessagesShowing = new Emitter(); @@ -82,8 +98,21 @@ export class MessageList { } private registerListeners(): void { - browser.onDidChangeFullscreen(() => this.positionMessageList()); - browser.onDidChangeZoomLevel(() => this.positionMessageList()); + this.toDispose.push(browser.onDidChangeFullscreen(() => this.positionMessageList())); + this.toDispose.push(browser.onDidChangeZoomLevel(() => this.positionMessageList())); + this.toDispose.push(registerThemingParticipant((theme, collector) => { + this.background = theme.getColor(NOTIFICATIONS_BACKGROUND); + this.foreground = theme.getColor(NOTIFICATIONS_FOREGROUND); + this.outlineBorder = theme.getColor(highContrastBorder); + this.buttonBackground = theme.getColor(buttonBackground); + this.infoBackground = theme.getColor(NOTIFICATIONS_INFO_BACKGROUND); + this.warningBackground = theme.getColor(NOTIFICATIONS_WARNING_BACKGROUND); + this.errorBackground = theme.getColor(NOTIFICATIONS_ERROR_BACKGROUND); + + collector.addRule(`.global-message-list li.message-list-entry .actions-container .message-action .action-button:hover { background-color: ${theme.getColor(buttonHoverBackground)} !important; }`); + + this.updateStyles(); + })); } public get onMessagesShowing(): Event { @@ -94,6 +123,14 @@ export class MessageList { return this._onMessagesCleared.event; } + public updateStyles(): void { + if (this.messageListContainer) { + this.messageListContainer.style('background-color', this.background ? this.background.toString() : null); + this.messageListContainer.style('color', this.foreground ? this.foreground.toString() : null); + this.messageListContainer.style('outline-color', this.outlineBorder ? this.outlineBorder.toString() : null); + } + } + public showMessage(severity: Severity, message: string, onHide?: () => void): () => void; public showMessage(severity: Severity, message: Error, onHide?: () => void): () => void; public showMessage(severity: Severity, message: string[], onHide?: () => void): () => void; @@ -206,6 +243,9 @@ export class MessageList { }, 50 /* Need this delay to reliably get the animation on some browsers */); } }); + + // Styles + this.updateStyles(); } private positionMessageList(animate?: boolean): void { @@ -232,30 +272,32 @@ export class MessageList { for (let i = 0; i < messageActions.length; i++) { const action = messageActions[i]; actionContainer.div({ class: 'message-action' }, div => { - div.a({ class: 'action-button', tabindex: '0', role: 'button' }).text(action.label).on([DOM.EventType.CLICK, DOM.EventType.KEY_DOWN], e => { - if (e instanceof KeyboardEvent) { - const event = new StandardKeyboardEvent(e); - if (!event.equals(KeyCode.Enter) && !event.equals(KeyCode.Space)) { - return; // Only handle Enter/Escape for keyboard access + div.a({ class: 'action-button', tabindex: '0', role: 'button' }) + .style('border-color', this.outlineBorder ? this.outlineBorder.toString() : null) + .style('background-color', this.buttonBackground ? this.buttonBackground.toString() : null) + .text(action.label) + .on([DOM.EventType.CLICK, DOM.EventType.KEY_DOWN], e => { + if (e instanceof KeyboardEvent) { + const event = new StandardKeyboardEvent(e); + if (!event.equals(KeyCode.Enter) && !event.equals(KeyCode.Space)) { + return; // Only handle Enter/Escape for keyboard access + } } - } - DOM.EventHelper.stop(e, true); + DOM.EventHelper.stop(e, true); - if (this.usageLogger) { - this.usageLogger.publicLog('workbenchActionExecuted', { id: action.id, from: 'message' }); - } + this.telemetryService.publicLog('workbenchActionExecuted', { id: action.id, from: 'message' }); - (action.run() || TPromise.as(null)) - .then(null, error => this.showMessage(Severity.Error, error)) - .done(r => { - if (typeof r === 'boolean' && r === false) { - return; - } + (action.run() || TPromise.as(null)) + .then(null, error => this.showMessage(Severity.Error, error)) + .done(r => { + if (typeof r === 'boolean' && r === false) { + return; + } - this.hideMessage(message.text); // hide all matching the text since there may be duplicates - }); - }); + this.hideMessage(message.text); // hide all matching the text since there may be duplicates + }); + }); }); } }); @@ -268,7 +310,11 @@ export class MessageList { // Severity indicator const sev = message.severity; const label = (sev === Severity.Error) ? nls.localize('error', "Error") : (sev === Severity.Warning) ? nls.localize('warning', "Warn") : nls.localize('info', "Info"); - $().span({ class: 'message-left-side severity ' + ((sev === Severity.Error) ? 'app-error' : (sev === Severity.Warning) ? 'app-warning' : 'app-info'), text: label }).appendTo(div); + const color = (sev === Severity.Error) ? this.errorBackground : (sev === Severity.Warning) ? this.warningBackground : this.infoBackground; + const sevLabel = $().span({ class: `message-left-side severity ${sev === Severity.Error ? 'app-error' : sev === Severity.Warning ? 'app-warning' : 'app-info'}`, text: label }); + sevLabel.style('border-color', this.outlineBorder ? this.outlineBorder.toString() : null); + sevLabel.style('background-color', color ? color.toString() : null); + sevLabel.appendTo(div); // Error message const messageContentElement = htmlRenderer.renderHtml({ @@ -413,4 +459,8 @@ export class MessageList { } }); } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } } \ No newline at end of file diff --git a/src/vs/workbench/services/message/browser/messageService.ts b/src/vs/workbench/services/message/browser/messageService.ts index 24ba1ec6f711d9064d100ea4d47ad00d56e56aa5..f0f6af73d98b39b12071d6b83d9cae9a084b284a 100644 --- a/src/vs/workbench/services/message/browser/messageService.ts +++ b/src/vs/workbench/services/message/browser/messageService.ts @@ -8,7 +8,7 @@ import errors = require('vs/base/common/errors'); import { toErrorMessage } from 'vs/base/common/errorMessage'; import types = require('vs/base/common/types'); import { MessageList, Severity as BaseSeverity } from 'vs/workbench/services/message/browser/messageList'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IMessageService, IMessageWithAction, IConfirmation, Severity } from 'vs/platform/message/common/message'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import Event from 'vs/base/common/event'; @@ -25,7 +25,7 @@ export class WorkbenchMessageService implements IMessageService { public _serviceBrand: any; private handler: MessageList; - private disposeables: IDisposable[]; + private toDispose: IDisposable[]; private canShowMessages: boolean; private messageBuffer: IBufferedMessage[]; @@ -35,10 +35,9 @@ export class WorkbenchMessageService implements IMessageService { telemetryService: ITelemetryService ) { this.handler = new MessageList(container, telemetryService); - this.messageBuffer = []; this.canShowMessages = true; - this.disposeables = []; + this.toDispose = [this.handler]; } public get onMessagesShowing(): Event { @@ -146,8 +145,6 @@ export class WorkbenchMessageService implements IMessageService { } public dispose(): void { - while (this.disposeables.length) { - this.disposeables.pop().dispose(); - } + this.toDispose = dispose(this.toDispose); } } \ No newline at end of file