11#include < dwmapi.h>
22#include < windows.h>
3+ #include < cmath>
34#include < iostream>
45#include " ../../foundation/id_allocator.h"
56#include " ../../window.h"
67#include " ../../window_manager.h"
78#include " ../../window_registry.h"
89#include " dpi_utils_windows.h"
910#include " string_utils_windows.h"
11+ #include " window_message_dispatcher.h"
1012
1113#pragma comment(lib, "dwmapi.lib")
1214
@@ -30,6 +32,9 @@ class Window::Impl {
3032 WindowId window_id_;
3133 TitleBarStyle title_bar_style_;
3234 VisualEffect visual_effect_;
35+ Size min_size_{0 , 0 };
36+ Size max_size_{0 , 0 };
37+ int min_max_handler_id_ = 0 ;
3338};
3439
3540// Custom window procedure to handle window messages
@@ -184,8 +189,14 @@ Window::Window(void* native_window) {
184189}
185190
186191Window::~Window () {
187- // Remove window from registry on destruction
188192 if (pimpl_ && pimpl_->window_id_ != IdAllocator::kInvalidId ) {
193+ // Unregister WM_GETMINMAXINFO handler if registered
194+ if (pimpl_->min_max_handler_id_ != 0 && pimpl_->hwnd_ ) {
195+ WindowMessageDispatcher::GetInstance ().UnregisterHandler (
196+ pimpl_->min_max_handler_id_ );
197+ }
198+
199+ // Remove window from registry on destruction
189200 WindowRegistry::GetInstance ().Remove (pimpl_->window_id_ );
190201
191202 // Remove the custom property from HWND if window is still valid
@@ -336,8 +347,14 @@ bool Window::IsFullScreen() const {
336347
337348void Window::SetBounds (Rectangle bounds) {
338349 if (pimpl_->hwnd_ ) {
339- SetWindowPos (pimpl_->hwnd_ , nullptr , static_cast <int >(bounds.x ), static_cast <int >(bounds.y ),
340- static_cast <int >(bounds.width ), static_cast <int >(bounds.height ), SWP_NOZORDER);
350+ double scale = GetScaleFactorForWindow (pimpl_->hwnd_ );
351+ if (scale <= 0.0 )
352+ scale = 1.0 ;
353+ SetWindowPos (pimpl_->hwnd_ , nullptr ,
354+ static_cast <int >(std::lround (bounds.x * scale)),
355+ static_cast <int >(std::lround (bounds.y * scale)),
356+ static_cast <int >(std::lround (bounds.width * scale)),
357+ static_cast <int >(std::lround (bounds.height * scale)), SWP_NOZORDER);
341358 }
342359}
343360
@@ -346,10 +363,13 @@ Rectangle Window::GetBounds() const {
346363 if (pimpl_->hwnd_ ) {
347364 RECT rect;
348365 GetWindowRect (pimpl_->hwnd_ , &rect);
349- bounds.x = rect.left ;
350- bounds.y = rect.top ;
351- bounds.width = rect.right - rect.left ;
352- bounds.height = rect.bottom - rect.top ;
366+ double scale = GetScaleFactorForWindow (pimpl_->hwnd_ );
367+ if (scale <= 0.0 )
368+ scale = 1.0 ;
369+ bounds.x = static_cast <double >(rect.left ) / scale;
370+ bounds.y = static_cast <double >(rect.top ) / scale;
371+ bounds.width = static_cast <double >(rect.right - rect.left ) / scale;
372+ bounds.height = static_cast <double >(rect.bottom - rect.top ) / scale;
353373 }
354374 return bounds;
355375}
@@ -358,8 +378,13 @@ void Window::SetSize(Size size, bool animate) {
358378 if (pimpl_->hwnd_ ) {
359379 // Windows doesn't have built-in animation for window resizing
360380 // Animation would require custom implementation
361- SetWindowPos (pimpl_->hwnd_ , nullptr , 0 , 0 , static_cast <int >(size.width ),
362- static_cast <int >(size.height ), SWP_NOMOVE | SWP_NOZORDER);
381+ double scale = GetScaleFactorForWindow (pimpl_->hwnd_ );
382+ if (scale <= 0.0 )
383+ scale = 1.0 ;
384+ SetWindowPos (pimpl_->hwnd_ , nullptr , 0 , 0 ,
385+ static_cast <int >(std::lround (size.width * scale)),
386+ static_cast <int >(std::lround (size.height * scale)),
387+ SWP_NOMOVE | SWP_NOZORDER);
363388 }
364389}
365390
@@ -368,8 +393,11 @@ Size Window::GetSize() const {
368393 if (pimpl_->hwnd_ ) {
369394 RECT rect;
370395 GetWindowRect (pimpl_->hwnd_ , &rect);
371- size.width = rect.right - rect.left ;
372- size.height = rect.bottom - rect.top ;
396+ double scale = GetScaleFactorForWindow (pimpl_->hwnd_ );
397+ if (scale <= 0.0 )
398+ scale = 1.0 ;
399+ size.width = static_cast <double >(rect.right - rect.left ) / scale;
400+ size.height = static_cast <double >(rect.bottom - rect.top ) / scale;
373401 }
374402 return size;
375403}
@@ -384,8 +412,13 @@ void Window::SetContentSize(Size size) {
384412 int borderWidth = (windowRect.right - windowRect.left ) - clientRect.right ;
385413 int borderHeight = (windowRect.bottom - windowRect.top ) - clientRect.bottom ;
386414
387- SetWindowPos (pimpl_->hwnd_ , nullptr , 0 , 0 , static_cast <int >(size.width ) + borderWidth,
388- static_cast <int >(size.height ) + borderHeight, SWP_NOMOVE | SWP_NOZORDER);
415+ double scale = GetScaleFactorForWindow (pimpl_->hwnd_ );
416+ if (scale <= 0.0 )
417+ scale = 1.0 ;
418+ SetWindowPos (pimpl_->hwnd_ , nullptr , 0 , 0 ,
419+ static_cast <int >(std::lround (size.width * scale)) + borderWidth,
420+ static_cast <int >(std::lround (size.height * scale)) + borderHeight,
421+ SWP_NOMOVE | SWP_NOZORDER);
389422 }
390423}
391424
@@ -394,8 +427,11 @@ Size Window::GetContentSize() const {
394427 if (pimpl_->hwnd_ ) {
395428 RECT rect;
396429 GetClientRect (pimpl_->hwnd_ , &rect);
397- size.width = rect.right ;
398- size.height = rect.bottom ;
430+ double scale = GetScaleFactorForWindow (pimpl_->hwnd_ );
431+ if (scale <= 0.0 )
432+ scale = 1.0 ;
433+ size.width = static_cast <double >(rect.right ) / scale;
434+ size.height = static_cast <double >(rect.bottom ) / scale;
399435 }
400436 return size;
401437}
@@ -418,11 +454,15 @@ void Window::SetContentBounds(Rectangle bounds) {
418454 int offsetX = clientTopLeft.x - windowRect.left ;
419455 int offsetY = clientTopLeft.y - windowRect.top ;
420456
457+ double scale = GetScaleFactorForWindow (pimpl_->hwnd_ );
458+ if (scale <= 0.0 )
459+ scale = 1.0 ;
460+
421461 // Calculate window position so that client area is at bounds position
422- int windowX = static_cast <int >(bounds.x ) - offsetX;
423- int windowY = static_cast <int >(bounds.y ) - offsetY;
424- int windowWidth = static_cast <int >(bounds.width ) + borderWidth;
425- int windowHeight = static_cast <int >(bounds.height ) + borderHeight;
462+ int windowX = static_cast <int >(std::lround ( bounds.x * scale) ) - offsetX;
463+ int windowY = static_cast <int >(std::lround ( bounds.y * scale) ) - offsetY;
464+ int windowWidth = static_cast <int >(std::lround ( bounds.width * scale) ) + borderWidth;
465+ int windowHeight = static_cast <int >(std::lround ( bounds.height * scale) ) + borderHeight;
426466
427467 SetWindowPos (pimpl_->hwnd_ , nullptr , windowX, windowY, windowWidth, windowHeight, SWP_NOZORDER);
428468 }
@@ -453,25 +493,83 @@ Rectangle Window::GetContentBounds() const {
453493 return bounds;
454494}
455495
496+ // Helper function: registers a WM_GETMINMAXINFO handler for the given HWND
497+ // via WindowMessageDispatcher if not already registered. Returns the handler ID.
498+ static int RegisterMinMaxInfoHandler (HWND hwnd, int existing_handler_id) {
499+ if (existing_handler_id != 0 ) {
500+ return existing_handler_id;
501+ }
502+ if (!hwnd || !IsWindow (hwnd)) {
503+ return 0 ;
504+ }
505+ auto & dispatcher = WindowMessageDispatcher::GetInstance ();
506+ return dispatcher.RegisterHandler (
507+ hwnd,
508+ [](HWND hwnd, UINT msg, WPARAM wparam,
509+ LPARAM lparam) -> std::optional<LRESULT> {
510+ if (msg == WM_GETMINMAXINFO) {
511+ HANDLE prop_handle = GetPropW (hwnd, kWindowIdProperty );
512+ if (prop_handle) {
513+ WindowId window_id = static_cast <WindowId>(
514+ reinterpret_cast <uintptr_t >(prop_handle));
515+ if (window_id != IdAllocator::kInvalidId ) {
516+ auto window = WindowRegistry::GetInstance ().Get (window_id);
517+ if (window) {
518+ auto minSize = window->GetMinimumSize ();
519+ auto maxSize = window->GetMaximumSize ();
520+ MINMAXINFO* mmi = reinterpret_cast <MINMAXINFO*>(lparam);
521+ double scale_mm = GetScaleFactorForWindow (hwnd);
522+ if (scale_mm <= 0.0 )
523+ scale_mm = 1.0 ;
524+ if (minSize.width > 0 && minSize.height > 0 ) {
525+ mmi->ptMinTrackSize .x = static_cast <LONG>(std::lround (minSize.width * scale_mm));
526+ mmi->ptMinTrackSize .y = static_cast <LONG>(std::lround (minSize.height * scale_mm));
527+ }
528+ if (maxSize.width > 0 && maxSize.height > 0 ) {
529+ mmi->ptMaxTrackSize .x = static_cast <LONG>(std::lround (maxSize.width * scale_mm));
530+ mmi->ptMaxTrackSize .y = static_cast <LONG>(std::lround (maxSize.height * scale_mm));
531+ }
532+ return std::make_optional (0 );
533+ }
534+ }
535+ }
536+ }
537+ return std::nullopt ;
538+ });
539+ }
540+
456541void Window::SetMinimumSize (Size size) {
457- // Windows minimum size would be handled in WM_GETMINMAXINFO message
458- // This is a placeholder implementation
542+ pimpl_->min_size_ = size;
543+
544+ if (pimpl_->hwnd_ ) {
545+ pimpl_->min_max_handler_id_ =
546+ RegisterMinMaxInfoHandler (pimpl_->hwnd_ , pimpl_->min_max_handler_id_ );
547+
548+ // Trigger the window to re-evaluate its size constraints
549+ SetWindowPos (pimpl_->hwnd_ , nullptr , 0 , 0 , 0 , 0 ,
550+ SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
551+ }
459552}
460553
461554Size Window::GetMinimumSize () const {
462- // Return default minimum size
463- return {0 , 0 };
555+ return pimpl_->min_size_ ;
464556}
465557
466558void Window::SetMaximumSize (Size size) {
467- // Windows maximum size would be handled in WM_GETMINMAXINFO message
468- // This is a placeholder implementation
559+ pimpl_->max_size_ = size;
560+
561+ if (pimpl_->hwnd_ ) {
562+ pimpl_->min_max_handler_id_ =
563+ RegisterMinMaxInfoHandler (pimpl_->hwnd_ , pimpl_->min_max_handler_id_ );
564+
565+ // Trigger the window to re-evaluate its size constraints
566+ SetWindowPos (pimpl_->hwnd_ , nullptr , 0 , 0 , 0 , 0 ,
567+ SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
568+ }
469569}
470570
471571Size 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))};
572+ return pimpl_->max_size_ ;
475573}
476574
477575void Window::SetResizable (bool is_resizable) {
@@ -603,8 +701,13 @@ bool Window::IsAlwaysOnTop() const {
603701
604702void Window::SetPosition (Point point) {
605703 if (pimpl_->hwnd_ ) {
606- SetWindowPos (pimpl_->hwnd_ , nullptr , static_cast <int >(point.x ), static_cast <int >(point.y ), 0 , 0 ,
607- SWP_NOSIZE | SWP_NOZORDER);
704+ double scale = GetScaleFactorForWindow (pimpl_->hwnd_ );
705+ if (scale <= 0.0 )
706+ scale = 1.0 ;
707+ SetWindowPos (pimpl_->hwnd_ , nullptr ,
708+ static_cast <int >(std::lround (point.x * scale)),
709+ static_cast <int >(std::lround (point.y * scale)),
710+ 0 , 0 , SWP_NOSIZE | SWP_NOZORDER);
608711 }
609712}
610713
@@ -613,8 +716,11 @@ Point Window::GetPosition() const {
613716 if (pimpl_->hwnd_ ) {
614717 RECT rect;
615718 GetWindowRect (pimpl_->hwnd_ , &rect);
616- point.x = rect.left ;
617- point.y = rect.top ;
719+ double scale = GetScaleFactorForWindow (pimpl_->hwnd_ );
720+ if (scale <= 0.0 )
721+ scale = 1.0 ;
722+ point.x = static_cast <double >(rect.left ) / scale;
723+ point.y = static_cast <double >(rect.top ) / scale;
618724 }
619725 return point;
620726}
@@ -635,6 +741,7 @@ void Window::Center() {
635741 GetMonitorInfo (monitor, &mi);
636742
637743 // Calculate the center position on the monitor's work area
744+ // All values here are in physical pixels (GetWindowRect and rcWork), so no DPI scaling needed
638745 int centerX = mi.rcWork .left + (mi.rcWork .right - mi.rcWork .left - windowWidth) / 2 ;
639746 int centerY = mi.rcWork .top + (mi.rcWork .bottom - mi.rcWork .top - windowHeight) / 2 ;
640747
0 commit comments