Skip to content

Commit 4b12239

Browse files
authored
Merge pull request flutter#13 from canonical/feature/anchor-to-view
Anchor to client area (view) instead of window frame
2 parents 4a924bd + d9092c5 commit 4b12239

File tree

2 files changed

+82
-45
lines changed

2 files changed

+82
-45
lines changed

shell/platform/windows/client_wrapper/flutter_window_controller.cc

Lines changed: 81 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -61,55 +61,88 @@ applyPositioner(flutter::FlutterWindowPositioner const& positioner,
6161
return GetMonitorInfo(monitor, &mi) ? mi.rcMonitor : RECT{0, 0, 0, 0};
6262
}(parent_hwnd)};
6363

64-
RECT frame;
65-
if (FAILED(DwmGetWindowAttribute(parent_hwnd, DWMWA_EXTENDED_FRAME_BOUNDS,
66-
&frame, sizeof(frame)))) {
67-
GetWindowRect(parent_hwnd, &frame);
68-
}
69-
7064
struct RectF {
7165
double left;
7266
double top;
7367
double right;
7468
double bottom;
7569
};
76-
7770
struct PointF {
7871
double x;
7972
double y;
8073
};
81-
RectF const cropped_frame{
82-
.left = frame.left + positioner.anchor_rect.x * dpr,
83-
.top = frame.top + positioner.anchor_rect.y * dpr,
84-
.right = frame.left +
85-
(positioner.anchor_rect.x + positioner.anchor_rect.width) * dpr,
86-
.bottom =
87-
frame.top +
88-
(positioner.anchor_rect.y + positioner.anchor_rect.height) * dpr};
89-
PointF const center{.x = (cropped_frame.left + cropped_frame.right) / 2.0,
90-
.y = (cropped_frame.top + cropped_frame.bottom) / 2.0};
74+
75+
auto const anchor_rect{[&]() -> RectF {
76+
if (positioner.anchor_rect) {
77+
// If the positioner's anchor rect is not std::nullopt, use it to anchor
78+
// relative to the client area
79+
RECT rect;
80+
GetClientRect(parent_hwnd, &rect);
81+
POINT top_left{rect.left, rect.top};
82+
ClientToScreen(parent_hwnd, &top_left);
83+
POINT bottom_right{rect.right, rect.bottom};
84+
ClientToScreen(parent_hwnd, &bottom_right);
85+
86+
RectF anchor_rect_screen_space{
87+
.left = top_left.x + positioner.anchor_rect->x * dpr,
88+
.top = top_left.y + positioner.anchor_rect->y * dpr,
89+
.right =
90+
top_left.x +
91+
(positioner.anchor_rect->x + positioner.anchor_rect->width) * dpr,
92+
.bottom = top_left.y + (positioner.anchor_rect->y +
93+
positioner.anchor_rect->height) *
94+
dpr};
95+
// Ensure the anchor rect stays within the bounds of the client rect
96+
anchor_rect_screen_space.left = std::clamp(
97+
anchor_rect_screen_space.left, static_cast<double>(top_left.x),
98+
static_cast<double>(bottom_right.x));
99+
anchor_rect_screen_space.top = std::clamp(
100+
anchor_rect_screen_space.top, static_cast<double>(top_left.y),
101+
static_cast<double>(bottom_right.y));
102+
anchor_rect_screen_space.right = std::clamp(
103+
anchor_rect_screen_space.right, static_cast<double>(top_left.x),
104+
static_cast<double>(bottom_right.x));
105+
anchor_rect_screen_space.bottom = std::clamp(
106+
anchor_rect_screen_space.bottom, static_cast<double>(top_left.y),
107+
static_cast<double>(bottom_right.y));
108+
return anchor_rect_screen_space;
109+
} else {
110+
// If the positioner's anchor rect is std::nullopt, create an anchor rect
111+
// that is equal to the window frame area
112+
RECT frame_rect;
113+
DwmGetWindowAttribute(parent_hwnd, DWMWA_EXTENDED_FRAME_BOUNDS,
114+
&frame_rect, sizeof(frame_rect));
115+
return {static_cast<double>(frame_rect.left),
116+
static_cast<double>(frame_rect.top),
117+
static_cast<double>(frame_rect.right),
118+
static_cast<double>(frame_rect.bottom)};
119+
}
120+
}()};
121+
122+
PointF const center{.x = (anchor_rect.left + anchor_rect.right) / 2.0,
123+
.y = (anchor_rect.top + anchor_rect.bottom) / 2.0};
91124
PointF child_size{size.width * dpr, size.height * dpr};
92125
PointF const child_center{child_size.x / 2.0, child_size.y / 2.0};
93126

