Skip to content

Implement positioning logic in Dart + nits #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: multiwindow_ffi_popup
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions engine/src/flutter/shell/platform/linux/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,10 @@ source_set("flutter_linux_sources") {
"fl_view.cc",
"fl_view_accessible.cc",
"fl_window_state_monitor.cc",
"fl_windowing_channel.cc",
"fl_windowing_handler.cc",
"fl_windowing.cc",
"fl_windowing.h",
"fl_windowing_channel.cc",
"fl_windowing_handler.cc",
"key_mapping.g.cc",
]

Expand Down
2 changes: 1 addition & 1 deletion examples/multi_window_ref_app/lib/app/main_window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class _ActiveWindowsTable extends StatelessWidget {
}
},
cells: [
DataCell(Text('$controller.controller.rootView.viewId')),
DataCell(Text('${controller.controller.rootView.viewId}')),
DataCell(
ListenableBuilder(
listenable: controller.controller,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ void showRegularWindowEditDialog(BuildContext context,
String? title =
titleController.text.isEmpty ? null : titleController.text;

onSave?.call(width, height, title, selectedState);
onSave?.call(width, height, title, WindowState.restored);
if (selectedState != WindowState.restored) {
onSave?.call(null, null, title, selectedState);
}
Navigator.of(context).pop();
},
child: Text("Save"),
Expand Down
3 changes: 1 addition & 2 deletions packages/flutter/lib/src/widgets/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ enum WindowState {
abstract class WindowController with ChangeNotifier {
@protected
/// Sets the view associated with this window.
// ignore: use_setters_to_change_properties
void setView(FlutterView view) {
set view(FlutterView view) {
_view = view;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/flutter/lib/src/widgets/window_linux.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class PopupWindowControllerLinux extends PopupWindowController {
final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere(
(FlutterView view) => view.viewId == viewId,
);
setView(flutterView);
view = flutterView;
}

Pointer<Void> getWindowHandle() {
Expand Down Expand Up @@ -142,7 +142,7 @@ class RegularWindowControllerLinux extends RegularWindowController {
final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere(
(FlutterView view) => view.viewId == viewId,
);
setView(flutterView);
view = flutterView;
if (title != null) {
setTitle(title);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter/lib/src/widgets/window_macos.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class PopupWindowControllerMacOS extends PopupWindowController {
final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere(
(FlutterView view) => view.viewId == viewId,
);
setView(flutterView);
view = flutterView;
// This calculates absolute position using the [WindowPositioner]. On Linux it would instead convert
// the positioner xdg_positioner and pass it to the window manager.
_positionWindow(parentWindow, position, anchorRect);
Expand Down Expand Up @@ -164,7 +164,7 @@ class RegularWindowControllerMacOS extends RegularWindowController {
final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere(
(FlutterView view) => view.viewId == viewId,
);
setView(flutterView);
view = flutterView;
if (title != null) {
setTitle(title);
}
Expand Down
175 changes: 143 additions & 32 deletions packages/flutter/lib/src/widgets/window_positioning.dart
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class WindowPositioner {
final Set<WindowPositionerConstraintAdjustment> constraintAdjustment;

/// Computes the screen-space rectangle for a child window placed according to
/// the this position. [childSize] is the frame size of the child window.
/// this [WindowPositioner]. [childSize] is the frame size of the child window.
/// [anchorRect] is the rectangle relative to which the child window is placed.
/// [parentRect] is the parent window's rectangle. [outputRect] is the output
/// display area where the child window will be placed. All sizes and rectangles
Expand All @@ -233,91 +233,202 @@ class WindowPositioner {
return defaultResult;
}
}
// TODO: Finish

if (constraintAdjustment.contains(WindowPositionerConstraintAdjustment.flipX)) {
final Offset result =
_constraintTo(
parentRect,
parentAnchor.flipX().anchorPositionFor(anchorRect) + _flipX(offset),
) +
childAnchor.flipX().offsetFor(childSize);
if (_rectContains(outputRect, result & childSize)) {
return result & childSize;
}
}

if (constraintAdjustment.contains(WindowPositionerConstraintAdjustment.flipY)) {
final Offset result =
_constraintTo(
parentRect,
parentAnchor.flipY().anchorPositionFor(anchorRect) + _flipY(offset),
) +
childAnchor.flipY().offsetFor(childSize);
if (_rectContains(outputRect, result & childSize)) {
return result & childSize;
}
}

if (constraintAdjustment.containsAll(<WindowPositionerConstraintAdjustment>{
WindowPositionerConstraintAdjustment.flipX,
WindowPositionerConstraintAdjustment.flipY,
})) {
final Offset result =
_constraintTo(
parentRect,
parentAnchor.flipY().flipX().anchorPositionFor(anchorRect) + _flipX(_flipY(offset)),
) +
childAnchor.flipY().flipX().offsetFor(childSize);
if (_rectContains(outputRect, result & childSize)) {
return result & childSize;
}
}

{
Offset result =
_constraintTo(parentRect, parentAnchor.anchorPositionFor(anchorRect) + offset) +
childAnchor.offsetFor(childSize);

if (constraintAdjustment.contains(WindowPositionerConstraintAdjustment.slideX)) {
final double leftOverhang = result.dx - outputRect.left;
final double rightOverhang = result.dx + childSize.width - outputRect.right;
if (leftOverhang < 0.0) {
result = result.translate(-leftOverhang, 0.0);
} else if (rightOverhang > 0.0) {
result = result.translate(-rightOverhang, 0.0);
}
}

if (constraintAdjustment.contains(WindowPositionerConstraintAdjustment.slideY)) {
final double topOverhang = result.dy - outputRect.top;
final double bottomOverhang = result.dy + childSize.height - outputRect.bottom;
if (topOverhang < 0.0) {
result = result.translate(0.0, -topOverhang);
} else if (bottomOverhang > 0.0) {
result = result.translate(0.0, -bottomOverhang);
}
}

if (_rectContains(outputRect, result & childSize)) {
return result & childSize;
}
}

{
Offset result =
_constraintTo(parentRect, parentAnchor.anchorPositionFor(anchorRect) + offset) +
childAnchor.offsetFor(childSize);

if (constraintAdjustment.contains(WindowPositionerConstraintAdjustment.resizeX)) {
final double leftOverhang = result.dx - outputRect.left;
final double rightOverhang = result.dx + childSize.width - outputRect.right;
if (leftOverhang < 0.0) {
result = result.translate(-leftOverhang, 0.0);
childSize = Size(childSize.width + leftOverhang, childSize.height);
}
if (rightOverhang > 0.0) {
childSize = Size(childSize.width - rightOverhang, childSize.height);
}
}

if (constraintAdjustment.contains(WindowPositionerConstraintAdjustment.resizeY)) {
final double topOverhang = result.dy - outputRect.top;
final double bottomOverhang = result.dy + childSize.height - outputRect.bottom;
if (topOverhang < 0.0) {
result = result.translate(0.0, -topOverhang);
childSize = Size(childSize.width, childSize.height + topOverhang);
}
if (bottomOverhang > 0.0) {
childSize = Size(childSize.width, childSize.height - bottomOverhang);
}
}

if (_rectContains(outputRect, result & childSize)) {
return result & childSize;
}
}

return defaultResult;
}
}

extension on WindowPositionerAnchor {
WindowPositionerAnchor flipX() {
switch (this) {
case WindowPositionerAnchor.topLeft:
return WindowPositionerAnchor.topRight;
case WindowPositionerAnchor.topRight:
return WindowPositionerAnchor.topLeft;
case WindowPositionerAnchor.center:
return WindowPositionerAnchor.center;
case WindowPositionerAnchor.top:
return WindowPositionerAnchor.top;
case WindowPositionerAnchor.bottom:
return WindowPositionerAnchor.bottom;
case WindowPositionerAnchor.left:
return WindowPositionerAnchor.right;
case WindowPositionerAnchor.right:
return WindowPositionerAnchor.left;
case WindowPositionerAnchor.topLeft:
return WindowPositionerAnchor.topRight;
case WindowPositionerAnchor.bottomLeft:
return WindowPositionerAnchor.bottomRight;
case WindowPositionerAnchor.topRight:
return WindowPositionerAnchor.topLeft;
case WindowPositionerAnchor.bottomRight:
return WindowPositionerAnchor.bottomLeft;
default:
return this;
}
}

WindowPositionerAnchor flipY() {
switch (this) {
case WindowPositionerAnchor.topLeft:
return WindowPositionerAnchor.bottomLeft;
case WindowPositionerAnchor.center:
return WindowPositionerAnchor.center;
case WindowPositionerAnchor.top:
return WindowPositionerAnchor.bottom;
case WindowPositionerAnchor.topRight:
return WindowPositionerAnchor.bottomRight;
case WindowPositionerAnchor.bottomLeft:
return WindowPositionerAnchor.topLeft;
case WindowPositionerAnchor.bottom:
return WindowPositionerAnchor.top;
case WindowPositionerAnchor.left:
return WindowPositionerAnchor.left;
case WindowPositionerAnchor.right:
return WindowPositionerAnchor.right;
case WindowPositionerAnchor.topLeft:
return WindowPositionerAnchor.bottomLeft;
case WindowPositionerAnchor.bottomLeft:
return WindowPositionerAnchor.topLeft;
case WindowPositionerAnchor.topRight:
return WindowPositionerAnchor.bottomRight;
case WindowPositionerAnchor.bottomRight:
return WindowPositionerAnchor.topRight;
default:
return this;
}
}

Offset offsetFor(Size size) {
switch (this) {
case WindowPositionerAnchor.topLeft:
return Offset.zero;
case WindowPositionerAnchor.center:
return Offset(-size.width / 2.0, -size.height / 2.0);
case WindowPositionerAnchor.top:
return Offset(-size.width / 2.0, 0.0);
case WindowPositionerAnchor.topRight:
return Offset(-size.width, 0.0);
case WindowPositionerAnchor.bottom:
return Offset(-size.width / 2.0, -1.0 * size.height);
case WindowPositionerAnchor.left:
return Offset(0.0, -size.height / 2.0);
case WindowPositionerAnchor.center:
return Offset(-size.width / 2.0, -size.height / 2.0);
case WindowPositionerAnchor.right:
return Offset(-1.0 * size.width, -size.height / 2.0);
case WindowPositionerAnchor.topLeft:
return Offset.zero;
case WindowPositionerAnchor.bottomLeft:
return Offset(0.0, -1.0 * size.height);
case WindowPositionerAnchor.bottom:
return Offset(-size.width / 2.0, -1.0 * size.height);
case WindowPositionerAnchor.topRight:
return Offset(-size.width, 0.0);
case WindowPositionerAnchor.bottomRight:
return Offset(-1.0 * size.width, -1.0 * size.height);
}
}

Offset anchorPositionFor(Rect rect) {
switch (this) {
case WindowPositionerAnchor.topLeft:
return rect.topLeft;
case WindowPositionerAnchor.center:
return rect.center;
case WindowPositionerAnchor.top:
return rect.topCenter;
case WindowPositionerAnchor.topRight:
return rect.topRight;
case WindowPositionerAnchor.bottom:
return rect.bottomCenter;
case WindowPositionerAnchor.left:
return rect.centerLeft;
case WindowPositionerAnchor.center:
return rect.center;
case WindowPositionerAnchor.right:
return rect.centerRight;
case WindowPositionerAnchor.topLeft:
return rect.topLeft;
case WindowPositionerAnchor.bottomLeft:
return rect.bottomLeft;
case WindowPositionerAnchor.bottom:
return rect.bottomCenter;
case WindowPositionerAnchor.topRight:
return rect.topRight;
case WindowPositionerAnchor.bottomRight:
return rect.bottomRight;
}
Expand Down
5 changes: 2 additions & 3 deletions packages/flutter/lib/src/widgets/window_win32.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ class RegularWindowControllerWin32 extends RegularWindowController
}) : _owner = owner,
_delegate = delegate,
super.empty() {
owner.addMessageHandler(this);
final Pointer<_WindowCreationRequest> request =
ffi.calloc<_WindowCreationRequest>()
..ref.width = size.width
Expand All @@ -128,7 +127,8 @@ class RegularWindowControllerWin32 extends RegularWindowController
final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere(
(FlutterView view) => view.viewId == viewId,
);
setView(flutterView);
view = flutterView;
owner.addMessageHandler(this);
}

@override
Expand Down Expand Up @@ -199,7 +199,6 @@ class RegularWindowControllerWin32 extends RegularWindowController
return;
}
_destroyWindow(getWindowHandle());
;
_destroyed = true;
_delegate.onWindowDestroyed();
_owner.removeMessageHandler(this);
Expand Down
Loading