Skip to content

Commit 6ca09cd

Browse files
authored
Merge pull request #24 from canonical/regular-windows-modify
feature: implemented modifyRegular + fixed a bug where a widget tree was mandatory on accident
2 parents 9bd57f3 + 0dc1c6b commit 6ca09cd

File tree

16 files changed

+954
-305
lines changed

16 files changed

+954
-305
lines changed

engine/src/flutter/shell/platform/common/geometry.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class Size {
4545
bool operator==(const Size& other) const {
4646
return width_ == other.width_ && height_ == other.height_;
4747
}
48+
bool operator!=(const Size& other) const { return !(*this == other); }
4849

4950
private:
5051
double width_ = 0.0;

engine/src/flutter/shell/platform/common/windowing.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@ struct WindowCreationSettings {
7575
std::optional<WindowState> state;
7676
};
7777

78+
// Settings for modifying a Flutter window.
79+
struct WindowModificationSettings {
80+
// The new requested size, in logical coordinates.
81+
std::optional<Size> size;
82+
// The new window title.
83+
std::optional<std::string> title;
84+
// The new window state.
85+
std::optional<WindowState> state;
86+
};
87+
7888
// Window metadata returned as the result of creating a Flutter window.
7989
struct WindowMetadata {
8090
// The ID of the view used for this window, which is unique to each window.

engine/src/flutter/shell/platform/windows/flutter_host_window.cc

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -213,16 +213,15 @@ bool IsClassRegistered(LPCWSTR class_name) {
213213
}
214214

215215
// Convert std::string to std::wstring.
216-
std::wstring StringToWstring(std::string const& str) {
216+
std::wstring StringToWstring(std::string_view str) {
217217
if (str.empty()) {
218218
return {};
219219
}
220220
if (int buffer_size =
221-
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0)) {
221+
MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), nullptr, 0)) {
222222
std::wstring wide_str(buffer_size, L'\0');
223-
if (MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wide_str[0],
223+
if (MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), &wide_str[0],
224224
buffer_size)) {
225-
wide_str.pop_back();
226225
return wide_str;
227226
}
228227
}
@@ -447,28 +446,12 @@ FlutterHostWindow* FlutterHostWindow::GetThisFromHandle(HWND hwnd) {
447446
GetWindowLongPtr(hwnd, GWLP_USERDATA));
448447
}
449448

450-
WindowArchetype FlutterHostWindow::GetArchetype() const {
451-
return archetype_;
452-
}
453-
454-
FlutterViewId FlutterHostWindow::GetFlutterViewId() const {
455-
return view_controller_->view()->view_id();
456-
};
457-
458-
WindowState FlutterHostWindow::GetState() const {
459-
return state_;
460-
}
461-
462449
HWND FlutterHostWindow::GetWindowHandle() const {
463450
return window_handle_;
464451
}
465452

