503 lines
18 KiB
Dart
503 lines
18 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// ignore_for_file: invalid_use_of_internal_member
|
|
|
|
import 'dart:ui';
|
|
import 'package:flutter/src/widgets/_window_positioner.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
group('WindowPlacementTest', () {
|
|
const clientDisplayArea = Rect.fromLTWH(0, 0, 800, 600);
|
|
const clientParentSize = Size(400, 300);
|
|
const clientChildSize = Size(100, 50);
|
|
final clientParentPosition = Offset(
|
|
(clientDisplayArea.width - clientParentSize.width) / 2,
|
|
(clientDisplayArea.height - clientParentSize.height) / 2,
|
|
);
|
|
|
|
final clientParentRect = Rect.fromLTWH(
|
|
clientParentPosition.dx,
|
|
clientParentPosition.dy,
|
|
clientParentSize.width,
|
|
clientParentSize.height,
|
|
);
|
|
|
|
const displayArea = Rect.fromLTWH(0, 0, 640, 480);
|
|
const parentSize = Size(600, 400);
|
|
const childSize = Size(300, 300);
|
|
const rectangleNearRhs = Rect.fromLTWH(590, 20, 10, 20);
|
|
const rectangleNearLeftSide = Rect.fromLTWH(0, 20, 20, 20);
|
|
const rectangleNearAllSides = Rect.fromLTWH(0, 20, 600, 380);
|
|
const rectangleNearBottom = Rect.fromLTWH(20, 380, 20, 20);
|
|
const rectangleNearBothBottomRight = Rect.fromLTWH(400, 380, 200, 20);
|
|
|
|
final parentPosition = Offset(
|
|
(displayArea.width - parentSize.width) / 2,
|
|
(displayArea.height - parentSize.height) / 2,
|
|
);
|
|
|
|
final parentRect = Rect.fromLTWH(
|
|
parentPosition.dx,
|
|
parentPosition.dy,
|
|
parentSize.width,
|
|
parentSize.height,
|
|
);
|
|
|
|
Rect anchorRectFor(Rect rect) => rect.translate(parentPosition.dx, parentPosition.dy);
|
|
Offset onTopEdge(Rect rect, Size childSize) => rect.topLeft - Offset(0, childSize.height);
|
|
Offset onLeftEdge(Rect rect, Size childSize) => rect.topLeft - Offset(childSize.width, 0);
|
|
|
|
test('Client anchors to parent given anchor rectangle right of parent', () {
|
|
const rectSize = 10.0;
|
|
final overlappingRight = Rect.fromCenter(
|
|
center: clientParentRect.topRight.translate(-rectSize / 2, clientParentRect.height / 2),
|
|
width: rectSize,
|
|
height: rectSize,
|
|
);
|
|
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topRight,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(slideY: true, resizeX: true),
|
|
);
|
|
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: clientChildSize,
|
|
anchorRect: overlappingRight,
|
|
parentRect: clientParentRect,
|
|
displayRect: clientDisplayArea,
|
|
);
|
|
|
|
final Offset expectedPosition = overlappingRight.topRight;
|
|
|
|
expect(childRect.topLeft, expectedPosition);
|
|
expect(childRect.size, clientChildSize);
|
|
});
|
|
|
|
test('Client anchors to parent given anchor rectangle above parent', () {
|
|
const rectSize = 10.0;
|
|
final overlappingAbove = Rect.fromCenter(
|
|
center: clientParentRect.topCenter.translate(0, -rectSize / 2),
|
|
width: rectSize,
|
|
height: rectSize,
|
|
);
|
|
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topRight,
|
|
childAnchor: WindowPositionerAnchor.bottomRight,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(slideX: true),
|
|
);
|
|
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: clientChildSize,
|
|
anchorRect: overlappingAbove,
|
|
parentRect: clientParentRect,
|
|
displayRect: clientDisplayArea,
|
|
);
|
|
|
|
final Offset expectedPosition =
|
|
overlappingAbove.bottomRight - Offset(clientChildSize.width, clientChildSize.height);
|
|
|
|
expect(childRect.topLeft, expectedPosition);
|
|
expect(childRect.size, clientChildSize);
|
|
});
|
|
|
|
test('Client anchors to parent given offset right of parent', () {
|
|
const rectSize = 10.0;
|
|
final midRight = Rect.fromLTWH(
|
|
clientParentRect.right - rectSize,
|
|
clientParentRect.center.dy,
|
|
rectSize,
|
|
rectSize,
|
|
);
|
|
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topRight,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
offset: Offset(rectSize, 0),
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(slideY: true, resizeX: true),
|
|
);
|
|
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: clientChildSize,
|
|
anchorRect: midRight,
|
|
parentRect: clientParentRect,
|
|
displayRect: clientDisplayArea,
|
|
);
|
|
|
|
final Offset expectedPosition = midRight.topRight;
|
|
|
|
expect(childRect.topLeft, expectedPosition);
|
|
expect(childRect.size, clientChildSize);
|
|
});
|
|
|
|
test('Client anchors to parent given offset above parent', () {
|
|
const rectSize = 10.0;
|
|
final midTop = Rect.fromLTWH(
|
|
clientParentRect.center.dx,
|
|
clientParentRect.top,
|
|
rectSize,
|
|
rectSize,
|
|
);
|
|
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topRight,
|
|
childAnchor: WindowPositionerAnchor.bottomRight,
|
|
offset: Offset(0, -rectSize),
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(slideX: true),
|
|
);
|
|
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: clientChildSize,
|
|
anchorRect: midTop,
|
|
parentRect: clientParentRect,
|
|
displayRect: clientDisplayArea,
|
|
);
|
|
|
|
final Offset expectedPosition =
|
|
clientParentPosition +
|
|
Offset(clientParentSize.width / 2 + rectSize, 0) -
|
|
Offset(clientChildSize.width, clientChildSize.height);
|
|
|
|
expect(childRect.topLeft, expectedPosition);
|
|
expect(childRect.size, clientChildSize);
|
|
});
|
|
|
|
test('Client anchors to parent given anchor rectangle and offset below left parent', () {
|
|
const rectSize = 10.0;
|
|
final belowLeft = Rect.fromLTWH(
|
|
clientParentRect.left - rectSize,
|
|
clientParentRect.bottom,
|
|
rectSize,
|
|
rectSize,
|
|
);
|
|
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.bottomLeft,
|
|
childAnchor: WindowPositionerAnchor.topRight,
|
|
offset: Offset(-rectSize, rectSize),
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(resizeX: true, resizeY: true),
|
|
);
|
|
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: clientChildSize,
|
|
anchorRect: belowLeft,
|
|
parentRect: clientParentRect,
|
|
displayRect: clientDisplayArea,
|
|
);
|
|
|
|
final Offset expectedPosition =
|
|
clientParentRect.bottomLeft - Offset(clientChildSize.width, 0);
|
|
|
|
expect(childRect.topLeft, expectedPosition);
|
|
expect(childRect.size, clientChildSize);
|
|
});
|
|
|
|
group('Can attach by every anchor given no constraint adjustment', () {
|
|
Offset anchorPositionFor(WindowPositionerAnchor anchor, Rect rect) {
|
|
return switch (anchor) {
|
|
WindowPositionerAnchor.center => rect.center,
|
|
WindowPositionerAnchor.top => rect.topCenter,
|
|
WindowPositionerAnchor.bottom => rect.bottomCenter,
|
|
WindowPositionerAnchor.left => rect.centerLeft,
|
|
WindowPositionerAnchor.right => rect.centerRight,
|
|
WindowPositionerAnchor.topLeft => rect.topLeft,
|
|
WindowPositionerAnchor.bottomLeft => rect.bottomLeft,
|
|
WindowPositionerAnchor.topRight => rect.topRight,
|
|
WindowPositionerAnchor.bottomRight => rect.bottomRight,
|
|
};
|
|
}
|
|
|
|
for (final WindowPositionerAnchor parentAnchor in WindowPositionerAnchor.values) {
|
|
for (final WindowPositionerAnchor childAnchor in WindowPositionerAnchor.values) {
|
|
test('parent: $parentAnchor, child: $childAnchor', () {
|
|
final Rect anchorRect = anchorRectFor(const Rect.fromLTWH(100, 50, 20, 20));
|
|
final positioner = WindowPositioner(
|
|
parentAnchor: parentAnchor,
|
|
childAnchor: childAnchor,
|
|
);
|
|
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
expect(
|
|
anchorPositionFor(childAnchor, childRect),
|
|
anchorPositionFor(parentAnchor, anchorRect),
|
|
);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
test('Placement is flipped given anchor rectangle near right side and offset', () {
|
|
const xOffset = 42.0;
|
|
const yOffset = 13.0;
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topRight,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
offset: Offset(xOffset, yOffset),
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(flipX: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearRhs);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
final Offset expectedPosition =
|
|
onLeftEdge(anchorRect, childSize) + const Offset(-xOffset, yOffset);
|
|
|
|
expect(childRect.topLeft, expectedPosition);
|
|
});
|
|
|
|
test('Placement is flipped given anchor rectangle near bottom and offset', () {
|
|
const xOffset = 42.0;
|
|
const yOffset = 13.0;
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.bottomLeft,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
offset: Offset(xOffset, yOffset),
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(flipY: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearBottom);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
final Offset expectedPosition =
|
|
onTopEdge(anchorRect, childSize) + const Offset(xOffset, -yOffset);
|
|
|
|
expect(childRect.topLeft, expectedPosition);
|
|
});
|
|
|
|
test('Placement is flipped both ways given anchor rectangle near bottom right and offset', () {
|
|
const xOffset = 42.0;
|
|
const yOffset = 13.0;
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.bottomRight,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
offset: Offset(xOffset, yOffset),
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(flipX: true, flipY: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearBothBottomRight);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
final Offset expectedPosition =
|
|
anchorRect.topLeft -
|
|
Offset(childSize.width, childSize.height) -
|
|
const Offset(xOffset, yOffset);
|
|
|
|
expect(childRect.topLeft, expectedPosition);
|
|
});
|
|
|
|
test('Placement can slide in X given anchor rectangle near right side', () {
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topRight,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(slideX: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearRhs);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
expect(childRect.topLeft.dx, displayArea.right - childSize.width);
|
|
});
|
|
|
|
test('Placement can slide in X given anchor rectangle near left side', () {
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topLeft,
|
|
childAnchor: WindowPositionerAnchor.topRight,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(slideX: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearLeftSide);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
expect(childRect.topLeft.dx, displayArea.left);
|
|
});
|
|
|
|
test('Placement can slide in Y given anchor rectangle near bottom', () {
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.bottomLeft,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(slideY: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearBottom);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
expect(childRect.topLeft.dy, displayArea.bottom - childSize.height);
|
|
});
|
|
|
|
test('Placement can slide in Y given anchor rectangle near top', () {
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topLeft,
|
|
childAnchor: WindowPositionerAnchor.bottomLeft,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(slideY: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearAllSides);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
expect(childRect.topLeft.dy, displayArea.top);
|
|
});
|
|
|
|
test('Placement can slide in X and Y given anchor rectangle near bottom right and offset', () {
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.bottomLeft,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(slideX: true, slideY: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearBothBottomRight);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
final expectedPosition = Offset(
|
|
displayArea.right - childSize.width,
|
|
displayArea.bottom - childSize.height,
|
|
);
|
|
|
|
expect(childRect.topLeft, expectedPosition);
|
|
});
|
|
|
|
test('Placement can resize in X given anchor rectangle near right side', () {
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topRight,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(resizeX: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearRhs);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
expect(childRect.width, displayArea.right - (anchorRect.left + anchorRect.width));
|
|
});
|
|
|
|
test('Placement can resize in X given anchor rectangle near left side', () {
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topLeft,
|
|
childAnchor: WindowPositionerAnchor.topRight,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(resizeX: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearLeftSide);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
expect(childRect.width, anchorRect.left - displayArea.left);
|
|
});
|
|
|
|
test('Placement can resize in Y given anchor rectangle near bottom', () {
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.bottomLeft,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(resizeY: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearAllSides);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
expect(childRect.height, displayArea.bottom - (anchorRect.top + anchorRect.height));
|
|
});
|
|
|
|
test('Placement can resize in Y given anchor rectangle near top', () {
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.topLeft,
|
|
childAnchor: WindowPositionerAnchor.bottomLeft,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(resizeY: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearAllSides);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
expect(childRect.height, anchorRect.top - displayArea.top);
|
|
});
|
|
|
|
test('Placement can resize in X and Y given anchor rectangle near bottom right and offset', () {
|
|
const positioner = WindowPositioner(
|
|
parentAnchor: WindowPositionerAnchor.bottomRight,
|
|
childAnchor: WindowPositionerAnchor.topLeft,
|
|
constraintAdjustment: WindowPositionerConstraintAdjustment(resizeX: true, resizeY: true),
|
|
);
|
|
|
|
final Rect anchorRect = anchorRectFor(rectangleNearBothBottomRight);
|
|
final Rect childRect = positioner.placeWindow(
|
|
childSize: childSize,
|
|
anchorRect: anchorRect,
|
|
parentRect: parentRect,
|
|
displayRect: displayArea,
|
|
);
|
|
|
|
final expectedSize = Size(
|
|
displayArea.right - (anchorRect.left + anchorRect.width),
|
|
displayArea.bottom - (anchorRect.top + anchorRect.height),
|
|
);
|
|
|
|
expect(childRect.size, expectedSize);
|
|
});
|
|
});
|
|
}
|