mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-26 11:26:56 +01:00
Maximize editor group (#194894)
* Maximize editor * unmaximze instead of exit maximize * add tests * 💄 * adopt changes * 💄 * 💄 * 💄 * 💄 * 💄 * 💄 * move context key to WorkbenchContextKeysHandler * 💄 * 💄 * bug fix * 💄 * touch file to remove from diff * test maximized group event * Update editor group behavior * 💄 * 💄 * 💄 * 💄 * 💄 * 💄 * setvisibility unmaximizes view if maximized * Add visibility option to serialized branch node and update gridview reconstruction behavior * Fix arrangeGroups method signature * Save maximized view by location * Fix activeGroup check in EditorPart * 💄 * 💄 * Add gridWidgetDisposables to prevent memory leaks. * simplify GridView.setChildVisible * Refactor Grid class to return IDisposable from maximizeView method * 💄 * move maximizing to gridview layer * Fix trySet2x2 method to exit maximized view before setting 2x2 * fix bug * hasMaximizedGroup * Refactor GridView for hasMaximizedView * fix migrations * Fix double click tab toggle value in workbench editor * 💄 * Fix editor group expansion test * 💄 * 💄 * 💄 * Fix migration with new settings validation --------- Co-authored-by: Benjamin Pasero <benjamin.pasero@microsoft.com> Co-authored-by: João Moreno <joao.moreno@microsoft.com>
This commit is contained in:
co-authored by
Benjamin Pasero
João Moreno
parent
bf9068c750
commit
246abc0ecb
@@ -51,6 +51,7 @@ export interface GridLeafNode<T extends IView> {
|
||||
readonly view: T;
|
||||
readonly box: Box;
|
||||
readonly cachedVisibleSize: number | undefined;
|
||||
readonly maximized: boolean;
|
||||
}
|
||||
|
||||
export interface GridBranchNode<T extends IView> {
|
||||
@@ -288,6 +289,7 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
|
||||
private didLayout = false;
|
||||
|
||||
readonly onDidChangeViewMaximized: Event<boolean>;
|
||||
/**
|
||||
* Create a new {@link Grid}. A grid must *always* have a view
|
||||
* inside.
|
||||
@@ -313,6 +315,7 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
|
||||
this.onDidChange = this.gridview.onDidChange;
|
||||
this.onDidScroll = this.gridview.onDidScroll;
|
||||
this.onDidChangeViewMaximized = this.gridview.onDidChangeViewMaximized;
|
||||
}
|
||||
|
||||
style(styles: IGridStyles): void {
|
||||
@@ -545,9 +548,28 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
*
|
||||
* @param view The reference {@link IView view}.
|
||||
*/
|
||||
isViewSizeMaximized(view: T): boolean {
|
||||
isViewExpanded(view: T): boolean {
|
||||
const location = this.getViewLocation(view);
|
||||
return this.gridview.isViewSizeMaximized(location);
|
||||
return this.gridview.isViewExpanded(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link IView view} is maximized.
|
||||
*
|
||||
* @param view The reference {@link IView view}.
|
||||
*/
|
||||
isViewMaximized(view: T): boolean {
|
||||
const location = this.getViewLocation(view);
|
||||
return this.gridview.isViewMaximized(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link IView view} is maximized.
|
||||
*
|
||||
* @param view The reference {@link IView view}.
|
||||
*/
|
||||
hasMaximizedView(): boolean {
|
||||
return this.gridview.hasMaximizedView();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -577,14 +599,30 @@ export class Grid<T extends IView = IView> extends Disposable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximize the size of a {@link IView view} by collapsing all other views
|
||||
* Maximizes the specified view and hides all other views.
|
||||
* @param view The view to maximize.
|
||||
*/
|
||||
maximizeView(view: T) {
|
||||
if (this.views.size < 2) {
|
||||
throw new Error('At least two views are required to maximize a view');
|
||||
}
|
||||
const location = this.getViewLocation(view);
|
||||
this.gridview.maximizeView(location);
|
||||
}
|
||||
|
||||
exitMaximizedView(): void {
|
||||
this.gridview.exitMaximizedView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the size of a {@link IView view} by collapsing all other views
|
||||
* to their minimum sizes.
|
||||
*
|
||||
* @param view The {@link IView view}.
|
||||
*/
|
||||
maximizeViewSize(view: T): void {
|
||||
expandView(view: T): void {
|
||||
const location = this.getViewLocation(view);
|
||||
this.gridview.maximizeViewSize(location);
|
||||
this.gridview.expandView(location);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -713,12 +751,14 @@ export interface ISerializedLeafNode {
|
||||
data: any;
|
||||
size: number;
|
||||
visible?: boolean;
|
||||
maximized?: boolean;
|
||||
}
|
||||
|
||||
export interface ISerializedBranchNode {
|
||||
type: 'branch';
|
||||
data: ISerializedNode[];
|
||||
size: number;
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
export type ISerializedNode = ISerializedLeafNode | ISerializedBranchNode;
|
||||
@@ -739,14 +779,23 @@ export class SerializableGrid<T extends ISerializableView> extends Grid<T> {
|
||||
const size = orientation === Orientation.VERTICAL ? node.box.width : node.box.height;
|
||||
|
||||
if (!isGridBranchNode(node)) {
|
||||
const serializedLeafNode: ISerializedLeafNode = { type: 'leaf', data: node.view.toJSON(), size };
|
||||
|
||||
if (typeof node.cachedVisibleSize === 'number') {
|
||||
return { type: 'leaf', data: node.view.toJSON(), size: node.cachedVisibleSize, visible: false };
|
||||
serializedLeafNode.size = node.cachedVisibleSize;
|
||||
serializedLeafNode.visible = false;
|
||||
} else if (node.maximized) {
|
||||
serializedLeafNode.maximized = true;
|
||||
}
|
||||
|
||||
return { type: 'leaf', data: node.view.toJSON(), size };
|
||||
return serializedLeafNode;
|
||||
}
|
||||
|
||||
return { type: 'branch', data: node.children.map(c => SerializableGrid.serializeNode(c, orthogonal(orientation))), size };
|
||||
const data = node.children.map(c => SerializableGrid.serializeNode(c, orthogonal(orientation)));
|
||||
if (data.some(c => c.visible !== false)) {
|
||||
return { type: 'branch', data: data, size };
|
||||
}
|
||||
return { type: 'branch', data: data, size, visible: false };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -148,12 +148,14 @@ export interface ISerializedLeafNode {
|
||||
data: any;
|
||||
size: number;
|
||||
visible?: boolean;
|
||||
maximized?: boolean;
|
||||
}
|
||||
|
||||
export interface ISerializedBranchNode {
|
||||
type: 'branch';
|
||||
data: ISerializedNode[];
|
||||
size: number;
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
export type ISerializedNode = ISerializedLeafNode | ISerializedBranchNode;
|
||||
@@ -180,6 +182,7 @@ export interface GridLeafNode {
|
||||
readonly view: IView;
|
||||
readonly box: Box;
|
||||
readonly cachedVisibleSize: number | undefined;
|
||||
readonly maximized: boolean;
|
||||
}
|
||||
|
||||
export interface GridBranchNode {
|
||||
@@ -284,11 +287,11 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
}
|
||||
|
||||
get minimumSize(): number {
|
||||
return this.children.length === 0 ? 0 : Math.max(...this.children.map(c => c.minimumOrthogonalSize));
|
||||
return this.children.length === 0 ? 0 : Math.max(...this.children.map((c, index) => this.splitview.isViewVisible(index) ? c.minimumOrthogonalSize : 0));
|
||||
}
|
||||
|
||||
get maximumSize(): number {
|
||||
return Math.min(...this.children.map(c => c.maximumOrthogonalSize));
|
||||
return Math.min(...this.children.map((c, index) => this.splitview.isViewVisible(index) ? c.maximumOrthogonalSize : Number.POSITIVE_INFINITY));
|
||||
}
|
||||
|
||||
get priority(): LayoutPriority {
|
||||
@@ -342,6 +345,10 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
private readonly _onDidChange = new Emitter<number | undefined>();
|
||||
readonly onDidChange: Event<number | undefined> = this._onDidChange.event;
|
||||
|
||||
private readonly _onDidVisibilityChange = new Emitter<boolean>();
|
||||
readonly onDidVisibilityChange: Event<boolean> = this._onDidVisibilityChange.event;
|
||||
private readonly childrenVisibilityChangeDisposable: DisposableStore = new DisposableStore();
|
||||
|
||||
private _onDidScroll = new Emitter<void>();
|
||||
private onDidScrollDisposable: IDisposable = Disposable.None;
|
||||
readonly onDidScroll: Event<void> = this._onDidScroll.event;
|
||||
@@ -427,7 +434,7 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
return {
|
||||
view: childDescriptor.node,
|
||||
size: childDescriptor.node.size,
|
||||
visible: childDescriptor.node instanceof LeafNode && childDescriptor.visible !== undefined ? childDescriptor.visible : true
|
||||
visible: childDescriptor.visible !== false
|
||||
};
|
||||
}),
|
||||
size: this.orthogonalSize
|
||||
@@ -579,8 +586,8 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
this.splitview.resizeView(index, size);
|
||||
}
|
||||
|
||||
isChildSizeMaximized(index: number): boolean {
|
||||
return this.splitview.isViewSizeMaximized(index);
|
||||
isChildExpanded(index: number): boolean {
|
||||
return this.splitview.isViewExpanded(index);
|
||||
}
|
||||
|
||||
distributeViewSizes(recursive = false): void {
|
||||
@@ -614,7 +621,15 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
return;
|
||||
}
|
||||
|
||||
const wereAllChildrenHidden = this.splitview.contentSize === 0;
|
||||
this.splitview.setViewVisible(index, visible);
|
||||
const areAllChildrenHidden = this.splitview.contentSize === 0;
|
||||
|
||||
// If all children are hidden then the parent should hide the entire splitview
|
||||
// If the entire splitview is hidden then the parent should show the splitview when a child is shown
|
||||
if ((visible && wereAllChildrenHidden) || (!visible && areAllChildrenHidden)) {
|
||||
this._onDidVisibilityChange.fire(visible);
|
||||
}
|
||||
}
|
||||
|
||||
getChildCachedVisibleSize(index: number): number | undefined {
|
||||
@@ -651,6 +666,15 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
const onDidScroll = Event.any(Event.signal(this.splitview.onDidScroll), ...this.children.map(c => c.onDidScroll));
|
||||
this.onDidScrollDisposable.dispose();
|
||||
this.onDidScrollDisposable = onDidScroll(this._onDidScroll.fire, this._onDidScroll);
|
||||
|
||||
this.childrenVisibilityChangeDisposable.clear();
|
||||
this.children.forEach((child, index) => {
|
||||
if (child instanceof BranchNode) {
|
||||
this.childrenVisibilityChangeDisposable.add(child.onDidVisibilityChange((visible) => {
|
||||
this.setChildVisible(index, visible);
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
trySet2x2(other: BranchNode): IDisposable {
|
||||
@@ -714,7 +738,9 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
|
||||
|
||||
this._onDidChange.dispose();
|
||||
this._onDidSashReset.dispose();
|
||||
this._onDidVisibilityChange.dispose();
|
||||
|
||||
this.childrenVisibilityChangeDisposable.dispose();
|
||||
this.splitviewSashResetDisposable.dispose();
|
||||
this.childrenSashResetDisposable.dispose();
|
||||
this.childrenChangeDisposable.dispose();
|
||||
@@ -1128,6 +1154,11 @@ export class GridView implements IDisposable {
|
||||
this.root.edgeSnapping = edgeSnapping;
|
||||
}
|
||||
|
||||
private maximizedNode: LeafNode | undefined = undefined;
|
||||
|
||||
private readonly _onDidChangeViewMaximized = new Emitter<boolean>();
|
||||
readonly onDidChangeViewMaximized = this._onDidChangeViewMaximized.event;
|
||||
|
||||
/**
|
||||
* Create a new {@link GridView} instance.
|
||||
*
|
||||
@@ -1173,6 +1204,10 @@ export class GridView implements IDisposable {
|
||||
* @param location The {@link GridLocation location} to insert the view on.
|
||||
*/
|
||||
addView(view: IView, size: number | Sizing, location: GridLocation): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
this.disposable2x2.dispose();
|
||||
this.disposable2x2 = Disposable.None;
|
||||
|
||||
@@ -1226,6 +1261,10 @@ export class GridView implements IDisposable {
|
||||
* @param sizing Whether to distribute other {@link IView view}'s sizes.
|
||||
*/
|
||||
removeView(location: GridLocation, sizing?: DistributeSizing | AutoSizing): IView {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
this.disposable2x2.dispose();
|
||||
this.disposable2x2 = Disposable.None;
|
||||
|
||||
@@ -1312,6 +1351,10 @@ export class GridView implements IDisposable {
|
||||
* @param to The index where the {@link IView view} should move to.
|
||||
*/
|
||||
moveView(parentLocation: GridLocation, from: number, to: number): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
const [, parent] = this.getNode(parentLocation);
|
||||
|
||||
if (!(parent instanceof BranchNode)) {
|
||||
@@ -1330,6 +1373,10 @@ export class GridView implements IDisposable {
|
||||
* @param to The {@link GridLocation location} of another view.
|
||||
*/
|
||||
swapViews(from: GridLocation, to: GridLocation): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
const [fromRest, fromIndex] = tail(from);
|
||||
const [, fromParent] = this.getNode(fromRest);
|
||||
|
||||
@@ -1378,6 +1425,10 @@ export class GridView implements IDisposable {
|
||||
* @param size The size the view should be. Optionally provide a single dimension.
|
||||
*/
|
||||
resizeView(location: GridLocation, size: Partial<IViewSize>): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
const [rest, index] = tail(location);
|
||||
const [pathToParent, parent] = this.getNode(rest);
|
||||
|
||||
@@ -1443,7 +1494,11 @@ export class GridView implements IDisposable {
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
maximizeViewSize(location: GridLocation): void {
|
||||
expandView(location: GridLocation): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
const [ancestors, node] = this.getNode(location);
|
||||
|
||||
if (!(node instanceof LeafNode)) {
|
||||
@@ -1460,7 +1515,12 @@ export class GridView implements IDisposable {
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
isViewSizeMaximized(location: GridLocation): boolean {
|
||||
isViewExpanded(location: GridLocation): boolean {
|
||||
if (this.hasMaximizedView()) {
|
||||
// No view can be expanded when a view is maximized
|
||||
return false;
|
||||
}
|
||||
|
||||
const [ancestors, node] = this.getNode(location);
|
||||
|
||||
if (!(node instanceof LeafNode)) {
|
||||
@@ -1468,7 +1528,7 @@ export class GridView implements IDisposable {
|
||||
}
|
||||
|
||||
for (let i = 0; i < ancestors.length; i++) {
|
||||
if (!ancestors[i].isChildSizeMaximized(location[i])) {
|
||||
if (!ancestors[i].isChildExpanded(location[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1476,6 +1536,80 @@ export class GridView implements IDisposable {
|
||||
return true;
|
||||
}
|
||||
|
||||
maximizeView(location: GridLocation) {
|
||||
const [, nodeToMaximize] = this.getNode(location);
|
||||
if (!(nodeToMaximize instanceof LeafNode)) {
|
||||
throw new Error('Location is not a LeafNode');
|
||||
}
|
||||
|
||||
if (this.maximizedNode === nodeToMaximize) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
function hideAllViewsBut(parent: BranchNode, exclude: LeafNode): void {
|
||||
for (let i = 0; i < parent.children.length; i++) {
|
||||
const child = parent.children[i];
|
||||
if (child instanceof LeafNode) {
|
||||
if (child !== exclude) {
|
||||
parent.setChildVisible(i, false);
|
||||
}
|
||||
} else {
|
||||
hideAllViewsBut(child, exclude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hideAllViewsBut(this.root, nodeToMaximize);
|
||||
|
||||
this.maximizedNode = nodeToMaximize;
|
||||
this._onDidChangeViewMaximized.fire(true);
|
||||
}
|
||||
|
||||
exitMaximizedView(): void {
|
||||
if (!this.maximizedNode) {
|
||||
return;
|
||||
}
|
||||
this.maximizedNode = undefined;
|
||||
|
||||
// When hiding a view, it's previous size is cached.
|
||||
// To restore the sizes of all views, they need to be made visible in reverse order.
|
||||
function showViewsInReverseOrder(parent: BranchNode): void {
|
||||
for (let index = parent.children.length - 1; index >= 0; index--) {
|
||||
const child = parent.children[index];
|
||||
if (child instanceof LeafNode) {
|
||||
parent.setChildVisible(index, true);
|
||||
} else {
|
||||
showViewsInReverseOrder(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showViewsInReverseOrder(this.root);
|
||||
|
||||
this._onDidChangeViewMaximized.fire(false);
|
||||
}
|
||||
|
||||
hasMaximizedView(): boolean {
|
||||
return this.maximizedNode !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link IView view} is maximized.
|
||||
*
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
isViewMaximized(location: GridLocation): boolean {
|
||||
const [, node] = this.getNode(location);
|
||||
if (!(node instanceof LeafNode)) {
|
||||
throw new Error('Location is not a LeafNode');
|
||||
}
|
||||
return node === this.maximizedNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Distribute the size among all {@link IView views} within the entire
|
||||
* grid or within a single {@link SplitView}.
|
||||
@@ -1486,6 +1620,10 @@ export class GridView implements IDisposable {
|
||||
* in the entire grid.
|
||||
*/
|
||||
distributeViewSizes(location?: GridLocation): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
}
|
||||
|
||||
if (!location) {
|
||||
this.root.distributeViewSizes(true);
|
||||
return;
|
||||
@@ -1523,6 +1661,11 @@ export class GridView implements IDisposable {
|
||||
* @param location The {@link GridLocation location} of the view.
|
||||
*/
|
||||
setViewVisible(location: GridLocation, visible: boolean): void {
|
||||
if (this.hasMaximizedView()) {
|
||||
this.exitMaximizedView();
|
||||
return;
|
||||
}
|
||||
|
||||
const [rest, index] = tail(location);
|
||||
const [, parent] = this.getNode(rest);
|
||||
|
||||
@@ -1596,6 +1739,10 @@ export class GridView implements IDisposable {
|
||||
result = new BranchNode(orientation, this.layoutController, this.styles, this.proportionalLayout, node.size, orthogonalSize, undefined, children);
|
||||
} else {
|
||||
result = new LeafNode(deserializer.fromJSON(node.data), orientation, this.layoutController, orthogonalSize, node.size);
|
||||
if (node.maximized && !this.maximizedNode) {
|
||||
this.maximizedNode = result;
|
||||
this._onDidChangeViewMaximized.fire(true);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -1605,7 +1752,7 @@ export class GridView implements IDisposable {
|
||||
const box = { top: node.top, left: node.left, width: node.width, height: node.height };
|
||||
|
||||
if (node instanceof LeafNode) {
|
||||
return { view: node.view, box, cachedVisibleSize };
|
||||
return { view: node.view, box, cachedVisibleSize, maximized: this.maximizedNode === node };
|
||||
}
|
||||
|
||||
const children: GridNode[] = [];
|
||||
|
||||
@@ -442,7 +442,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
private scrollableElement: SmoothScrollableElement;
|
||||
private size = 0;
|
||||
private layoutContext: TLayoutContext | undefined;
|
||||
private contentSize = 0;
|
||||
private _contentSize = 0;
|
||||
private proportions: (number | undefined)[] | undefined = undefined;
|
||||
private viewItems: ViewItem<TLayoutContext, TView>[] = [];
|
||||
sashItems: ISashItem[] = []; // used in tests
|
||||
@@ -459,6 +459,11 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
private _startSnappingEnabled = true;
|
||||
private _endSnappingEnabled = true;
|
||||
|
||||
/**
|
||||
* The sum of all views' sizes.
|
||||
*/
|
||||
get contentSize(): number { return this._contentSize; }
|
||||
|
||||
/**
|
||||
* Fires whenever the user resizes a {@link Sash sash}.
|
||||
*/
|
||||
@@ -624,7 +629,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
});
|
||||
|
||||
// Initialize content size and proportions for first layout
|
||||
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
this.saveProportions();
|
||||
}
|
||||
}
|
||||
@@ -834,7 +839,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
* @param layoutContext An optional layout context to pass along to {@link IView views}.
|
||||
*/
|
||||
layout(size: number, layoutContext?: TLayoutContext): void {
|
||||
const previousSize = Math.max(this.size, this.contentSize);
|
||||
const previousSize = Math.max(this.size, this._contentSize);
|
||||
this.size = size;
|
||||
this.layoutContext = layoutContext;
|
||||
|
||||
@@ -862,7 +867,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
const item = this.viewItems[i];
|
||||
const proportion = this.proportions[i];
|
||||
|
||||
if (typeof proportion === 'number') {
|
||||
if (typeof proportion === 'number' && total > 0) {
|
||||
item.size = clamp(Math.round(proportion * size / total), item.minimumSize, item.maximumSize);
|
||||
}
|
||||
}
|
||||
@@ -873,8 +878,8 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
}
|
||||
|
||||
private saveProportions(): void {
|
||||
if (this.proportionalLayout && this.contentSize > 0) {
|
||||
this.proportions = this.viewItems.map(i => i.proportionalLayout ? i.size / this.contentSize : undefined);
|
||||
if (this.proportionalLayout && this._contentSize > 0) {
|
||||
this.proportions = this.viewItems.map(v => v.proportionalLayout && v.visible ? v.size / this._contentSize : undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1052,7 +1057,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
/**
|
||||
* Returns whether all other {@link IView views} are at their minimum size.
|
||||
*/
|
||||
isViewSizeMaximized(index: number): boolean {
|
||||
isViewExpanded(index: number): boolean {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
return false;
|
||||
}
|
||||
@@ -1347,7 +1352,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
|
||||
private layoutViews(): void {
|
||||
// Save new content size
|
||||
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
|
||||
// Layout views
|
||||
let offset = 0;
|
||||
@@ -1367,12 +1372,12 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
if (this.orientation === Orientation.VERTICAL) {
|
||||
this.scrollableElement.setScrollDimensions({
|
||||
height: this.size,
|
||||
scrollHeight: this.contentSize
|
||||
scrollHeight: this._contentSize
|
||||
});
|
||||
} else {
|
||||
this.scrollableElement.setScrollDimensions({
|
||||
width: this.size,
|
||||
scrollWidth: this.contentSize
|
||||
scrollWidth: this._contentSize
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1411,7 +1416,7 @@ export class SplitView<TLayoutContext = undefined, TView extends IView<TLayoutCo
|
||||
|
||||
if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) {
|
||||
sash.state = SashState.AtMinimum;
|
||||
} else if (snappedAfter && collapsesDown[index] && (position < this.contentSize || this.endSnappingEnabled)) {
|
||||
} else if (snappedAfter && collapsesDown[index] && (position < this._contentSize || this.endSnappingEnabled)) {
|
||||
sash.state = SashState.AtMaximum;
|
||||
} else {
|
||||
sash.state = SashState.Disabled;
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as assert from 'assert';
|
||||
import { createSerializedGrid, Direction, getRelativeLocation, Grid, GridNode, GridNodeDescriptor, ISerializableView, isGridBranchNode, IViewDeserializer, Orientation, sanitizeGridNodeDescriptor, SerializableGrid, Sizing } from 'vs/base/browser/ui/grid/grid';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { nodesToArrays, TestView } from './util';
|
||||
import { nodesToArrays, TestView } from 'vs/base/test/browser/ui/grid/util';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
|
||||
@@ -464,6 +464,206 @@ suite('Grid', function () {
|
||||
|
||||
assert.deepStrictEqual(grid.getNeighborViews(view1, Direction.Right), [view2, view3]);
|
||||
});
|
||||
|
||||
test('hiding splitviews and restoring sizes', function () {
|
||||
const view1 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
const grid = store.add(new Grid(view1));
|
||||
container.appendChild(grid.element);
|
||||
|
||||
grid.layout(800, 600);
|
||||
|
||||
const view2 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view2, Sizing.Distribute, view1, Direction.Right);
|
||||
|
||||
const view3 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view3, Sizing.Distribute, view2, Direction.Down);
|
||||
|
||||
const view4 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view4, Sizing.Distribute, view2, Direction.Right);
|
||||
|
||||
const size1 = view1.size;
|
||||
const size2 = view2.size;
|
||||
const size3 = view3.size;
|
||||
const size4 = view4.size;
|
||||
|
||||
grid.maximizeView(view1);
|
||||
|
||||
// Views 2, 3, 4 are hidden
|
||||
// Splitview (2,4) and ((2,4),3) are hidden
|
||||
assert.deepStrictEqual(view1.size, [800, 600]);
|
||||
assert.deepStrictEqual(view2.size, [0, 0]);
|
||||
assert.deepStrictEqual(view3.size, [0, 0]);
|
||||
assert.deepStrictEqual(view4.size, [0, 0]);
|
||||
|
||||
grid.exitMaximizedView();
|
||||
|
||||
assert.deepStrictEqual(view1.size, size1);
|
||||
assert.deepStrictEqual(view2.size, size2);
|
||||
assert.deepStrictEqual(view3.size, size3);
|
||||
assert.deepStrictEqual(view4.size, size4);
|
||||
|
||||
// Views 1, 3, 4 are hidden
|
||||
// All splitviews are still visible => only orthogonalsize is 0
|
||||
grid.maximizeView(view2);
|
||||
|
||||
assert.deepStrictEqual(view1.size, [0, 600]);
|
||||
assert.deepStrictEqual(view2.size, [800, 600]);
|
||||
assert.deepStrictEqual(view3.size, [800, 0]);
|
||||
assert.deepStrictEqual(view4.size, [0, 600]);
|
||||
|
||||
grid.exitMaximizedView();
|
||||
|
||||
assert.deepStrictEqual(view1.size, size1);
|
||||
assert.deepStrictEqual(view2.size, size2);
|
||||
assert.deepStrictEqual(view3.size, size3);
|
||||
assert.deepStrictEqual(view4.size, size4);
|
||||
});
|
||||
|
||||
test('hasMaximizedView', function () {
|
||||
const view1 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
const grid = store.add(new Grid(view1));
|
||||
container.appendChild(grid.element);
|
||||
|
||||
grid.layout(800, 600);
|
||||
|
||||
const view2 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view2, Sizing.Distribute, view1, Direction.Right);
|
||||
|
||||
const view3 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view3, Sizing.Distribute, view2, Direction.Down);
|
||||
|
||||
const view4 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view4, Sizing.Distribute, view2, Direction.Right);
|
||||
|
||||
function checkIsMaximized(view: TestView) {
|
||||
grid.maximizeView(view);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
|
||||
// When a view is maximized, no view can be expanded even if it is maximized
|
||||
assert.deepStrictEqual(grid.isViewExpanded(view1), false);
|
||||
assert.deepStrictEqual(grid.isViewExpanded(view2), false);
|
||||
assert.deepStrictEqual(grid.isViewExpanded(view3), false);
|
||||
assert.deepStrictEqual(grid.isViewExpanded(view4), false);
|
||||
|
||||
grid.exitMaximizedView();
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
}
|
||||
|
||||
checkIsMaximized(view1);
|
||||
checkIsMaximized(view2);
|
||||
checkIsMaximized(view3);
|
||||
checkIsMaximized(view4);
|
||||
});
|
||||
|
||||
test('Changes to the grid unmaximize the view', function () {
|
||||
const view1 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
const grid = store.add(new Grid(view1));
|
||||
container.appendChild(grid.element);
|
||||
|
||||
grid.layout(800, 600);
|
||||
|
||||
const view2 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view2, Sizing.Distribute, view1, Direction.Right);
|
||||
|
||||
const view3 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view3, Sizing.Distribute, view2, Direction.Down);
|
||||
|
||||
const view4 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
|
||||
// Adding a view unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.addView(view4, Sizing.Distribute, view2, Direction.Right);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view4), true);
|
||||
|
||||
// Removing a view unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.removeView(view4);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
|
||||
// Changing the visibility of any view while a view is maximized, unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.setViewVisible(view3, true);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
});
|
||||
|
||||
test('Changes to the grid sizing unmaximize the view', function () {
|
||||
const view1 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
const grid = store.add(new Grid(view1));
|
||||
container.appendChild(grid.element);
|
||||
|
||||
grid.layout(800, 600);
|
||||
|
||||
const view2 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view2, Sizing.Distribute, view1, Direction.Right);
|
||||
|
||||
const view3 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view3, Sizing.Distribute, view2, Direction.Down);
|
||||
|
||||
const view4 = store.add(new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE));
|
||||
grid.addView(view4, Sizing.Distribute, view2, Direction.Right);
|
||||
|
||||
// Maximizing a different view unmaximizes the current one and maximizes the new one
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.maximizeView(view2);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view4), false);
|
||||
|
||||
// Distributing the size unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.distributeViewSizes();
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view4), true);
|
||||
|
||||
// Expanding a different view unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.expandView(view2);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view4), true);
|
||||
|
||||
// Expanding the maximized view unmaximizes the view
|
||||
grid.maximizeView(view1);
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), true);
|
||||
grid.expandView(view1);
|
||||
|
||||
assert.deepStrictEqual(grid.hasMaximizedView(), false);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view1), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view2), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view3), true);
|
||||
assert.deepStrictEqual(grid.isViewVisible(view4), true);
|
||||
});
|
||||
});
|
||||
|
||||
class TestSerializableView extends TestView implements ISerializableView {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as assert from 'assert';
|
||||
import { $ } from 'vs/base/browser/dom';
|
||||
import { GridView, IView, Orientation, Sizing } from 'vs/base/browser/ui/grid/gridview';
|
||||
import { nodesToArrays, TestView } from './util';
|
||||
import { nodesToArrays, TestView } from 'vs/base/test/browser/ui/grid/util';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
|
||||
suite('Gridview', function () {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds, TitleBarVisibleContext } from 'vs/workbench/common/contextkeys';
|
||||
import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds, MaximizedEditorGroupContext, TitleBarVisibleContext } from 'vs/workbench/common/contextkeys';
|
||||
import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { trackFocus, addDisposableListener, EventType, onDidRegisterWindow } from 'vs/base/browser/dom';
|
||||
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
@@ -44,6 +44,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
private activeEditorGroupLast: IContextKey<boolean>;
|
||||
private activeEditorGroupLocked: IContextKey<boolean>;
|
||||
private multipleEditorGroupsContext: IContextKey<boolean>;
|
||||
private maximizedEditorGroupContext: IContextKey<boolean>;
|
||||
|
||||
private editorsVisibleContext: IContextKey<boolean>;
|
||||
|
||||
@@ -136,6 +137,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
this.activeEditorGroupLast = ActiveEditorGroupLastContext.bindTo(this.contextKeyService);
|
||||
this.activeEditorGroupLocked = ActiveEditorGroupLockedContext.bindTo(this.contextKeyService);
|
||||
this.multipleEditorGroupsContext = MultipleEditorGroupsContext.bindTo(this.contextKeyService);
|
||||
this.maximizedEditorGroupContext = MaximizedEditorGroupContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Working Copies
|
||||
this.dirtyWorkingCopiesContext = DirtyWorkingCopiesContext.bindTo(this.contextKeyService);
|
||||
@@ -233,6 +235,8 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
this._register(this.editorGroupService.onDidChangeActiveGroup(() => this.updateEditorGroupContextKeys()));
|
||||
this._register(this.editorGroupService.onDidChangeGroupLocked(() => this.updateEditorGroupContextKeys()));
|
||||
|
||||
this._register(this.editorGroupService.onDidChangeGroupMaximized((maximized) => this.maximizedEditorGroupContext.set(maximized)));
|
||||
|
||||
this._register(this.editorGroupService.onDidChangeEditorPartOptions(() => this.updateEditorAreaContextKeys()));
|
||||
|
||||
this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposableStore }) => disposableStore.add(addDisposableListener(window, EventType.FOCUS_IN, () => this.updateInputContextKeys(window.document), true)), { window, disposableStore: this._store }));
|
||||
|
||||
@@ -10,7 +10,7 @@ import { IEditorFactoryRegistry, EditorExtensions } from 'vs/workbench/common/ed
|
||||
import {
|
||||
TextCompareEditorActiveContext, ActiveEditorPinnedContext, EditorGroupEditorsCountContext, ActiveEditorStickyContext, ActiveEditorAvailableEditorIdsContext,
|
||||
MultipleEditorGroupsContext, ActiveEditorDirtyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext,
|
||||
EditorTabsVisibleContext, ActiveEditorLastInGroupContext
|
||||
EditorTabsVisibleContext, ActiveEditorLastInGroupContext, MaximizedEditorGroupContext
|
||||
} from 'vs/workbench/common/contextkeys';
|
||||
import { SideBySideEditorInput, SideBySideEditorInputSerializer } from 'vs/workbench/common/editor/sideBySideEditorInput';
|
||||
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
|
||||
@@ -40,13 +40,14 @@ import {
|
||||
QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction,
|
||||
ReOpenInTextEditorAction, DuplicateGroupDownAction, DuplicateGroupLeftAction, DuplicateGroupRightAction, DuplicateGroupUpAction, ToggleEditorTypeAction, SplitEditorToAboveGroupAction, SplitEditorToBelowGroupAction,
|
||||
SplitEditorToFirstGroupAction, SplitEditorToLastGroupAction, SplitEditorToLeftGroupAction, SplitEditorToNextGroupAction, SplitEditorToPreviousGroupAction, SplitEditorToRightGroupAction, NavigateForwardInEditsAction,
|
||||
NavigateBackwardsInEditsAction, NavigateForwardInNavigationsAction, NavigateBackwardsInNavigationsAction, NavigatePreviousInNavigationsAction, NavigatePreviousInEditsAction, NavigateToLastNavigationLocationAction, ExperimentalMoveEditorIntoNewWindowAction
|
||||
NavigateBackwardsInEditsAction, NavigateForwardInNavigationsAction, NavigateBackwardsInNavigationsAction, NavigatePreviousInNavigationsAction, NavigatePreviousInEditsAction, NavigateToLastNavigationLocationAction,
|
||||
MaximizeGroupHideSidebarAction, UnmaximizeEditorGroupAction, ExperimentalMoveEditorIntoNewWindowAction
|
||||
} from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import {
|
||||
CLOSE_EDITORS_AND_GROUP_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_EDITOR_GROUP_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
|
||||
CLOSE_PINNED_EDITOR_COMMAND_ID, CLOSE_SAVED_EDITORS_COMMAND_ID, GOTO_NEXT_CHANGE, GOTO_PREVIOUS_CHANGE, KEEP_EDITOR_COMMAND_ID, PIN_EDITOR_COMMAND_ID, SHOW_EDITORS_IN_GROUP, SPLIT_EDITOR_DOWN, SPLIT_EDITOR_LEFT,
|
||||
SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, TOGGLE_DIFF_SIDE_BY_SIDE, TOGGLE_KEEP_EDITORS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, setup as registerEditorCommands, REOPEN_WITH_COMMAND_ID,
|
||||
TOGGLE_LOCK_GROUP_COMMAND_ID, UNLOCK_GROUP_COMMAND_ID, SPLIT_EDITOR_IN_GROUP, JOIN_EDITOR_IN_GROUP, FOCUS_FIRST_SIDE_EDITOR, FOCUS_SECOND_SIDE_EDITOR, TOGGLE_SPLIT_EDITOR_IN_GROUP_LAYOUT, SPLIT_EDITOR
|
||||
TOGGLE_LOCK_GROUP_COMMAND_ID, UNLOCK_GROUP_COMMAND_ID, SPLIT_EDITOR_IN_GROUP, JOIN_EDITOR_IN_GROUP, FOCUS_FIRST_SIDE_EDITOR, FOCUS_SECOND_SIDE_EDITOR, TOGGLE_SPLIT_EDITOR_IN_GROUP_LAYOUT, SPLIT_EDITOR, MAXIMIZE_EDITOR_GROUP, UNMAXIMIZE_EDITOR_GROUP
|
||||
} from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { inQuickPickContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
@@ -214,6 +215,8 @@ registerAction2(NavigateBetweenGroupsAction);
|
||||
registerAction2(ResetGroupSizesAction);
|
||||
registerAction2(ToggleGroupSizesAction);
|
||||
registerAction2(MaximizeGroupAction);
|
||||
registerAction2(UnmaximizeEditorGroupAction);
|
||||
registerAction2(MaximizeGroupHideSidebarAction);
|
||||
registerAction2(MinimizeOtherGroupsAction);
|
||||
|
||||
registerAction2(MoveEditorLeftInGroupAction);
|
||||
@@ -382,7 +385,9 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SHOW_EDITORS_IN
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '5_close', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('togglePreviewMode', "Enable Preview Editors"), toggled: ContextKeyExpr.has('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_lock', order: 10, when: MultipleEditorGroupsContext });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: MAXIMIZE_EDITOR_GROUP, title: localize('maximizeGroup', "Maximize Group") }, group: '8_group_operations', order: 5, when: ContextKeyExpr.and(MaximizedEditorGroupContext.negate(), MultipleEditorGroupsContext) });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: UNMAXIMIZE_EDITOR_GROUP, title: localize('unmaximizeGroup', "Unmaximize Group") }, group: '8_group_operations', order: 5, when: MaximizedEditorGroupContext });
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_group_operations', order: 10, when: MultipleEditorGroupsContext });
|
||||
|
||||
function appendEditorToolItem(primary: ICommandAction, when: ContextKeyExpression | undefined, order: number, alternative?: ICommandAction, precondition?: ContextKeyExpression | undefined): void {
|
||||
const item: IMenuItem = {
|
||||
|
||||
@@ -48,7 +48,7 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
|
||||
splitSizing: 'auto',
|
||||
splitOnDragAndDrop: true,
|
||||
centeredLayoutFixedWidth: false,
|
||||
doubleClickTabToToggleEditorGroupSizes: true,
|
||||
doubleClickTabToToggleEditorGroupSizes: 'expand',
|
||||
};
|
||||
|
||||
export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean {
|
||||
@@ -116,7 +116,6 @@ function validateEditorPartOptions(options: IEditorPartOptions): void {
|
||||
'restoreViewState',
|
||||
'splitOnDragAndDrop',
|
||||
'centeredLayoutFixedWidth',
|
||||
'doubleClickTabToToggleEditorGroupSizes'
|
||||
];
|
||||
for (const option of booleanOptions) {
|
||||
if (typeof option === 'string') {
|
||||
@@ -149,6 +148,7 @@ function validateEditorPartOptions(options: IEditorPartOptions): void {
|
||||
['labelFormat', ['default', 'short', 'medium', 'long']],
|
||||
['splitInGroupLayout', ['vertical', 'horizontal']],
|
||||
['splitSizing', ['distribute', 'split', 'auto']],
|
||||
['doubleClickTabToToggleEditorGroupSizes', ['maximize', 'expand', 'off']]
|
||||
];
|
||||
for (const [option, allowed] of stringOptions) {
|
||||
if (typeof option === 'string') {
|
||||
@@ -235,6 +235,8 @@ export interface IEditorGroupsView {
|
||||
removeGroup(group: IEditorGroupView | GroupIdentifier): void;
|
||||
|
||||
arrangeGroups(arrangement: GroupsArrangement, target?: IEditorGroupView | GroupIdentifier): void;
|
||||
toggleMaximizeGroup(group?: IEditorGroupView | GroupIdentifier): void;
|
||||
toggleExpandGroup(group?: IEditorGroupView | GroupIdentifier): void;
|
||||
}
|
||||
|
||||
export interface IEditorGroupTitleHeight {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro
|
||||
import { GoFilter, IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveCopyArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, COPY_ACTIVE_EDITOR_COMMAND_ID, SPLIT_EDITOR } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveCopyArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, COPY_ACTIVE_EDITOR_COMMAND_ID, SPLIT_EDITOR, UNMAXIMIZE_EDITOR_GROUP, MAXIMIZE_EDITOR_GROUP, resolveCommandsContext, getCommandsContext } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -33,7 +33,8 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
|
||||
import { ActiveEditorAvailableEditorIdsContext, ActiveEditorContext, ActiveEditorGroupEmptyContext } from 'vs/workbench/common/contextkeys';
|
||||
import { ActiveEditorAvailableEditorIdsContext, ActiveEditorContext, ActiveEditorGroupEmptyContext, MaximizedEditorGroupContext, MultipleEditorGroupsContext } from 'vs/workbench/common/contextkeys';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { getActiveDocument } from 'vs/base/browser/dom';
|
||||
|
||||
class ExecuteCommandAction extends Action2 {
|
||||
@@ -1014,7 +1015,7 @@ export class MinimizeOtherGroupsAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.minimizeOtherEditors',
|
||||
title: { value: localize('minimizeOtherEditorGroups', "Maximize Editor Group"), original: 'Maximize Editor Group' },
|
||||
title: { value: localize('minimizeOtherEditorGroups', "Expand Editor Group"), original: 'Expand Editor Group' },
|
||||
f1: true,
|
||||
category: Categories.View
|
||||
});
|
||||
@@ -1023,7 +1024,7 @@ export class MinimizeOtherGroupsAction extends Action2 {
|
||||
override async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
editorGroupService.arrangeGroups(GroupsArrangement.MAXIMIZE);
|
||||
editorGroupService.arrangeGroups(GroupsArrangement.EXPAND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1059,7 +1060,7 @@ export class ToggleGroupSizesAction extends Action2 {
|
||||
override async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
|
||||
editorGroupService.arrangeGroups(GroupsArrangement.TOGGLE);
|
||||
editorGroupService.toggleExpandGroup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1067,10 +1068,34 @@ export class MaximizeGroupAction extends Action2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.maximizeEditor',
|
||||
title: { value: localize('maximizeEditor', "Maximize Editor Group and Hide Side Bars"), original: 'Maximize Editor Group and Hide Side Bars' },
|
||||
id: MAXIMIZE_EDITOR_GROUP,
|
||||
title: { value: localize('maximizeEditor', "Maximize Editor Group"), original: 'Maximize Editor Group' },
|
||||
f1: true,
|
||||
category: Categories.View
|
||||
category: Categories.View,
|
||||
precondition: ContextKeyExpr.and(MaximizedEditorGroupContext.negate(), MultipleEditorGroupsContext),
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyM),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise<void> {
|
||||
const editorsGroupService = accessor.get(IEditorGroupsService);
|
||||
const { group } = resolveCommandsContext(editorsGroupService, getCommandsContext(resourceOrContext, context));
|
||||
editorsGroupService.arrangeGroups(GroupsArrangement.MAXIMIZE, group);
|
||||
}
|
||||
}
|
||||
|
||||
export class MaximizeGroupHideSidebarAction extends Action2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.maximizeEditorHideSidebar',
|
||||
title: { value: localize('maximizeEditorHideSidebar', "Maximize Editor Group and Hide Side Bars"), original: 'Maximize Editor Group and Hide Side Bars' },
|
||||
f1: true,
|
||||
category: Categories.View,
|
||||
precondition: ContextKeyExpr.and(MaximizedEditorGroupContext.negate(), MultipleEditorGroupsContext)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1087,6 +1112,36 @@ export class MaximizeGroupAction extends Action2 {
|
||||
}
|
||||
}
|
||||
|
||||
export class UnmaximizeEditorGroupAction extends Action2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: UNMAXIMIZE_EDITOR_GROUP,
|
||||
title: { value: localize('UnmaximizeEditorGroup', "Unmaximize Editor Group"), original: 'Unmaximize Editor Group' },
|
||||
f1: true,
|
||||
category: Categories.View,
|
||||
precondition: MaximizedEditorGroupContext,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyM),
|
||||
},
|
||||
menu: {
|
||||
id: MenuId.EditorTitle,
|
||||
order: -10000, // towards the front
|
||||
group: 'navigation',
|
||||
when: MaximizedEditorGroupContext
|
||||
},
|
||||
icon: Codicon.screenFull,
|
||||
toggled: MaximizedEditorGroupContext,
|
||||
});
|
||||
}
|
||||
|
||||
override async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
editorGroupService.toggleMaximizeGroup();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractNavigateEditorAction extends Action2 {
|
||||
|
||||
override async run(accessor: ServicesAccessor): Promise<void> {
|
||||
|
||||
@@ -80,6 +80,9 @@ export const SPLIT_EDITOR_DOWN = 'workbench.action.splitEditorDown';
|
||||
export const SPLIT_EDITOR_LEFT = 'workbench.action.splitEditorLeft';
|
||||
export const SPLIT_EDITOR_RIGHT = 'workbench.action.splitEditorRight';
|
||||
|
||||
export const MAXIMIZE_EDITOR_GROUP = 'workbench.action.maximizeEditorGroup';
|
||||
export const UNMAXIMIZE_EDITOR_GROUP = 'workbench.action.unmaximizeEditorGroup';
|
||||
|
||||
export const SPLIT_EDITOR_IN_GROUP = 'workbench.action.splitEditorInGroup';
|
||||
export const TOGGLE_SPLIT_EDITOR_IN_GROUP = 'workbench.action.toggleSplitEditorInGroup';
|
||||
export const JOIN_EDITOR_IN_GROUP = 'workbench.action.joinEditorInGroup';
|
||||
@@ -104,7 +107,8 @@ export const EDITOR_CORE_NAVIGATION_COMMANDS = [
|
||||
SPLIT_EDITOR,
|
||||
CLOSE_EDITOR_COMMAND_ID,
|
||||
UNPIN_EDITOR_COMMAND_ID,
|
||||
UNLOCK_GROUP_COMMAND_ID
|
||||
UNLOCK_GROUP_COMMAND_ID,
|
||||
UNMAXIMIZE_EDITOR_GROUP
|
||||
];
|
||||
|
||||
export interface ActiveEditorMoveCopyArguments {
|
||||
@@ -1464,7 +1468,7 @@ function getEditorsContext(accessor: ServicesAccessor, resourceOrContext?: URI |
|
||||
};
|
||||
}
|
||||
|
||||
function getCommandsContext(resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext | undefined {
|
||||
export function getCommandsContext(resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext | undefined {
|
||||
if (URI.isUri(resourceOrContext)) {
|
||||
return context;
|
||||
}
|
||||
@@ -1480,7 +1484,7 @@ function getCommandsContext(resourceOrContext?: URI | IEditorCommandsContext, co
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup; editor?: EditorInput } {
|
||||
export function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup; editor?: EditorInput } {
|
||||
|
||||
// Resolve from context
|
||||
let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined;
|
||||
|
||||
@@ -101,6 +101,9 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
private readonly _onDidChangeGroupLocked = this._register(new Emitter<IEditorGroupView>());
|
||||
readonly onDidChangeGroupLocked = this._onDidChangeGroupLocked.event;
|
||||
|
||||
private readonly _onDidChangeGroupMaximized = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeGroupMaximized = this._onDidChangeGroupMaximized.event;
|
||||
|
||||
private readonly _onDidActivateGroup = this._register(new Emitter<IEditorGroupView>());
|
||||
readonly onDidActivateGroup = this._onDidActivateGroup.event;
|
||||
|
||||
@@ -137,6 +140,7 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
private centeredLayoutWidget!: CenteredViewLayout;
|
||||
|
||||
private gridWidget!: SerializableGrid<IEditorGroupView>;
|
||||
private readonly gridWidgetDisposables = this._register(new DisposableStore());
|
||||
private readonly gridWidgetView = this._register(new GridWidgetView<IEditorGroupView>());
|
||||
|
||||
constructor(
|
||||
@@ -147,7 +151,7 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
@@ -369,21 +373,48 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
this.gridWidget.distributeViewSizes();
|
||||
break;
|
||||
case GroupsArrangement.MAXIMIZE:
|
||||
this.gridWidget.maximizeViewSize(target);
|
||||
break;
|
||||
case GroupsArrangement.TOGGLE:
|
||||
if (this.isGroupMaximized(target)) {
|
||||
this.arrangeGroups(GroupsArrangement.EVEN);
|
||||
} else {
|
||||
this.arrangeGroups(GroupsArrangement.MAXIMIZE);
|
||||
if (this.groups.length < 2) {
|
||||
return; // need at least 2 groups to be maximized
|
||||
}
|
||||
|
||||
this.gridWidget.maximizeView(target);
|
||||
this.doSetGroupActive(target);
|
||||
break;
|
||||
case GroupsArrangement.EXPAND:
|
||||
this.gridWidget.expandView(target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
isGroupMaximized(targetGroup: IEditorGroupView): boolean {
|
||||
return this.gridWidget.isViewSizeMaximized(targetGroup);
|
||||
toggleMaximizeGroup(target: IEditorGroupView = this.activeGroup): void {
|
||||
if (this.hasMaximizedGroup()) {
|
||||
this.unmaximizeGroup();
|
||||
} else {
|
||||
this.arrangeGroups(GroupsArrangement.MAXIMIZE, target);
|
||||
}
|
||||
}
|
||||
|
||||
toggleExpandGroup(target: IEditorGroupView = this.activeGroup): void {
|
||||
if (this.isGroupExpanded(this.activeGroup)) {
|
||||
this.arrangeGroups(GroupsArrangement.EVEN);
|
||||
} else {
|
||||
this.arrangeGroups(GroupsArrangement.EXPAND, target);
|
||||
}
|
||||
}
|
||||
|
||||
private unmaximizeGroup(): void {
|
||||
this.gridWidget.exitMaximizedView();
|
||||
}
|
||||
|
||||
private hasMaximizedGroup(): boolean {
|
||||
return this.gridWidget.hasMaximizedView();
|
||||
}
|
||||
|
||||
private isGroupMaximized(targetGroup: IEditorGroupView): boolean {
|
||||
return this.gridWidget.isViewMaximized(targetGroup);
|
||||
}
|
||||
|
||||
isGroupExpanded(targetGroup: IEditorGroupView): boolean {
|
||||
return this.gridWidget.isViewExpanded(targetGroup);
|
||||
}
|
||||
|
||||
setGroupOrientation(orientation: GroupOrientation): void {
|
||||
@@ -524,7 +555,7 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
if (locationView.groupsView === this) {
|
||||
const restoreFocus = this.shouldRestoreFocus(locationView.element);
|
||||
|
||||
const shouldMaximize = this.groupViews.size > 1 && this.isGroupMaximized(locationView);
|
||||
const shouldExpand = this.groupViews.size > 1 && this.isGroupExpanded(locationView);
|
||||
newGroupView = this.doCreateGroupView(groupToCopy);
|
||||
|
||||
// Add to grid widget
|
||||
@@ -544,9 +575,9 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
// Notify group index change given a new group was added
|
||||
this.notifyGroupIndexChange();
|
||||
|
||||
// Maximize new group, if the reference view was previously maximized
|
||||
if (shouldMaximize) {
|
||||
this.arrangeGroups(GroupsArrangement.MAXIMIZE, newGroupView);
|
||||
// Expand new group, if the reference view was previously expanded
|
||||
if (shouldExpand) {
|
||||
this.arrangeGroups(GroupsArrangement.EXPAND, newGroupView);
|
||||
}
|
||||
|
||||
// Restore focus if we had it previously after completing the grid
|
||||
@@ -642,7 +673,7 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
// Mark group as new active
|
||||
group.setActive(true);
|
||||
|
||||
// Maximize the group if it is currently minimized
|
||||
// Expand the group if it is currently minimized
|
||||
this.doRestoreGroup(group);
|
||||
|
||||
// Event
|
||||
@@ -657,9 +688,13 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
|
||||
private doRestoreGroup(group: IEditorGroupView): void {
|
||||
if (this.gridWidget) {
|
||||
if (this.hasMaximizedGroup() && !this.isGroupMaximized(group)) {
|
||||
this.unmaximizeGroup();
|
||||
}
|
||||
|
||||
const viewSize = this.gridWidget.getViewSize(group);
|
||||
if (viewSize.width === group.minimumWidth || viewSize.height === group.minimumHeight) {
|
||||
this.arrangeGroups(GroupsArrangement.MAXIMIZE, group);
|
||||
this.arrangeGroups(GroupsArrangement.EXPAND, group);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1032,6 +1067,10 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
}
|
||||
|
||||
centerLayout(active: boolean): void {
|
||||
if (this.hasMaximizedGroup()) {
|
||||
this.unmaximizeGroup();
|
||||
}
|
||||
|
||||
this.centeredLayoutWidget.activate(active);
|
||||
|
||||
this._activeGroup.focus();
|
||||
@@ -1158,6 +1197,10 @@ export class EditorPart extends Part implements IEditorPart {
|
||||
|
||||
this._onDidChangeSizeConstraints.input = gridWidget.onDidChange;
|
||||
this._onDidScroll.input = gridWidget.onDidScroll;
|
||||
this.gridWidgetDisposables.clear();
|
||||
this.gridWidgetDisposables.add(gridWidget.onDidChangeViewMaximized(maximized => this._onDidChangeGroupMaximized.fire(maximized)));
|
||||
|
||||
this._onDidChangeGroupMaximized.fire(this.hasMaximizedGroup());
|
||||
|
||||
this.onDidSetGridWidget.fire(undefined);
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
disposables.add(part.onDidRemoveGroup(group => this._onDidRemoveGroup.fire(group)));
|
||||
disposables.add(part.onDidMoveGroup(group => this._onDidMoveGroup.fire(group)));
|
||||
disposables.add(part.onDidActivateGroup(group => this._onDidActivateGroup.fire(group)));
|
||||
disposables.add(part.onDidChangeGroupMaximized(maximized => this._onDidChangeGroupMaximized.fire(maximized)));
|
||||
|
||||
disposables.add(part.onDidChangeGroupIndex(group => this._onDidChangeGroupIndex.fire(group)));
|
||||
disposables.add(part.onDidChangeGroupLocked(group => this._onDidChangeGroupLocked.fire(group)));
|
||||
@@ -176,6 +177,9 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
private readonly _onDidChangeGroupLocked = this._register(new Emitter<IEditorGroupView>());
|
||||
readonly onDidChangeGroupLocked = this._onDidChangeGroupLocked.event;
|
||||
|
||||
private readonly _onDidChangeGroupMaximized = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeGroupMaximized = this._onDidChangeGroupMaximized.event;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Editor Groups Service
|
||||
@@ -227,11 +231,19 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
}
|
||||
|
||||
setSize(group: IEditorGroupView | GroupIdentifier, size: { width: number; height: number }): void {
|
||||
return this.getPart(group).setSize(group, size);
|
||||
this.getPart(group).setSize(group, size);
|
||||
}
|
||||
|
||||
arrangeGroups(arrangement: GroupsArrangement): void {
|
||||
return this.activePart.arrangeGroups(arrangement);
|
||||
arrangeGroups(arrangement: GroupsArrangement, group?: IEditorGroupView): void {
|
||||
(group !== undefined ? this.getPart(group) : this.activePart).arrangeGroups(arrangement, group);
|
||||
}
|
||||
|
||||
toggleMaximizeGroup(group?: IEditorGroupView): void {
|
||||
(group !== undefined ? this.getPart(group) : this.activePart).toggleMaximizeGroup(group);
|
||||
}
|
||||
|
||||
toggleExpandGroup(group?: IEditorGroupView): void {
|
||||
(group !== undefined ? this.getPart(group) : this.activePart).toggleExpandGroup(group);
|
||||
}
|
||||
|
||||
restoreGroup(group: IEditorGroupView | GroupIdentifier): IEditorGroupView {
|
||||
@@ -239,7 +251,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
}
|
||||
|
||||
applyLayout(layout: EditorGroupLayout): void {
|
||||
return this.activePart.applyLayout(layout);
|
||||
this.activePart.applyLayout(layout);
|
||||
}
|
||||
|
||||
getLayout(): EditorGroupLayout {
|
||||
@@ -247,7 +259,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
}
|
||||
|
||||
centerLayout(active: boolean): void {
|
||||
return this.activePart.centerLayout(active);
|
||||
this.activePart.centerLayout(active);
|
||||
}
|
||||
|
||||
isLayoutCentered(): boolean {
|
||||
@@ -259,7 +271,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
}
|
||||
|
||||
setGroupOrientation(orientation: GroupOrientation): void {
|
||||
return this.activePart.setGroupOrientation(orientation);
|
||||
this.activePart.setGroupOrientation(orientation);
|
||||
}
|
||||
|
||||
findGroup(scope: IFindGroupScope, source?: IEditorGroupView | GroupIdentifier, wrap?: boolean): IEditorGroupView | undefined {
|
||||
@@ -275,7 +287,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd
|
||||
}
|
||||
|
||||
removeGroup(group: IEditorGroupView | GroupIdentifier): void {
|
||||
return this.getPart(group).removeGroup(group);
|
||||
this.getPart(group).removeGroup(group);
|
||||
}
|
||||
|
||||
moveGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView {
|
||||
|
||||
@@ -31,7 +31,7 @@ import { activeContrastBorder, contrastBorder, editorBackground } from 'vs/platf
|
||||
import { ResourcesDropHandler, DraggedEditorIdentifier, DraggedEditorGroupIdentifier, extractTreeDropData } from 'vs/workbench/browser/dnd';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { MergeGroupMode, IMergeGroupOptions, GroupsArrangement, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { MergeGroupMode, IMergeGroupOptions, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { addDisposableListener, EventType, EventHelper, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode, DragAndDropObserver, isMouseEvent } from 'vs/base/browser/dom';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IEditorGroupsView, EditorServiceImpl, IEditorGroupView, IInternalEditorOpenOptions, IEditorPartsView } from 'vs/workbench/browser/parts/editor/editor';
|
||||
@@ -992,9 +992,17 @@ export class MultiEditorTabsControl extends EditorTabsControl {
|
||||
|
||||
const editor = this.tabsModel.getEditorByIndex(tabIndex);
|
||||
if (editor && this.tabsModel.isPinned(editor)) {
|
||||
if (this.groupsView.partOptions.doubleClickTabToToggleEditorGroupSizes) {
|
||||
this.groupsView.arrangeGroups(GroupsArrangement.TOGGLE, this.groupView);
|
||||
switch (this.groupsView.partOptions.doubleClickTabToToggleEditorGroupSizes) {
|
||||
case 'maximize':
|
||||
this.groupsView.toggleMaximizeGroup(this.groupView);
|
||||
break;
|
||||
case 'expand':
|
||||
this.groupsView.toggleExpandGroup(this.groupView);
|
||||
break;
|
||||
case 'off':
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
this.groupView.pinEditor(editor);
|
||||
}
|
||||
|
||||
@@ -322,9 +322,15 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
|
||||
'description': localize('centeredLayoutDynamicWidth', "Controls whether the centered layout tries to maintain constant width when the window is resized.")
|
||||
},
|
||||
'workbench.editor.doubleClickTabToToggleEditorGroupSizes': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'doubleClickTabToToggleEditorGroupSizes' }, "Controls whether to maximize/restore the editor group when double clicking on a tab. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.")
|
||||
'type': 'string',
|
||||
'enum': ['maximize', 'expand', 'off'],
|
||||
'default': 'expand',
|
||||
'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'doubleClickTabToToggleEditorGroupSizes' }, "Controls how the editor group is resized when double clicking on a tab. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`."),
|
||||
'enumDescriptions': [
|
||||
localize('workbench.editor.doubleClickTabToToggleEditorGroupSizes.maximize', "All other editor groups are hidden and the current editor group is maximized to take up the entire editor area."),
|
||||
localize('workbench.editor.doubleClickTabToToggleEditorGroupSizes.expand', "The editor group takes as much space as possible by making all other editor groups as small as possible."),
|
||||
localize('workbench.editor.doubleClickTabToToggleEditorGroupSizes.off', "No editor group is resized when double clicking on a tab.")
|
||||
]
|
||||
},
|
||||
'workbench.editor.limit.enabled': {
|
||||
'type': 'boolean',
|
||||
@@ -780,16 +786,20 @@ Registry.as<IConfigurationMigrationRegistry>(Extensions.ConfigurationMigration)
|
||||
|
||||
Registry.as<IConfigurationMigrationRegistry>(Extensions.ConfigurationMigration)
|
||||
.registerConfigurationMigrations([{
|
||||
key: 'workbench.editor.doubleClickTabToToggleEditorGroupSizes', migrateFn: (value: any) => {
|
||||
if (typeof value === 'boolean') {
|
||||
value = value ? 'expand' : 'off';
|
||||
}
|
||||
return [['workbench.editor.doubleClickTabToToggleEditorGroupSizes', { value: value }]];
|
||||
}
|
||||
}, {
|
||||
key: 'workbench.editor.showTabs', migrateFn: (value: any) => {
|
||||
if (typeof value === 'boolean') {
|
||||
value = value ? 'multiple' : 'single';
|
||||
}
|
||||
return [['workbench.editor.showTabs', { value: value }]];
|
||||
}
|
||||
}]);
|
||||
|
||||
Registry.as<IConfigurationMigrationRegistry>(Extensions.ConfigurationMigration)
|
||||
.registerConfigurationMigrations([{
|
||||
}, {
|
||||
key: 'zenMode.hideTabs', migrateFn: (value: any) => {
|
||||
const result: ConfigurationKeyValuePairs = [['zenMode.hideTabs', { value: undefined }]];
|
||||
if (value === true) {
|
||||
|
||||
@@ -79,6 +79,7 @@ export const SplitEditorsVertically = new RawContextKey<boolean>('splitEditorsVe
|
||||
export const EditorAreaVisibleContext = new RawContextKey<boolean>('editorAreaVisible', true, localize('editorAreaVisible', "Whether the editor area is visible"));
|
||||
export const EditorTabsVisibleContext = new RawContextKey<boolean>('editorTabsVisible', true, localize('editorTabsVisible', "Whether editor tabs are visible"));
|
||||
export const EditorPinnedAndUnpinnedTabsContext = new RawContextKey<boolean>('editorPinnedAndUnpinnedTabsVisible', false, true);
|
||||
export const MaximizedEditorGroupContext = new RawContextKey<boolean>('maximizedEditorGroup', false, localize('editorGroupMaximized', "Editor group is maximized"));
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
@@ -1139,7 +1139,7 @@ interface IEditorPartConfiguration {
|
||||
splitSizing?: 'auto' | 'split' | 'distribute';
|
||||
splitOnDragAndDrop?: boolean;
|
||||
centeredLayoutFixedWidth?: boolean;
|
||||
doubleClickTabToToggleEditorGroupSizes?: boolean;
|
||||
doubleClickTabToToggleEditorGroupSizes?: 'maximize' | 'expand' | 'off';
|
||||
limit?: IEditorPartLimitConfiguration;
|
||||
decorations?: IEditorPartDecorationsConfiguration;
|
||||
}
|
||||
|
||||
@@ -43,23 +43,22 @@ export interface IFindGroupScope {
|
||||
}
|
||||
|
||||
export const enum GroupsArrangement {
|
||||
/**
|
||||
* Make the current active group consume the entire
|
||||
* editor area.
|
||||
*/
|
||||
MAXIMIZE,
|
||||
|
||||
/**
|
||||
* Make the current active group consume the maximum
|
||||
* amount of space possible.
|
||||
*/
|
||||
MAXIMIZE,
|
||||
EXPAND,
|
||||
|
||||
/**
|
||||
* Size all groups evenly.
|
||||
*/
|
||||
EVEN,
|
||||
|
||||
/**
|
||||
* Will behave like MINIMIZE_OTHERS if the active
|
||||
* group is not already maximized and EVEN otherwise
|
||||
*/
|
||||
TOGGLE
|
||||
EVEN
|
||||
}
|
||||
|
||||
export interface GroupLayoutArgument {
|
||||
@@ -215,6 +214,11 @@ export interface IEditorGroupsContainer {
|
||||
*/
|
||||
readonly onDidChangeGroupLocked: Event<IEditorGroup>;
|
||||
|
||||
/**
|
||||
* An event for when the maximized state of a group changes.
|
||||
*/
|
||||
readonly onDidChangeGroupMaximized: Event<boolean>;
|
||||
|
||||
/**
|
||||
* An active group is the default location for new editors to open.
|
||||
*/
|
||||
@@ -273,7 +277,17 @@ export interface IEditorGroupsContainer {
|
||||
/**
|
||||
* Arrange all groups in the container according to the provided arrangement.
|
||||
*/
|
||||
arrangeGroups(arrangement: GroupsArrangement): void;
|
||||
arrangeGroups(arrangement: GroupsArrangement, target?: IEditorGroup | GroupIdentifier): void;
|
||||
|
||||
/**
|
||||
* Toggles the target goup size to maximize/unmaximize.
|
||||
*/
|
||||
toggleMaximizeGroup(group?: IEditorGroup | GroupIdentifier): void;
|
||||
|
||||
/**
|
||||
* Toggles the target goup size to expand/distribute even.
|
||||
*/
|
||||
toggleExpandGroup(group?: IEditorGroup | GroupIdentifier): void;
|
||||
|
||||
/**
|
||||
* Applies the provided layout by either moving existing groups or creating new groups.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { workbenchInstantiationService, registerTestEditor, TestFileEditorInput, TestEditorPart, TestServiceAccessor, createEditorPart, ITestInstantiationService, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { GroupDirection, GroupsOrder, MergeGroupMode, GroupOrientation, GroupLocation, isEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { GroupDirection, GroupsOrder, MergeGroupMode, GroupOrientation, GroupLocation, isEditorGroup, IEditorGroupsService, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { CloseDirection, IEditorPartOptions, EditorsOrder, EditorInputCapabilities, GroupModelChangeKind, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
@@ -1718,5 +1718,44 @@ suite('EditorGroupsService', () => {
|
||||
assert.strictEqual(rootGroup.isLocked, false);
|
||||
});
|
||||
|
||||
test('maximize editor group', async () => {
|
||||
const instantiationService = workbenchInstantiationService(undefined, disposables);
|
||||
const [part] = await createPart(instantiationService);
|
||||
|
||||
const rootGroup = part.activeGroup;
|
||||
const editorPartSize = part.getSize(rootGroup);
|
||||
|
||||
const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT);
|
||||
const rightBottomGroup = part.addGroup(rightGroup, GroupDirection.DOWN);
|
||||
|
||||
const sizeRootGroup = part.getSize(rootGroup);
|
||||
const sizeRightGroup = part.getSize(rightGroup);
|
||||
const sizeRightBottomGroup = part.getSize(rightBottomGroup);
|
||||
|
||||
let maximizedValue;
|
||||
const maxiizeGroupEventDisposable = part.onDidChangeGroupMaximized((maximized) => {
|
||||
maximizedValue = maximized;
|
||||
});
|
||||
|
||||
part.arrangeGroups(GroupsArrangement.MAXIMIZE, rootGroup);
|
||||
|
||||
// getSize()
|
||||
assert.deepStrictEqual(part.getSize(rootGroup), editorPartSize);
|
||||
assert.deepStrictEqual(part.getSize(rightGroup), { width: 0, height: 0 });
|
||||
assert.deepStrictEqual(part.getSize(rightBottomGroup), { width: 0, height: 0 });
|
||||
|
||||
assert.deepStrictEqual(maximizedValue, true);
|
||||
|
||||
part.toggleMaximizeGroup();
|
||||
|
||||
// Size is restored
|
||||
assert.deepStrictEqual(part.getSize(rootGroup), sizeRootGroup);
|
||||
assert.deepStrictEqual(part.getSize(rightGroup), sizeRightGroup);
|
||||
assert.deepStrictEqual(part.getSize(rightBottomGroup), sizeRightBottomGroup);
|
||||
|
||||
assert.deepStrictEqual(maximizedValue, false);
|
||||
maxiizeGroupEventDisposable.dispose();
|
||||
});
|
||||
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
});
|
||||
|
||||
@@ -1691,7 +1691,7 @@ suite('EditorService', () => {
|
||||
editor = await service.openEditor(input2, { pinned: true, activation: EditorActivation.ACTIVATE }, sideGroup);
|
||||
assert.strictEqual(part.activeGroup, sideGroup);
|
||||
|
||||
part.arrangeGroups(GroupsArrangement.MAXIMIZE);
|
||||
part.arrangeGroups(GroupsArrangement.EXPAND);
|
||||
editor = await service.openEditor(input1, { pinned: true, preserveFocus: true, activation: EditorActivation.RESTORE }, rootGroup);
|
||||
assert.strictEqual(part.activeGroup, sideGroup);
|
||||
});
|
||||
@@ -1711,13 +1711,13 @@ suite('EditorService', () => {
|
||||
assert.strictEqual(part.activeGroup, sideGroup);
|
||||
assert.notStrictEqual(rootGroup, sideGroup);
|
||||
|
||||
part.arrangeGroups(GroupsArrangement.MAXIMIZE, part.activeGroup);
|
||||
part.arrangeGroups(GroupsArrangement.EXPAND, part.activeGroup);
|
||||
|
||||
await rootGroup.closeEditor(input2);
|
||||
assert.strictEqual(part.activeGroup, sideGroup);
|
||||
|
||||
assert(!part.isGroupMaximized(rootGroup));
|
||||
assert(part.isGroupMaximized(part.activeGroup));
|
||||
assert(!part.isGroupExpanded(rootGroup));
|
||||
assert(part.isGroupExpanded(part.activeGroup));
|
||||
});
|
||||
|
||||
test('active editor change / visible editor change events', async function () {
|
||||
|
||||
@@ -824,6 +824,7 @@ export class TestEditorGroupsService implements IEditorGroupsService {
|
||||
onDidMoveGroup: Event<IEditorGroup> = Event.None;
|
||||
onDidChangeGroupIndex: Event<IEditorGroup> = Event.None;
|
||||
onDidChangeGroupLocked: Event<IEditorGroup> = Event.None;
|
||||
onDidChangeGroupMaximized: Event<boolean> = Event.None;
|
||||
onDidLayout: Event<IDimension> = Event.None;
|
||||
onDidChangeEditorPartOptions = Event.None;
|
||||
onDidScroll = Event.None;
|
||||
@@ -850,6 +851,8 @@ export class TestEditorGroupsService implements IEditorGroupsService {
|
||||
getSize(_group: number | IEditorGroup): { width: number; height: number } { return { width: 100, height: 100 }; }
|
||||
setSize(_group: number | IEditorGroup, _size: { width: number; height: number }): void { }
|
||||
arrangeGroups(_arrangement: GroupsArrangement): void { }
|
||||
toggleMaximizeGroup(): void { }
|
||||
toggleExpandGroup(): void { }
|
||||
applyLayout(_layout: EditorGroupLayout): void { }
|
||||
getLayout(): EditorGroupLayout { throw new Error('not implemented'); }
|
||||
setGroupOrientation(_orientation: GroupOrientation): void { }
|
||||
@@ -966,6 +969,8 @@ export class TestEditorGroupAccessor implements IEditorGroupsView {
|
||||
copyGroup(group: number | IEditorGroupView, location: number | IEditorGroupView, direction: GroupDirection): IEditorGroupView { throw new Error('Method not implemented.'); }
|
||||
removeGroup(group: number | IEditorGroupView): void { throw new Error('Method not implemented.'); }
|
||||
arrangeGroups(arrangement: GroupsArrangement, target?: number | IEditorGroupView | undefined): void { throw new Error('Method not implemented.'); }
|
||||
toggleMaximizeGroup(group: number | IEditorGroupView): void { throw new Error('Method not implemented.'); }
|
||||
toggleExpandGroup(group: number | IEditorGroupView): void { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
|
||||
export class TestEditorService implements EditorServiceImpl {
|
||||
|
||||
Reference in New Issue
Block a user