94127
auto const get_parent_anchor_point{
95128
[&](flutter::FlutterWindowPositioner::Anchor anchor) -> PointF {
96129
switch (anchor) {
97130
case flutter::FlutterWindowPositioner::Anchor::top:
98-
return {center.x, cropped_frame.top};
131+
return {center.x, anchor_rect.top};
99132
case flutter::FlutterWindowPositioner::Anchor::bottom:
100-
return {center.x, cropped_frame.bottom};
133+
return {center.x, anchor_rect.bottom};
101134
case flutter::FlutterWindowPositioner::Anchor::left:
102-
return {cropped_frame.left, center.y};
135+
return {anchor_rect.left, center.y};
103136
case flutter::FlutterWindowPositioner::Anchor::right:
104-
return {cropped_frame.right, center.y};
137+
return {anchor_rect.right, center.y};
105138
case flutter::FlutterWindowPositioner::Anchor::top_left:
106-
return {cropped_frame.left, cropped_frame.top};
139+
return {anchor_rect.left, anchor_rect.top};
107140
case flutter::FlutterWindowPositioner::Anchor::bottom_left:
108-
return {cropped_frame.left, cropped_frame.bottom};
141+
return {anchor_rect.left, anchor_rect.bottom};
109142
case flutter::FlutterWindowPositioner::Anchor::top_right:
110-
return {cropped_frame.right, cropped_frame.top};
143+
return {anchor_rect.right, anchor_rect.top};
111144
case flutter::FlutterWindowPositioner::Anchor::bottom_right:
112-
return {cropped_frame.right, cropped_frame.bottom};
145+
return {anchor_rect.right, anchor_rect.bottom};
113146
default:
114147
return center;
115148
}
@@ -493,22 +526,29 @@ void handleCreatePopupWindow(flutter::MethodCall<> const& call,
493526
static_cast<unsigned int>(height)};
494527

495528
// anchorRect
496-
auto const* const anchor_rect_list{
497-
std::get_if<std::vector<flutter::EncodableValue>>(
498-
&anchor_rect_it->second)};
499-
if (anchor_rect_list->size() != 4 ||
500-
!std::holds_alternative<int>(anchor_rect_list->at(0)) ||
501-
!std::holds_alternative<int>(anchor_rect_list->at(1)) ||
502-
!std::holds_alternative<int>(anchor_rect_list->at(2)) ||
503-
!std::holds_alternative<int>(anchor_rect_list->at(3))) {
504-
result->Error("INVALID_VALUE",
505-
"Values for 'anchorRect' must be of type int.");
506-
return;
529+
std::optional<flutter::FlutterWindowPositioner::Rect> anchor_rect;
530+
if (auto const* const anchor_rect_list{
531+
std::get_if<std::vector<flutter::EncodableValue>>(
532+
&anchor_rect_it->second)}) {
533+
if (anchor_rect_list->size() != 4) {
534+
result->Error(
535+
"INVALID_VALUE",
536+
"Values for 'anchorRect' must be an array of 4 integers.");
537+
return;
538+
} else if (!std::holds_alternative<int>(anchor_rect_list->at(0)) ||
539+
!std::holds_alternative<int>(anchor_rect_list->at(1)) ||
540+
!std::holds_alternative<int>(anchor_rect_list->at(2)) ||
541+
!std::holds_alternative<int>(anchor_rect_list->at(3))) {
542+
result->Error("INVALID_VALUE",
543+
"Values for 'anchorRect' must be of type int.");
544+
return;
545+
}
546+
anchor_rect = flutter::FlutterWindowPositioner::Rect{
547+
.x = std::get<int>(anchor_rect_list->at(0)),
548+
.y = std::get<int>(anchor_rect_list->at(1)),
549+
.width = std::get<int>(anchor_rect_list->at(2)),
550+
.height = std::get<int>(anchor_rect_list->at(3))};
507551
}
508-
auto const anchor_rect_x{std::get<int>(anchor_rect_list->at(0))};
509-
auto const anchor_rect_y{std::get<int>(anchor_rect_list->at(1))};
510-
auto const anchor_rect_width{std::get<int>(anchor_rect_list->at(2))};
511-
auto const anchor_rect_height{std::get<int>(anchor_rect_list->at(3))};
512552

513553
// positionerParentAnchor
514554
auto const* const positioner_parent_anchor{
@@ -582,10 +622,7 @@ void handleCreatePopupWindow(flutter::MethodCall<> const& call,
582622
}
583623

584624
flutter::FlutterWindowPositioner const positioner{
585-
.anchor_rect = {.x = anchor_rect_x,
586-
.y = anchor_rect_y,
587-
.width = anchor_rect_width,
588-
.height = anchor_rect_height},
625+
.anchor_rect = anchor_rect,
589626
.anchor = static_cast<flutter::FlutterWindowPositioner::Anchor>(
590627
*positioner_parent_anchor),
591628
.gravity = gravity,

shell/platform/windows/client_wrapper/include/flutter/win32_window.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ struct FlutterWindowPositioner {
7070
resize_y = 32
7171
};
7272

73-
Rect anchor_rect;
73+
std::optional<Rect> anchor_rect;
7474
Anchor anchor;
7575
Gravity gravity;
7676
Offset offset;

0 commit comments

Comments
 (0)