提交 6ad67e2a 编写于 作者: B Benjamin Pasero

ui debt - align status bar flyovers closer to each other

上级 43ead816
......@@ -12,7 +12,7 @@ import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch';
import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions';
import { BaseActionItem, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { IContextViewProvider, IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { IMenuOptions } from 'vs/base/browser/ui/menu/menu';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { EventHelper, EventType } from 'vs/base/browser/dom';
......@@ -33,6 +33,7 @@ export class BaseDropdown extends ActionRunner {
private $boxContainer: Builder;
private $label: Builder;
private $contents: Builder;
private visible: boolean;
constructor(container: HTMLElement, options: IBaseDropdownOptions) {
super();
......@@ -58,7 +59,11 @@ export class BaseDropdown extends ActionRunner {
return; // prevent multiple clicks to open multiple context menus (https://github.com/Microsoft/vscode/issues/41363)
}
this.show();
if (this.visible) {
this.hide();
} else {
this.show();
}
}).appendTo(this.$el);
let cleanupFn = labelRenderer(this.$label.getHTMLElement());
......@@ -87,11 +92,11 @@ export class BaseDropdown extends ActionRunner {
}
public show(): void {
// noop
this.visible = true;
}
public hide(): void {
// noop
this.visible = false;
}
protected onEvent(e: Event, activeElement: HTMLElement): void {
......@@ -135,10 +140,12 @@ export class Dropdown extends BaseDropdown {
}
public show(): void {
super.show();
this.element.addClass('active');
this.contextViewProvider.showContextView({
getAnchor: () => this.element.getHTMLElement(),
getAnchor: () => this.getAnchor(),
render: (container) => {
return this.renderContents(container);
......@@ -148,13 +155,21 @@ export class Dropdown extends BaseDropdown {
this.onEvent(e, activeElement);
},
onHide: () => {
this.element.removeClass('active');
}
onHide: () => this.onHide()
});
}
protected getAnchor(): HTMLElement | IAnchor {
return this.element.getHTMLElement();
}
protected onHide(): void {
this.element.removeClass('active');
}
public hide(): void {
super.hide();
if (this.contextViewProvider) {
this.contextViewProvider.hideContextView();
}
......@@ -217,6 +232,8 @@ export class DropdownMenu extends BaseDropdown {
}
public show(): void {
super.show();
this.element.addClass('active');
this._contextMenuProvider.showContextMenu({
......@@ -232,7 +249,7 @@ export class DropdownMenu extends BaseDropdown {
}
public hide(): void {
// noop
super.hide();
}
}
......
......@@ -8,7 +8,7 @@
import 'vs/css!./media/editorstatus';
import nls = require('vs/nls');
import { TPromise } from 'vs/base/common/winjs.base';
import { $, append, runAtThisOrScheduleAtNextAnimationFrame, addDisposableListener } from 'vs/base/browser/dom';
import { $, append, runAtThisOrScheduleAtNextAnimationFrame, addDisposableListener, getDomNodePagePosition } from 'vs/base/browser/dom';
import strings = require('vs/base/common/strings');
import paths = require('vs/base/common/paths');
import types = require('vs/base/common/types');
......@@ -50,13 +50,15 @@ import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/con
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { attachStylerCallback, attachButtonStyler } from 'vs/platform/theme/common/styler';
import { widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { widgetShadow, editorWidgetBackground, foreground, darken, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { deepClone } from 'vs/base/common/objects';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Button } from 'vs/base/browser/ui/button/button';
import { Schemas } from 'vs/base/common/network';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { Themable } from 'vs/workbench/common/theme';
// TODO@Sandeep layer breaker
// tslint:disable-next-line:import-patterns
......@@ -271,7 +273,7 @@ export class EditorStatus implements IStatusbarItem {
private activeEditorListeners: IDisposable[];
private delayedRender: IDisposable;
private toRender: StateChange;
private lastScreenReaderExplanation: ScreenReaderDetectedExplanation;
private screenReaderExplanation: ScreenReaderDetectedExplanation;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
......@@ -286,7 +288,6 @@ export class EditorStatus implements IStatusbarItem {
this.toDispose = [];
this.activeEditorListeners = [];
this.state = new State();
this.lastScreenReaderExplanation = null;
}
public render(container: HTMLElement): IDisposable {
......@@ -484,7 +485,18 @@ export class EditorStatus implements IStatusbarItem {
}
private onScreenReaderModeClick(): void {
this.lastScreenReaderExplanation = this.instantiationService.createInstance(ScreenReaderDetectedExplanation, this.screenRedearModeElement);
const showExplanation = !this.screenReaderExplanation || !this.screenReaderExplanation.visible;
if (!this.screenReaderExplanation) {
this.screenReaderExplanation = this.instantiationService.createInstance(ScreenReaderDetectedExplanation);
this.toDispose.push(this.screenReaderExplanation);
}
if (showExplanation) {
this.screenReaderExplanation.show(this.screenRedearModeElement);
} else {
this.screenReaderExplanation.hide();
}
}
private onSelectionClick(): void {
......@@ -649,9 +661,8 @@ export class EditorStatus implements IStatusbarItem {
screenReaderMode = (editorWidget.getConfiguration().accessibilitySupport === AccessibilitySupport.Enabled);
}
if (screenReaderMode === false && this.lastScreenReaderExplanation) {
this.lastScreenReaderExplanation.hide();
this.lastScreenReaderExplanation = null;
if (screenReaderMode === false && this.screenReaderExplanation && this.screenReaderExplanation.visible) {
this.screenReaderExplanation.hide();
}
this.updateState({ screenReaderMode: screenReaderMode });
......@@ -1218,111 +1229,146 @@ export class ChangeEncodingAction extends Action {
}
}
class ScreenReaderDetectedExplanation {
private _isDisposed: boolean;
private _toDispose: IDisposable[];
class ScreenReaderDetectedExplanation extends Themable {
private container: HTMLElement;
private hrElement: HTMLHRElement;
private _visible: boolean;
constructor(
anchorElement: HTMLElement,
@IThemeService private readonly themeService: IThemeService,
@IThemeService themeService: IThemeService,
@IContextViewService private readonly contextViewService: IContextViewService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
) {
this._isDisposed = false;
this._toDispose = [];
super(themeService);
}
this.contextViewService.showContextView({
getAnchor: () => anchorElement,
public get visible(): boolean {
return this._visible;
}
protected updateStyles(): void {
if (this.container) {
const background = this.getColor(editorWidgetBackground);
this.container.style.backgroundColor = background ? background.toString() : null;
const widgetShadowColor = this.getColor(widgetShadow);
this.container.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null;
const contrastBorderColor = this.getColor(contrastBorder);
this.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : null;
const foregroundColor = this.getColor(foreground);
this.hrElement.style.backgroundColor = foregroundColor ? foregroundColor.toString() : null;
}
}
public show(anchorElement: HTMLElement): void {
this._visible = true;
this.contextViewService.showContextView({
getAnchor: () => {
const res = getDomNodePagePosition(anchorElement);
return {
x: res.left,
y: res.top - 9, /* above the status bar */
width: res.width,
height: res.height
} as IAnchor;
},
render: (container) => {
return this.renderContents(container);
},
onDOMEvent: (e, activeElement) => {
},
onDOMEvent: (e, activeElement) => { },
onHide: () => {
this.dispose();
this._visible = false;
}
});
}
public dispose(): void {
this._isDisposed = true;
this._toDispose = dispose(this._toDispose);
}
public hide(): void {
if (this._isDisposed) {
return;
}
this.contextViewService.hideContextView();
}
protected renderContents(container: HTMLElement): IDisposable {
const domNode = $('div.screen-reader-detected-explanation', {
protected renderContents(parent: HTMLElement): IDisposable {
const toDispose: IDisposable[] = [];
this.container = $('div.screen-reader-detected-explanation', {
'aria-hidden': 'true'
});
const title = $('h2.title', {}, nls.localize('screenReaderDetectedExplanation.title', "Screen Reader Optimized"));
domNode.appendChild(title);
this.container.appendChild(title);
const closeBtn = $('div.cancel');
this._toDispose.push(addDisposableListener(closeBtn, 'click', () => {
toDispose.push(addDisposableListener(closeBtn, 'click', () => {
this.contextViewService.hideContextView();
}));
domNode.appendChild(closeBtn);
toDispose.push(addDisposableListener(closeBtn, 'mouseover', () => {
const theme = this.themeService.getTheme();
let darkenFactor: number;
switch (theme.type) {
case 'light':
darkenFactor = 0.1;
break;
case 'dark':
darkenFactor = 0.2;
break;
}
if (darkenFactor) {
closeBtn.style.backgroundColor = this.getColor(editorWidgetBackground, (color, theme) => darken(color, darkenFactor)(theme));
}
}));
toDispose.push(addDisposableListener(closeBtn, 'mouseout', () => {
closeBtn.style.backgroundColor = null;
}));
this.container.appendChild(closeBtn);
const question = $('p.question', {}, nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate VS Code?"));
domNode.appendChild(question);
this.container.appendChild(question);
const buttonContainer = $('div.buttons');
domNode.appendChild(buttonContainer);
this.container.appendChild(buttonContainer);
const yesBtn = new Button(buttonContainer);
yesBtn.label = nls.localize('screenReaderDetectedExplanation.answerYes', "Yes");
this._toDispose.push(attachButtonStyler(yesBtn, this.themeService));
this._toDispose.push(yesBtn.onDidClick(e => {
toDispose.push(attachButtonStyler(yesBtn, this.themeService));
toDispose.push(yesBtn.onDidClick(e => {
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
this.contextViewService.hideContextView();
}));
const noBtn = new Button(buttonContainer);
noBtn.label = nls.localize('screenReaderDetectedExplanation.answerNo', "No");
this._toDispose.push(attachButtonStyler(noBtn, this.themeService));
this._toDispose.push(noBtn.onDidClick(e => {
toDispose.push(attachButtonStyler(noBtn, this.themeService));
toDispose.push(noBtn.onDidClick(e => {
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
this.contextViewService.hideContextView();
}));
const clear = $('div');
clear.style.clear = 'both';
domNode.appendChild(clear);
this.container.appendChild(clear);
const br = $('br');
domNode.appendChild(br);
this.container.appendChild(br);
const hr = $('hr');
domNode.appendChild(hr);
this.hrElement = $('hr');
this.container.appendChild(this.hrElement);
const explanation1 = $('p.body1', {}, nls.localize('screenReaderDetectedExplanation.body1', "VS Code is now optimized for usage with a screen reader."));
domNode.appendChild(explanation1);
this.container.appendChild(explanation1);
const explanation2 = $('p.body2', {}, nls.localize('screenReaderDetectedExplanation.body2', "Some editor features will have different behaviour: e.g. word wrapping, folding, etc."));
domNode.appendChild(explanation2);
this.container.appendChild(explanation2);
container.appendChild(domNode);
parent.appendChild(this.container);
this._toDispose.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground }, colors => {
domNode.style.backgroundColor = colors.editorWidgetBackground;
if (colors.widgetShadow) {
domNode.style.boxShadow = `0 5px 8px ${colors.widgetShadow}`;
}
}));
this.updateStyles();
return {
dispose: () => { this.dispose(); }
dispose: () => dispose(toDispose)
};
}
}
......@@ -22,7 +22,6 @@
cursor: default !important;
}
.monaco-shell .screen-reader-detected-explanation {
width: 420px;
top: 30px;
......@@ -53,8 +52,9 @@
font-size: 1.2em;
}
.monaco-shell .screen-reader-detected-explanation p.question {
font-size: 1.4em;
.monaco-shell .screen-reader-detected-explanation hr {
border: 0;
height: 2px;
}
.monaco-shell .screen-reader-detected-explanation .buttons {
......@@ -72,21 +72,8 @@
.monaco-shell.vs .screen-reader-detected-explanation .cancel {
background: url('close-big.svg') center center no-repeat;
}
.monaco-shell.vs .screen-reader-detected-explanation .cancel:hover {
background-color: #eaeaea;
}
.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel,
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel {
background: url('close-big-dark.svg') center center no-repeat;
}
.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel:hover {
background-color: rgba(30,30,30,0.8);
}
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel {
opacity: 0.6;
}
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel:hover {
opacity: 1;
}
}
\ No newline at end of file
......@@ -1277,7 +1277,6 @@ export class Workbench implements IPartService {
// Notifications Status
const notificationsStatus = this.instantiationService.createInstance(NotificationsStatus, this.notificationService.model);
this.toUnbind.push(notificationsStatus);
// Eventing
this.toUnbind.push(this.notificationsCenter.onDidChangeVisibility(() => {
......
......@@ -18,9 +18,10 @@ import * as errors from 'vs/base/common/errors';
import { IIntegrityService } from 'vs/platform/integrity/common/integrity';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
import { editorWidgetBackground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, buttonBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { editorWidgetBackground, widgetShadow, inputBorder, inputForeground, inputBackground, inputActiveOptionBorder, editorBackground, buttonBackground, contrastBorder, darken } from 'vs/platform/theme/common/colorRegistry';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
export const FEEDBACK_VISIBLE_CONFIG = 'workbench.statusBar.feedback.visible';
......@@ -37,6 +38,7 @@ export interface IFeedbackService {
export interface IFeedbackDropdownOptions {
contextViewProvider: IContextViewService;
feedbackService?: IFeedbackService;
onFeedbackVisibilityChange?: (visible: boolean) => void;
}
enum FormEvent {
......@@ -69,7 +71,7 @@ export class FeedbackDropdown extends Dropdown {
constructor(
container: HTMLElement,
options: IFeedbackDropdownOptions,
private options: IFeedbackDropdownOptions,
@ICommandService private commandService: ICommandService,
@ITelemetryService private telemetryService: ITelemetryService,
@IIntegrityService private integrityService: IIntegrityService,
......@@ -112,6 +114,17 @@ export class FeedbackDropdown extends Dropdown {
this.requestFeatureLink = product.sendASmile.requestFeatureUrl;
}
protected getAnchor(): HTMLElement | IAnchor {
const res = dom.getDomNodePagePosition(this.element.getHTMLElement());
return {
x: res.left,
y: res.top - 9, /* above the status bar */
width: res.width,
height: res.height
} as IAnchor;
}
protected renderContents(container: HTMLElement): IDisposable {
const $form = $('form.feedback-form').attr({
action: 'javascript:void(0);'
......@@ -123,7 +136,27 @@ export class FeedbackDropdown extends Dropdown {
$('h2.title').text(nls.localize("label.sendASmile", "Tweet us your feedback.")).appendTo($form);
this.invoke($('div.cancel').attr('tabindex', '0'), () => {
const cancelBtn = $('div.cancel').attr('tabindex', '0');
cancelBtn.on(dom.EventType.MOUSE_OVER, () => {
const theme = this.themeService.getTheme();
let darkenFactor: number;
switch (theme.type) {
case 'light':
darkenFactor = 0.1;
break;
case 'dark':
darkenFactor = 0.2;
break;
}
if (darkenFactor) {
cancelBtn.getHTMLElement().style.backgroundColor = darken(theme.getColor(editorWidgetBackground), darkenFactor)(theme).toString();
}
});
cancelBtn.on(dom.EventType.MOUSE_OUT, () => {
cancelBtn.getHTMLElement().style.backgroundColor = null;
});
this.invoke(cancelBtn, () => {
this.hide();
}).appendTo($form);
......@@ -223,7 +256,7 @@ export class FeedbackDropdown extends Dropdown {
this.toDispose.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground, inputBackground, inputForeground, inputBorder, editorBackground, contrastBorder }, colors => {
$form.style('background-color', colors.editorWidgetBackground);
$form.style('box-shadow', colors.widgetShadow ? `0 5px 8px ${colors.widgetShadow}` : null);
$form.style('box-shadow', colors.widgetShadow ? `0 0 8px ${colors.widgetShadow}` : null);
if (this.feedbackDescriptionInput) {
this.feedbackDescriptionInput.style.backgroundColor = colors.inputBackground;
......@@ -293,6 +326,20 @@ export class FeedbackDropdown extends Dropdown {
return element;
}
public show(): void {
super.show();
if (this.options.onFeedbackVisibilityChange) {
this.options.onFeedbackVisibilityChange(true);
}
}
protected onHide(): void {
if (this.options.onFeedbackVisibilityChange) {
this.options.onFeedbackVisibilityChange(false);
}
}
public hide(): void {
if (this.feedbackDescriptionInput) {
this.feedback = this.feedbackDescriptionInput.value;
......
......@@ -7,7 +7,7 @@
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { FeedbackDropdown, IFeedback, IFeedbackService, FEEDBACK_VISIBLE_CONFIG } from 'vs/workbench/parts/feedback/electron-browser/feedback';
import { FeedbackDropdown, IFeedback, IFeedbackService, FEEDBACK_VISIBLE_CONFIG, IFeedbackDropdownOptions } from 'vs/workbench/parts/feedback/electron-browser/feedback';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import product from 'vs/platform/node/product';
......@@ -16,7 +16,7 @@ import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector }
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { clearNode, EventHelper } from 'vs/base/browser/dom';
import { clearNode, EventHelper, addClass, removeClass } from 'vs/base/browser/dom';
import { $ } from 'vs/base/browser/builder';
import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
......@@ -130,8 +130,15 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem {
if (!this.dropdown) {
this.dropdown = this.instantiationService.createInstance(FeedbackDropdown, this.container, {
contextViewProvider: this.contextViewService,
feedbackService: this.instantiationService.createInstance(TwitterFeedbackService)
});
feedbackService: this.instantiationService.createInstance(TwitterFeedbackService),
onFeedbackVisibilityChange: visible => {
if (visible) {
addClass(this.container, 'has-beak');
} else {
removeClass(this.container, 'has-beak');
}
}
} as IFeedbackDropdownOptions);
this.toUnbind.push(this.dropdown);
this.updateStyles();
......
......@@ -151,10 +151,6 @@
background: url('close.svg') center center no-repeat;
}
.monaco-shell.vs .feedback-form .cancel:hover {
background-color: #eaeaea;
}
.monaco-shell .feedback-form .form-buttons {
display: flex;
}
......@@ -221,10 +217,6 @@
background: url('close-dark.svg') center center no-repeat;
}
.monaco-shell.vs-dark .feedback-form .cancel:hover {
background-color: rgba(30,30,30,0.8);
}
.monaco-shell .feedback-form .sentiment.smile {
background-image: url('happy.svg');
background-position: center;
......@@ -264,13 +256,6 @@
padding: 10px;
float: right;
}
.monaco-shell.hc-black .feedback-form .cancel {
opacity: 0.6;
}
.monaco-shell.hc-black .feedback-form .cancel:hover {
opacity: 1;
}
.monaco-shell.hc-black .feedback-form .form-buttons .send,
.monaco-shell.hc-black .feedback-form .form-buttons .send.in-progress,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册