Skip to content

Commit 784fa08

Browse files
committed
fix(windows): implement SetMinimumSize/SetMaximumSize via WM_GETMINMAXINFO
The SetMinimumSize and SetMaximumSize methods on Windows were empty placeholder implementations that did nothing. This caused setting window size constraints to have no effect in Flutter apps and other consumers. Fix by: - Storing min/max size constraints in Window::Impl - Registering a WM_GETMINMAXINFO handler via WindowMessageDispatcher that reads the constraints from the Window object and sets ptMinTrackSize/ptMaxTrackSize on the MINMAXINFO structure - This works for both self-created windows and Flutter-hosted windows (via Window(void*) constructor) - Triggering SWP_FRAMECHANGED after setting constraints so the window immediately re-evaluates them - Properly cleaning up the handler in the destructor Closes libnativeapi/nativeapi-flutter#9
1 parent 00b6fff commit 784fa08

1 file changed

Lines changed: 75 additions & 10 deletions

File tree

src/platform/windows/window_windows.cpp

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "../../window_registry.h"
88
#include "dpi_utils_windows.h"
99
#include "string_utils_windows.h"
10+
#include "window_message_dispatcher.h"
1011

1112
#pragma comment(lib, "dwmapi.lib")
1213

@@ -30,6 +31,9 @@ class Window::Impl {
3031
WindowId window_id_;
3132
TitleBarStyle title_bar_style_;
3233
VisualEffect visual_effect_;
34+
Size min_size_{0, 0};
35+
Size max_size_{0, 0};
36+
int min_max_handler_id_ = 0;
3337
};
3438

3539
// Custom window procedure to handle window messages
@@ -184,8 +188,14 @@ Window::Window(void* native_window) {
184188
}
185189

186190
Window::~Window() {
187-
// Remove window from registry on destruction
188191
if (pimpl_ && pimpl_->window_id_ != IdAllocator::kInvalidId) {
192+
// Unregister WM_GETMINMAXINFO handler if registered
193+
if (pimpl_->min_max_handler_id_ != 0 && pimpl_->hwnd_) {
194+
WindowMessageDispatcher::GetInstance().UnregisterHandler(
195+
pimpl_->min_max_handler_id_);
196+
}
197+
198+
// Remove window from registry on destruction
189199
WindowRegistry::GetInstance().Remove(pimpl_->window_id_);
190200

191201
// Remove the custom property from HWND if window is still valid
@@ -453,25 +463,80 @@ Rectangle Window::GetContentBounds() const {
453463
return bounds;
454464
}
455465

466+
// Helper function: registers a WM_GETMINMAXINFO handler for the given HWND
467+
// via WindowMessageDispatcher if not already registered. Returns the handler ID.
468+
static int RegisterMinMaxInfoHandler(HWND hwnd, int existing_handler_id) {
469+
if (existing_handler_id != 0) {
470+
return existing_handler_id;
471+
}
472+
if (!hwnd || !IsWindow(hwnd)) {
473+
return 0;
474+
}
475+
auto& dispatcher = WindowMessageDispatcher::GetInstance();
476+
return dispatcher.RegisterHandler(
477+
hwnd,
478+
[](HWND hwnd, UINT msg, WPARAM wparam,
479+
LPARAM lparam) -> std::optional<LRESULT> {
480+
if (msg == WM_GETMINMAXINFO) {
481+
HANDLE prop_handle = GetPropW(hwnd, kWindowIdProperty);
482+
if (prop_handle) {
483+
WindowId window_id = static_cast<WindowId>(
484+
reinterpret_cast<uintptr_t>(prop_handle));
485+
if (window_id != IdAllocator::kInvalidId) {
486+
auto window = WindowRegistry::GetInstance().Get(window_id);
487+
if (window) {
488+
auto minSize = window->GetMinimumSize();
489+
auto maxSize = window->GetMaximumSize();
490+
MINMAXINFO* mmi = reinterpret_cast<MINMAXINFO*>(lparam);
491+
if (minSize.width > 0 && minSize.height > 0) {
492+
mmi->ptMinTrackSize.x = static_cast<LONG>(minSize.width);
493+
mmi->ptMinTrackSize.y = static_cast<LONG>(minSize.height);
494+
}
495+
if (maxSize.width > 0 && maxSize.height > 0) {
496+
mmi->ptMaxTrackSize.x = static_cast<LONG>(maxSize.width);
497+
mmi->ptMaxTrackSize.y = static_cast<LONG>(maxSize.height);
498+
}
499+
return std::make_optional(0);
500+
}
501+
}
502+
}
503+
}
504+
return std::nullopt;
505+
});
506+
}
507+
456508
void Window::SetMinimumSize(Size size) {
457-
// Windows minimum size would be handled in WM_GETMINMAXINFO message
458-
// This is a placeholder implementation
509+
pimpl_->min_size_ = size;
510+
511+
if (pimpl_->hwnd_) {
512+
pimpl_->min_max_handler_id_ =
513+
RegisterMinMaxInfoHandler(pimpl_->hwnd_, pimpl_->min_max_handler_id_);
514+
515+
// Trigger the window to re-evaluate its size constraints
516+
SetWindowPos(pimpl_->hwnd_, nullptr, 0, 0, 0, 0,
517+
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
518+
}
459519
}
460520

461521
Size Window::GetMinimumSize() const {
462-
// Return default minimum size
463-
return {0, 0};
522+
return pimpl_->min_size_;
464523
}
465524

466525
void Window::SetMaximumSize(Size size) {
467-
// Windows maximum size would be handled in WM_GETMINMAXINFO message
468-
// This is a placeholder implementation
526+
pimpl_->max_size_ = size;
527+
528+
if (pimpl_->hwnd_) {
529+
pimpl_->min_max_handler_id_ =
530+
RegisterMinMaxInfoHandler(pimpl_->hwnd_, pimpl_->min_max_handler_id_);
531+
532+
// Trigger the window to re-evaluate its size constraints
533+
SetWindowPos(pimpl_->hwnd_, nullptr, 0, 0, 0, 0,
534+
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
535+
}
469536
}
470537

471538
Size Window::GetMaximumSize() const {
472-
// Return default maximum size (screen size)
473-
return {static_cast<double>(GetSystemMetrics(SM_CXSCREEN)),
474-
static_cast<double>(GetSystemMetrics(SM_CYSCREEN))};
539+
return pimpl_->max_size_;
475540
}
476541

477542
void Window::SetResizable(bool is_resizable) {

0 commit comments

Comments
 (0)