diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index ae6e39c7a666d512c9a33b01d495c3ed74601817..291fe64a6629e03b5aac5de0a9e3f88927b0588a 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -42,7 +42,7 @@ function getNode(node: GridNode, location: number[]): GridNo return getNode(node.children[index], rest); } -function getViews(node: GridNode): T[] { +function getAllViews(node: GridNode): T[] { const result: T[] = []; function collectViews(node: GridNode): void { @@ -246,61 +246,57 @@ export class Grid implements IDisposable { getNeighborViews(view: T, direction: Direction, wrap: boolean = false): T[] { const location = this.getViewLocation(view); - const locationOrientation = getLocationOrientation(this.orientation, location); - const directionOrientation = getDirectionOrientation(direction); - const diff = direction === Direction.Up || direction === Direction.Left ? -1 : 1; - const root = this.getViews(); - const [parentLocation, index] = tail2(location); - - if (locationOrientation === directionOrientation) { - const parent = getNode(root, parentLocation); - - if (!isGridBranchNode(parent)) { - throw new Error('Invalid location'); - } + const result = this._getNeighborViews(location, direction); - let neighborIndex = index + diff; - - if (!wrap && (neighborIndex === -1 || neighborIndex >= parent.children.length)) { - return []; - } + if (result.length > 0 || !wrap) { + return result; + } - neighborIndex = neighborIndex % parent.children.length; - const neighbor = parent.children[neighborIndex]; + const directionOrientation = getDirectionOrientation(direction); + const ancestorLocation = location.slice(0, directionOrientation === this.orientation ? 0 : 1); + const root = this.getViews(); + const ancestorParent = getNode(root, ancestorLocation); - return getViews(neighbor); + if (!isGridBranchNode(ancestorParent)) { + return [ancestorParent.view]; + } + if (direction === Direction.Up || direction === Direction.Left) { + return getAllViews(ancestorParent.children[ancestorParent.children.length - 1]); } else { - if (parentLocation.length === 0) { - return []; - } - - const [grandParentLocation, parentIndex] = tail2(parentLocation); - const grandParent = getNode(root, grandParentLocation); + return getAllViews(ancestorParent.children[0]); + } + } - if (!isGridBranchNode(grandParent)) { - throw new Error('Invalid location'); - } + _getNeighborViews(location: number[], direction: Direction): T[] { + if (location.length === 0) { + return []; + } - let uncleIndex = parentIndex + diff; + const locationOrientation = getLocationOrientation(this.orientation, location); + const directionOrientation = getDirectionOrientation(direction); + const [parentLocation, index] = tail2(location); - if (!wrap && (uncleIndex === -1 || uncleIndex >= grandParent.children.length)) { - return []; - } + if (locationOrientation !== directionOrientation) { + return this._getNeighborViews(parentLocation, direction); + } - uncleIndex = uncleIndex % grandParent.children.length; + const root = this.getViews(); + const parent = getNode(root, parentLocation); - const uncle = grandParent.children[uncleIndex]; + if (!isGridBranchNode(parent)) { + throw new Error('Invalid location'); + } - if (!isGridBranchNode(uncle)) { - return [uncle.view]; - } + const diff = direction === Direction.Up || direction === Direction.Left ? -1 : 1; + let neighborIndex = index + diff; - const uncleLocation = [...grandParentLocation, uncleIndex]; - const range = this.gridview.getViewRange(location); - const cousinIndexes = this.gridview.getChildrenInRange(uncleLocation, range); - return cousinIndexes.reduce((r, i) => [...r, ...getViews(uncle.children[i])], [] as T[]); + if (neighborIndex === -1 || neighborIndex >= parent.children.length) { + return this._getNeighborViews(parentLocation, direction); } + + const neighbor = parent.children[neighborIndex]; + return getAllViews(neighbor); } private getViewLocation(view: T): number[] { diff --git a/src/vs/base/test/browser/ui/grid/grid.test.ts b/src/vs/base/test/browser/ui/grid/grid.test.ts index 15fa42d59635672a8ce375986e3da9d90f806a15..a26546928c2c7975b8ecc2ee43ac8575d839edd0 100644 --- a/src/vs/base/test/browser/ui/grid/grid.test.ts +++ b/src/vs/base/test/browser/ui/grid/grid.test.ts @@ -288,6 +288,103 @@ suite('Grid', function () { assert.deepEqual(view2.size, [800, 200]); assert.deepEqual(view4.size, [800, 200]); }); + + test('getNeighborViews should work on single view layout', function () { + const view1 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE); + const grid = new Grid(container, view1); + grid.layout(800, 600); + + assert.deepEqual(grid.getNeighborViews(view1, Direction.Up), []); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Right), []); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Down), []); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Left), []); + + assert.deepEqual(grid.getNeighborViews(view1, Direction.Up, true), [view1]); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Right, true), [view1]); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Down, true), [view1]); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Left, true), [view1]); + }); + + test('getNeighborViews should work on simple layout', function () { + const view1 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE); + const grid = new Grid(container, view1); + grid.layout(800, 600); + + const view2 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE); + grid.addView(view2, Sizing.Distribute, view1, Direction.Down); + + const view3 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE); + grid.addView(view3, Sizing.Distribute, view2, Direction.Down); + + assert.deepEqual(grid.getNeighborViews(view1, Direction.Up), []); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Right), []); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Down), [view2]); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Left), []); + + assert.deepEqual(grid.getNeighborViews(view1, Direction.Up, true), [view3]); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Right, true), [view1]); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Down, true), [view2]); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Left, true), [view1]); + + assert.deepEqual(grid.getNeighborViews(view2, Direction.Up), [view1]); + assert.deepEqual(grid.getNeighborViews(view2, Direction.Right), []); + assert.deepEqual(grid.getNeighborViews(view2, Direction.Down), [view3]); + assert.deepEqual(grid.getNeighborViews(view2, Direction.Left), []); + + assert.deepEqual(grid.getNeighborViews(view2, Direction.Up, true), [view1]); + assert.deepEqual(grid.getNeighborViews(view2, Direction.Right, true), [view2]); + assert.deepEqual(grid.getNeighborViews(view2, Direction.Down, true), [view3]); + assert.deepEqual(grid.getNeighborViews(view2, Direction.Left, true), [view2]); + + assert.deepEqual(grid.getNeighborViews(view3, Direction.Up), [view2]); + assert.deepEqual(grid.getNeighborViews(view3, Direction.Right), []); + assert.deepEqual(grid.getNeighborViews(view3, Direction.Down), []); + assert.deepEqual(grid.getNeighborViews(view3, Direction.Left), []); + + assert.deepEqual(grid.getNeighborViews(view3, Direction.Up, true), [view2]); + assert.deepEqual(grid.getNeighborViews(view3, Direction.Right, true), [view3]); + assert.deepEqual(grid.getNeighborViews(view3, Direction.Down, true), [view1]); + assert.deepEqual(grid.getNeighborViews(view3, Direction.Left, true), [view3]); + }); + + test('getNeighborViews should work on a complex layout', function () { + const view1 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE); + const grid = new Grid(container, view1); + grid.layout(800, 600); + + const view2 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE); + grid.addView(view2, Sizing.Distribute, view1, Direction.Down); + + const view3 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE); + grid.addView(view3, Sizing.Distribute, view2, Direction.Down); + + const view4 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE); + grid.addView(view4, Sizing.Distribute, view2, Direction.Right); + + const view5 = new TestView(50, Number.MAX_VALUE, 50, Number.MAX_VALUE); + grid.addView(view5, Sizing.Distribute, view4, Direction.Down); + + assert.deepEqual(grid.getNeighborViews(view1, Direction.Up), []); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Right), []); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Down), [view2, view4, view5]); + assert.deepEqual(grid.getNeighborViews(view1, Direction.Left), []); + assert.deepEqual(grid.getNeighborViews(view2, Direction.Up), [view1]); + assert.deepEqual(grid.getNeighborViews(view2, Direction.Right), [view4, view5]); + assert.deepEqual(grid.getNeighborViews(view2, Direction.Down), [view3]); + assert.deepEqual(grid.getNeighborViews(view2, Direction.Left), []); + assert.deepEqual(grid.getNeighborViews(view4, Direction.Up), [view1]); + assert.deepEqual(grid.getNeighborViews(view4, Direction.Right), []); + assert.deepEqual(grid.getNeighborViews(view4, Direction.Down), [view5]); + assert.deepEqual(grid.getNeighborViews(view4, Direction.Left), [view2]); + assert.deepEqual(grid.getNeighborViews(view5, Direction.Up), [view4]); + assert.deepEqual(grid.getNeighborViews(view5, Direction.Right), []); + assert.deepEqual(grid.getNeighborViews(view5, Direction.Down), [view3]); + assert.deepEqual(grid.getNeighborViews(view5, Direction.Left), [view2]); + assert.deepEqual(grid.getNeighborViews(view3, Direction.Up), [view2, view4, view5]); + assert.deepEqual(grid.getNeighborViews(view3, Direction.Right), []); + assert.deepEqual(grid.getNeighborViews(view3, Direction.Down), []); + assert.deepEqual(grid.getNeighborViews(view3, Direction.Left), []); + }); }); class TestSerializableView extends TestView implements ISerializableView {