466-
void FlutterHostWindow::SetQuitOnClose(bool quit_on_close) {
467-
quit_on_close_ = quit_on_close;
468-
}
469-
470-
bool FlutterHostWindow::GetQuitOnClose() const {
471-
return quit_on_close_;
453+
WindowState FlutterHostWindow::GetState() const {
454+
return state_;
472455
}
473456

474457
void FlutterHostWindow::FocusViewOf(FlutterHostWindow* window) {
@@ -587,6 +570,19 @@ LRESULT FlutterHostWindow::HandleMessage(HWND hwnd,
587570
return DefWindowProc(hwnd, message, wparam, lparam);
588571
}
589572

573+
void FlutterHostWindow::SetClientSize(Size const& client_size) const {
574+
WINDOWINFO window_info = {.cbSize = sizeof(WINDOWINFO)};
575+
GetWindowInfo(window_handle_, &window_info);
576+
577+
std::optional<Size> const window_size = GetWindowSizeForClientSize(
578+
client_size, min_size_, max_size_, window_info.dwStyle,
579+
window_info.dwExStyle, nullptr);
580+
581+
Size const size = window_size.value_or(client_size);
582+
SetWindowPos(window_handle_, NULL, 0, 0, size.width(), size.height(),
583+
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
584+
}
585+
590586
void FlutterHostWindow::SetChildContent(HWND content) {
591587
child_content_ = content;
592588
SetParent(content, window_handle_);
@@ -597,4 +593,28 @@ void FlutterHostWindow::SetChildContent(HWND content) {
597593
client_rect.bottom - client_rect.top, true);
598594
}
599595

596+
void FlutterHostWindow::SetState(WindowState state) {
597+
WINDOWPLACEMENT window_placement = {.length = sizeof(WINDOWPLACEMENT)};
598+
GetWindowPlacement(window_handle_, &window_placement);
599+
window_placement.showCmd = [&]() {
600+
switch (state) {
601+
case WindowState::kRestored:
602+
return SW_RESTORE;
603+
case WindowState::kMaximized:
604+
return SW_MAXIMIZE;
605+
case WindowState::kMinimized:
606+
return SW_MINIMIZE;
607+
default:
608+
FML_UNREACHABLE();
609+
};
610+
}();
611+
SetWindowPlacement(window_handle_, &window_placement);
612+
state_ = state;
613+
}
614+
615+
void FlutterHostWindow::SetTitle(std::string_view title) const {
616+
std::wstring title_wide = StringToWstring(title);
617+
SetWindowText(window_handle_, title_wide.c_str());
618+
}
619+
600620
} // namespace flutter

engine/src/flutter/shell/platform/windows/flutter_host_window.h

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,13 @@ class FlutterHostWindow {
3333
// Returns the instance pointer for |hwnd| or nulllptr if invalid.
3434
static FlutterHostWindow* GetThisFromHandle(HWND hwnd);
3535

36-
// Returns the window archetype.
37-
WindowArchetype GetArchetype() const;
38-
39-
// Returns the hosted Flutter view's ID.
40-
FlutterViewId GetFlutterViewId() const;
41-
4236
// Returns the current window state.
4337
WindowState GetState() const;
4438

4539
// Returns the backing window handle, or nullptr if the native window is not
4640
// created or has already been destroyed.
4741
HWND GetWindowHandle() const;
4842

49-
// Sets whether closing this window will quit the application.
50-
void SetQuitOnClose(bool quit_on_close);
51-
52-
// Returns whether closing this window will quit the application.
53-
bool GetQuitOnClose() const;
54-
5543
private:
5644
friend FlutterHostWindowController;
5745

@@ -69,9 +57,18 @@ class FlutterHostWindow {
6957
// inheriting classes can handle.
7058
LRESULT HandleMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
7159

60+
// Resizes the window to accommodate a client area of the given |client_size|.
61+
void SetClientSize(Size const& client_size) const;
62+
7263
// Inserts |content| into the window tree.
7364
void SetChildContent(HWND content);
7465

66+
// Sets the window state.
67+
void SetState(WindowState state);
68+
69+
// Sets the window title.
70+
void SetTitle(std::string_view title) const;
71+
7572
// Controller for this window.
7673
FlutterHostWindowController* const window_controller_;
7774

engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "flutter/shell/platform/common/windowing.h"
1010
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
11+
#include "flutter/shell/platform/windows/flutter_windows_view_controller.h"
1112

1213
namespace flutter {
1314

@@ -38,39 +39,76 @@ FlutterHostWindowController::~FlutterHostWindowController() {
3839
std::optional<WindowMetadata> FlutterHostWindowController::CreateHostWindow(
3940
WindowCreationSettings const& settings) {
4041
auto window = std::make_unique<FlutterHostWindow>(this, settings);
42+
4143
if (!window->GetWindowHandle()) {
44+
FML_LOG(ERROR) << "Failed to create host window";
4245
return std::nullopt;
4346
}
4447

4548
// Assume first window is the main window.
4649
if (windows_.empty()) {
47-
window->SetQuitOnClose(true);
50+
window->quit_on_close_ = true;
4851
}
4952

50-
FlutterViewId const view_id = window->GetFlutterViewId();
51-
WindowState const state = window->GetState();
53+
FlutterViewId const view_id = window->view_controller_->view()->view_id();
54+
WindowState const state = window->state_;
5255
windows_[view_id] = std::move(window);
5356

5457
WindowMetadata const result = {.view_id = view_id,
5558
.archetype = settings.archetype,
56-
.size = GetWindowSize(view_id),
59+
.size = GetViewSize(view_id),
5760
.parent_id = std::nullopt,
5861
.state = state};
5962

6063
return result;
6164
}
6265

63-
bool FlutterHostWindowController::DestroyHostWindow(FlutterViewId view_id) {
64-
if (auto const it = windows_.find(view_id); it != windows_.end()) {
65-
FlutterHostWindow* const window = it->second.get();
66-
HWND const window_handle = window->GetWindowHandle();
66+
bool FlutterHostWindowController::ModifyHostWindow(
67+
FlutterViewId view_id,
68+
WindowModificationSettings const& settings) const {
69+
FlutterHostWindow* const window = GetHostWindow(view_id);
70+
if (!window) {
71+
FML_LOG(ERROR) << "Failed to find window with view ID " << view_id;
72+
return false;
73+
}
74+
75+
std::optional<Size> changed_size;
76+
Size const size_before = GetViewSize(view_id);
77+
78+
if (settings.size.has_value()) {
79+
window->SetClientSize(*settings.size);
80+
}
81+
if (settings.title.has_value()) {
82+
window->SetTitle(*settings.title);
83+
}
84+
if (settings.state.has_value()) {
85+
window->SetState(*settings.state);
86+
}
6787

68-
// |window| will be removed from |windows_| when WM_NCDESTROY is handled.
69-
PostMessage(window->GetWindowHandle(), WM_CLOSE, 0, 0);
88+
Size const size_after = GetViewSize(view_id);
89+
if (size_before != size_after) {
90+
changed_size = size_after;
91+
}
7092

71-
return true;
93+
if (changed_size) {
94+
SendOnWindowChanged(view_id, changed_size, std::nullopt);
7295
}
73-
return false;
96+
97+
return true;
98+
}
99+
100+
bool FlutterHostWindowController::DestroyHostWindow(
101+
FlutterViewId view_id) const {
102+
FlutterHostWindow* const window = GetHostWindow(view_id);
103+
if (!window) {
104+
FML_LOG(ERROR) << "Failed to find window with view ID " << view_id;
105+
return false;
106+
}
107+
108+
// |window| will be removed from |windows_| when WM_NCDESTROY is handled.
109+
PostMessage(window->GetWindowHandle(), WM_CLOSE, 0, 0);
110+
111+
return true;
74112
}
75113

76114
FlutterHostWindow* FlutterHostWindowController::GetHostWindow(
@@ -93,7 +131,7 @@ LRESULT FlutterHostWindowController::HandleMessage(HWND hwnd,
93131
});
94132
if (it != windows_.end()) {
95133
FlutterViewId const view_id = it->first;
96-
bool const quit_on_close = it->second->GetQuitOnClose();
134+
bool const quit_on_close = it->second->quit_on_close_;
97135

98136
windows_.erase(it);
99137

@@ -118,7 +156,7 @@ LRESULT FlutterHostWindowController::HandleMessage(HWND hwnd,
118156
? WindowState::kMinimized
119157
: WindowState::kRestored;
120158
}
121-
SendOnWindowChanged(view_id, GetWindowSize(view_id), std::nullopt);
159+
SendOnWindowChanged(view_id, GetViewSize(view_id), std::nullopt);
122160
}
123161
} break;
124162
default:
@@ -155,17 +193,14 @@ void FlutterHostWindowController::DestroyAllWindows() {
155193
}
156194
}
157195

158-
Size FlutterHostWindowController::GetWindowSize(FlutterViewId view_id) const {
159-
HWND const hwnd = windows_.at(view_id)->GetWindowHandle();
160-
RECT frame_rect;
161-
DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame_rect,
162-
sizeof(frame_rect));
163-
164-
// Convert to logical coordinates.
165-
double const dpr = FlutterDesktopGetDpiForHWND(hwnd) /
196+
Size FlutterHostWindowController::GetViewSize(FlutterViewId view_id) const {
197+
HWND const window_handle = GetHostWindow(view_id)->GetWindowHandle();
198+
RECT rect;
199+
GetClientRect(window_handle, &rect);
200+
double const dpr = FlutterDesktopGetDpiForHWND(window_handle) /
166201
static_cast<double>(USER_DEFAULT_SCREEN_DPI);
167-
double const width = (frame_rect.right - frame_rect.left) / dpr;
168-
double const height = (frame_rect.bottom - frame_rect.top) / dpr;
202+
double const width = rect.right / dpr;
203+
double const height = rect.bottom / dpr;
169204
return {width, height};
170205
}
171206

engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,21 @@ class FlutterHostWindowController {
3434
virtual std::optional<WindowMetadata> CreateHostWindow(
3535
WindowCreationSettings const& settings);
3636

37+
// Modifies the attributes of the window hosting the view with ID |view_id|
38+
// according to the given |settings|. A "onWindowChanged" message is sent if
39+
// at least one attribute is modified.
40+
//
41+
// Returns false if the controller does not have a window hosting a view with
42+
// ID |view_id|.
43+
virtual bool ModifyHostWindow(
44+
FlutterViewId view_id,
45+
WindowModificationSettings const& settings) const;
46+
3747
// Destroys the window that hosts the view with ID |view_id|.
3848
//
3949
// Returns false if the controller does not have a window hosting a view with
4050
// ID |view_id|.
41-
virtual bool DestroyHostWindow(FlutterViewId view_id);
51+
virtual bool DestroyHostWindow(FlutterViewId view_id) const;
4252

4353
// Gets the window hosting the view with ID |view_id|.
4454
//
@@ -62,9 +72,8 @@ class FlutterHostWindowController {
6272
// Destroys all windows managed by this controller.
6373
void DestroyAllWindows();
6474

65-
// Gets the size of the host window for the view with ID |view_id|. The size
66-
// is in logical coordinates and excludes the drop-shadow area.
67-
Size GetWindowSize(FlutterViewId view_id) const;
75+
// Retrieves the size of the view with ID |view_id|, in logical coordinates.
76+
Size GetViewSize(FlutterViewId view_id) const;
6877

6978
// Sends the "onWindowChanged" message to the Flutter engine.
7079
void SendOnWindowChanged(FlutterViewId view_id,

0 commit comments

Comments
 (0)