提交 a9d47772 编写于 作者: B Benjamin Pasero

status - cleanup rendering

上级 13c2fc3f
...@@ -73,6 +73,9 @@ export interface IStatusbarService { ...@@ -73,6 +73,9 @@ export interface IStatusbarService {
* *
* @param id identifier of the entry is needed to allow users to hide entries via settings * @param id identifier of the entry is needed to allow users to hide entries via settings
* @param name human readable name the entry is about * @param name human readable name the entry is about
* @param alignment either LEFT or RIGHT
* @param priority items get arranged from highest priority to lowest priority from left to right
* in their respective alignment slot
*/ */
addEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority?: number): IStatusbarEntryAccessor; addEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority?: number): IStatusbarEntryAccessor;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import 'vs/css!./media/statusbarpart'; import 'vs/css!./media/statusbarpart';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { toErrorMessage } from 'vs/base/common/errorMessage'; import { toErrorMessage } from 'vs/base/common/errorMessage';
import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { dispose, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { Registry } from 'vs/platform/registry/common/platform'; import { Registry } from 'vs/platform/registry/common/platform';
import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICommandService } from 'vs/platform/commands/common/commands';
...@@ -33,7 +33,6 @@ import { coalesce } from 'vs/base/common/arrays'; ...@@ -33,7 +33,6 @@ import { coalesce } from 'vs/base/common/arrays';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions'; import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { Event, Emitter } from 'vs/base/common/event';
import { values } from 'vs/base/common/map'; import { values } from 'vs/base/common/map';
interface IPendingStatusbarEntry { interface IPendingStatusbarEntry {
...@@ -50,6 +49,7 @@ interface IStatusbarViewModelItem { ...@@ -50,6 +49,7 @@ interface IStatusbarViewModelItem {
name: string; name: string;
alignment: StatusbarAlignment; alignment: StatusbarAlignment;
priority: number; priority: number;
container: HTMLElement;
} }
class StatusbarViewModel extends Disposable { class StatusbarViewModel extends Disposable {
...@@ -59,9 +59,6 @@ class StatusbarViewModel extends Disposable { ...@@ -59,9 +59,6 @@ class StatusbarViewModel extends Disposable {
private readonly _items: IStatusbarViewModelItem[] = []; private readonly _items: IStatusbarViewModelItem[] = [];
get items(): IStatusbarViewModelItem[] { return this._items; } get items(): IStatusbarViewModelItem[] { return this._items; }
private readonly _onDidVisibilityChange: Emitter<string> = this._register(new Emitter());
get onDidVisibilityChange(): Event<string> { return this._onDidVisibilityChange.event; }
private hidden: Set<string>; private hidden: Set<string>;
constructor(private storageService: IStorageService) { constructor(private storageService: IStorageService) {
...@@ -104,24 +101,24 @@ class StatusbarViewModel extends Disposable { ...@@ -104,24 +101,24 @@ class StatusbarViewModel extends Disposable {
const changed = new Set<string>(); const changed = new Set<string>();
// Check for each entry that is now visible // Check for each entry that is now visible
currentlyHidden.forEach(entry => { currentlyHidden.forEach(id => {
if (!this.hidden.has(entry)) { if (!this.hidden.has(id)) {
changed.add(entry); changed.add(id);
} }
}); });
// Check for each entry that is now hidden // Check for each entry that is now hidden
this.hidden.forEach(entry => { this.hidden.forEach(id => {
if (!currentlyHidden.has(entry)) { if (!currentlyHidden.has(id)) {
changed.add(entry); changed.add(id);
} }
}); });
// Notify listeners that visibility for entries have changed // Update visibility for entries have changed
if (changed.size > 0) { if (changed.size > 0) {
this._items.forEach(item => { this._items.forEach(item => {
if (changed.has(item.id)) { if (changed.has(item.id)) {
this._onDidVisibilityChange.fire(item.id); this.updateVisibility(item.id);
changed.delete(item.id); changed.delete(item.id);
} }
...@@ -130,12 +127,18 @@ class StatusbarViewModel extends Disposable { ...@@ -130,12 +127,18 @@ class StatusbarViewModel extends Disposable {
} }
} }
add(item: IStatusbarViewModelItem): void { add(item: IStatusbarViewModelItem): IDisposable {
this._items.push(item); this._items.push(item); // intentionally not using a map here since multiple items can have the same ID!
// Update visibility directly
this.updateVisibility(item);
this.sort(); this.sort();
return toDisposable(() => this.remove(item));
} }
remove(item: IStatusbarViewModelItem): void { private remove(item: IStatusbarViewModelItem): void {
const index = this._items.indexOf(item); const index = this._items.indexOf(item);
if (index >= 0) { if (index >= 0) {
this._items.splice(index, 1); this._items.splice(index, 1);
...@@ -150,7 +153,7 @@ class StatusbarViewModel extends Disposable { ...@@ -150,7 +153,7 @@ class StatusbarViewModel extends Disposable {
if (!this.hidden.has(id)) { if (!this.hidden.has(id)) {
this.hidden.add(id); this.hidden.add(id);
this._onDidVisibilityChange.fire(id); this.updateVisibility(id);
this.saveState(); this.saveState();
} }
...@@ -160,12 +163,56 @@ class StatusbarViewModel extends Disposable { ...@@ -160,12 +163,56 @@ class StatusbarViewModel extends Disposable {
if (this.hidden.has(id)) { if (this.hidden.has(id)) {
this.hidden.delete(id); this.hidden.delete(id);
this._onDidVisibilityChange.fire(id); this.updateVisibility(id);
this.saveState(); this.saveState();
} }
} }
findItem(container: HTMLElement): IStatusbarViewModelItem | undefined {
for (const item of this._items) {
if (item.container === container) {
return item;
}
}
return undefined;
}
getItems(alignment: StatusbarAlignment): IStatusbarViewModelItem[] {
return this._items.filter(item => item.alignment === alignment);
}
private updateVisibility(id: string): void;
private updateVisibility(item: IStatusbarViewModelItem): void;
private updateVisibility(arg1: string | IStatusbarViewModelItem): void {
// By identifier
if (typeof arg1 === 'string') {
const id = arg1;
for (const item of this._items) {
if (item.id !== id) {
continue;
}
this.updateVisibility(item);
}
}
// By item
else {
const item = arg1;
const isHidden = this.isHidden(item.id);
if (isHidden) {
hide(item.container);
} else {
show(item.container);
}
}
}
private saveState(): void { private saveState(): void {
if (this.hidden.size > 0) { if (this.hidden.size > 0) {
this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(values(this.hidden)), StorageScope.GLOBAL); this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(values(this.hidden)), StorageScope.GLOBAL);
...@@ -177,7 +224,7 @@ class StatusbarViewModel extends Disposable { ...@@ -177,7 +224,7 @@ class StatusbarViewModel extends Disposable {
private sort(): void { private sort(): void {
this._items.sort((itemA, itemB) => { this._items.sort((itemA, itemB) => {
if (itemA.alignment === itemB.alignment) { if (itemA.alignment === itemB.alignment) {
return itemB.priority - itemA.priority; return itemB.priority - itemA.priority; // higher priority towards the left
} }
if (itemA.alignment === StatusbarAlignment.LEFT) { if (itemA.alignment === StatusbarAlignment.LEFT) {
...@@ -229,10 +276,6 @@ export class StatusbarPart extends Part implements IStatusbarService { ...@@ -229,10 +276,6 @@ export class StatusbarPart extends Part implements IStatusbarService {
_serviceBrand: ServiceIdentifier<IStatusbarService>; _serviceBrand: ServiceIdentifier<IStatusbarService>;
private static readonly PRIORITY_PROP = 'statusbar-item-priority';
private static readonly ALIGNMENT_PROP = 'statusbar-item-alignment';
private static readonly IDENTIFIER_PROP = 'statusbar-item-identifier';
//#region IView //#region IView
readonly minimumWidth: number = 0; readonly minimumWidth: number = 0;
...@@ -265,20 +308,6 @@ export class StatusbarPart extends Part implements IStatusbarService { ...@@ -265,20 +308,6 @@ export class StatusbarPart extends Part implements IStatusbarService {
private registerListeners(): void { private registerListeners(): void {
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateStyles())); this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateStyles()));
this._register(this.viewModel.onDidVisibilityChange(id => this.onDidVisibilityChange(id)));
}
private onDidVisibilityChange(id: string): void {
const isHidden = this.viewModel.isHidden(id);
const items = this.getEntries(id);
items.forEach(item => {
if (isHidden) {
hide(item);
} else {
show(item);
}
});
} }
addEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number = 0): IStatusbarEntryAccessor { addEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number = 0): IStatusbarEntryAccessor {
...@@ -320,33 +349,16 @@ export class StatusbarPart extends Part implements IStatusbarService { ...@@ -320,33 +349,16 @@ export class StatusbarPart extends Part implements IStatusbarService {
private doAddEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number): IStatusbarEntryAccessor { private doAddEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number): IStatusbarEntryAccessor {
// Add to view model // Create item
const viewModelItem: IStatusbarViewModelItem = { id, name, alignment, priority };
this.viewModel.add(viewModelItem);
// Render entry in status bar
const itemContainer = this.doCreateStatusItem(id, name, alignment, priority, ...coalesce(['statusbar-entry', entry.showBeak ? 'has-beak' : undefined])); const itemContainer = this.doCreateStatusItem(id, name, alignment, priority, ...coalesce(['statusbar-entry', entry.showBeak ? 'has-beak' : undefined]));
const item = this.instantiationService.createInstance(StatusbarEntryItem, itemContainer, entry); const item = this.instantiationService.createInstance(StatusbarEntryItem, itemContainer, entry);
// Insert according to priority // Append to parent
const container = this.element; this.appendOneStatusbarEntry(itemContainer, alignment, priority);
const neighbours = this.getEntries(alignment);
let inserted = false;
for (const neighbour of neighbours) {
const nPriority = Number(neighbour.getAttribute(StatusbarPart.PRIORITY_PROP));
if (
alignment === StatusbarAlignment.LEFT && nPriority < priority ||
alignment === StatusbarAlignment.RIGHT && nPriority > priority
) {
container.insertBefore(itemContainer, neighbour);
inserted = true;
break;
}
}
if (!inserted) { // Add to view model
container.appendChild(itemContainer); const viewModelItem: IStatusbarViewModelItem = { id, name, alignment, priority, container: itemContainer };
} const viewModelItemDispose = this.viewModel.add(viewModelItem);
return { return {
update: entry => { update: entry => {
...@@ -362,39 +374,13 @@ export class StatusbarPart extends Part implements IStatusbarService { ...@@ -362,39 +374,13 @@ export class StatusbarPart extends Part implements IStatusbarService {
item.update(entry); item.update(entry);
}, },
dispose: () => { dispose: () => {
this.viewModel.remove(viewModelItem); dispose(viewModelItemDispose);
itemContainer.remove(); itemContainer.remove();
dispose(item); dispose(item);
} }
}; };
} }
private getEntries(scope: StatusbarAlignment | string): HTMLElement[] {
const entries: HTMLElement[] = [];
const container = this.element;
const children = container.children;
for (let i = 0; i < children.length; i++) {
const childElement = <HTMLElement>children.item(i);
// By alignment
if (typeof scope === 'number') {
if (Number(childElement.getAttribute(StatusbarPart.ALIGNMENT_PROP)) === scope) {
entries.push(childElement);
}
}
// By identifier
else {
if (childElement.getAttribute(StatusbarPart.IDENTIFIER_PROP) === scope) {
entries.push(childElement);
}
}
}
return entries;
}
updateEntryVisibility(id: string, visible: boolean): void { updateEntryVisibility(id: string, visible: boolean): void {
if (visible) { if (visible) {
this.viewModel.show(id); this.viewModel.show(id);
...@@ -410,49 +396,30 @@ export class StatusbarPart extends Part implements IStatusbarService { ...@@ -410,49 +396,30 @@ export class StatusbarPart extends Part implements IStatusbarService {
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(e))); this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(e)));
// Initial status bar entries // Initial status bar entries
this.createInitialStatusEntries(); this.createInitialStatusbarEntries();
return this.element; return this.element;
} }
private createInitialStatusEntries(): void { private createInitialStatusbarEntries(): void {
const registry = Registry.as<IStatusbarRegistry>(Extensions.Statusbar); const registry = Registry.as<IStatusbarRegistry>(Extensions.Statusbar);
const descriptors = registry.items.slice().sort((itemA, itemB) => { // Create initial items that were contributed from the registry
if (itemA.alignment === itemB.alignment) { for (const { id, name, alignment, priority, syncDescriptor } of registry.items) {
if (itemA.alignment === StatusbarAlignment.LEFT) {
return itemB.priority - itemA.priority;
}
return itemA.priority - itemB.priority;
}
if (itemA.alignment === StatusbarAlignment.LEFT) {
return 1;
}
if (itemA.alignment === StatusbarAlignment.RIGHT) {
return -1;
}
return 0;
});
// Fill in initial items that were contributed from the registry
for (const { id, name, alignment, priority, syncDescriptor } of descriptors) {
// Add to view model // Create item
const viewModelItem: IStatusbarViewModelItem = { id, name, alignment, priority };
this.viewModel.add(viewModelItem);
// Render
const item = this.instantiationService.createInstance(syncDescriptor); const item = this.instantiationService.createInstance(syncDescriptor);
const itemContainer = this.doCreateStatusItem(id, name, alignment, priority); const itemContainer = this.doCreateStatusItem(id, name, alignment, priority);
this._register(item.render(itemContainer)); this._register(item.render(itemContainer));
this.element.appendChild(itemContainer);
// Add to view model
const viewModelItem: IStatusbarViewModelItem = { id, name, alignment, priority, container: itemContainer };
this.viewModel.add(viewModelItem);
} }
// Add items in order
this.appendAllStatusbarEntries();
// Fill in pending entries if any // Fill in pending entries if any
while (this.pendingEntries.length) { while (this.pendingEntries.length) {
const pending = this.pendingEntries.shift(); const pending = this.pendingEntries.shift();
...@@ -462,6 +429,35 @@ export class StatusbarPart extends Part implements IStatusbarService { ...@@ -462,6 +429,35 @@ export class StatusbarPart extends Part implements IStatusbarService {
} }
} }
private appendAllStatusbarEntries(): void {
[
...this.viewModel.getItems(StatusbarAlignment.LEFT),
...this.viewModel.getItems(StatusbarAlignment.RIGHT).reverse() // reversing due to display: float
].forEach(item => this.element.appendChild(item.container));
}
private appendOneStatusbarEntry(itemContainer: HTMLElement, alignment: StatusbarAlignment, priority: number): void {
const items = this.viewModel.getItems(alignment);
// Some massaging due to display: float is needed
if (alignment === StatusbarAlignment.RIGHT) {
items.reverse();
priority *= -1;
}
// find an item that has lower priority than the new one
// and then insert the item before that one
for (const item of items) {
if (item.priority < priority) {
this.element.insertBefore(itemContainer, item.container);
return;
}
}
// Fallback to just appending otherwise
this.element.appendChild(itemContainer);
}
private showContextMenu(e: MouseEvent): void { private showContextMenu(e: MouseEvent): void {
EventHelper.stop(e, true); EventHelper.stop(e, true);
...@@ -487,16 +483,17 @@ export class StatusbarPart extends Part implements IStatusbarService { ...@@ -487,16 +483,17 @@ export class StatusbarPart extends Part implements IStatusbarService {
const actions: Action[] = []; const actions: Action[] = [];
// Figure out if mouse is over an entry // Figure out if mouse is over an entry
let statusEntryUnderMouse: string | undefined = undefined; let statusEntryUnderMouse: IStatusbarViewModelItem | undefined = undefined;
for (let element: HTMLElement | null = event.target; element; element = element.parentElement) { for (let element: HTMLElement | null = event.target; element; element = element.parentElement) {
if (element.hasAttribute(StatusbarPart.IDENTIFIER_PROP)) { const item = this.viewModel.findItem(element);
statusEntryUnderMouse = element.getAttribute(StatusbarPart.IDENTIFIER_PROP)!; if (item) {
statusEntryUnderMouse = item;
break; break;
} }
} }
if (statusEntryUnderMouse) { if (statusEntryUnderMouse) {
actions.push(new HideStatusbarEntryAction(statusEntryUnderMouse, this.viewModel)); actions.push(new HideStatusbarEntryAction(statusEntryUnderMouse.id, this.viewModel));
actions.push(new Separator()); actions.push(new Separator());
} }
...@@ -557,14 +554,6 @@ export class StatusbarPart extends Part implements IStatusbarService { ...@@ -557,14 +554,6 @@ export class StatusbarPart extends Part implements IStatusbarService {
addClass(itemContainer, 'left'); addClass(itemContainer, 'left');
} }
itemContainer.setAttribute(StatusbarPart.PRIORITY_PROP, String(priority));
itemContainer.setAttribute(StatusbarPart.ALIGNMENT_PROP, String(alignment));
itemContainer.setAttribute(StatusbarPart.IDENTIFIER_PROP, id);
if (this.viewModel.isHidden(id)) {
hide(itemContainer);
}
return itemContainer; return itemContainer;
} }
......
...@@ -20,7 +20,6 @@ import { Button } from 'vs/base/browser/ui/button/button'; ...@@ -20,7 +20,6 @@ import { Button } from 'vs/base/browser/ui/button/button';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export interface IFeedback { export interface IFeedback {
feedback: string; feedback: string;
...@@ -66,8 +65,7 @@ export class FeedbackDropdown extends Dropdown { ...@@ -66,8 +65,7 @@ export class FeedbackDropdown extends Dropdown {
@ITelemetryService private readonly telemetryService: ITelemetryService, @ITelemetryService private readonly telemetryService: ITelemetryService,
@IIntegrityService private readonly integrityService: IIntegrityService, @IIntegrityService private readonly integrityService: IIntegrityService,
@IThemeService private readonly themeService: IThemeService, @IThemeService private readonly themeService: IThemeService,
@IStatusbarService private readonly statusbarService: IStatusbarService, @IStatusbarService private readonly statusbarService: IStatusbarService
@IConfigurationService private readonly configurationService: IConfigurationService
) { ) {
super(container, { super(container, {
contextViewProvider: options.contextViewProvider, contextViewProvider: options.contextViewProvider,
...@@ -91,10 +89,6 @@ export class FeedbackDropdown extends Dropdown { ...@@ -91,10 +89,6 @@ export class FeedbackDropdown extends Dropdown {
dom.addClass(this.element, 'send-feedback'); dom.addClass(this.element, 'send-feedback');
this.element.title = nls.localize('sendFeedback', "Tweet Feedback"); this.element.title = nls.localize('sendFeedback', "Tweet Feedback");
if (!this.configurationService.getValue('workbench.statusBar.feedback.visible')) {
this.statusbarService.updateEntryVisibility('status.feedback', false);
}
} }
protected getAnchor(): HTMLElement | IAnchor { protected getAnchor(): HTMLElement | IAnchor {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册