diff --git a/README.md b/README.md index cc4a6fd6..4c3ead3e 100644 --- a/README.md +++ b/README.md @@ -653,14 +653,14 @@ Makes the window not show in the taskbar / dock. Sets progress value in progress bar. Valid range is [0, 1.0]. -##### hasShadow `macos` +##### hasShadow `macos` `windows` -Returns `bool` - Whether the window has a shadow. +Returns `bool` - Whether the window has a shadow. On Windows, always returns true unless window is frameless. -##### setHasShadow `macos` +##### setHasShadow `macos` `windows` -Sets whether the window should have a shadow. +Sets whether the window should have a shadow. On Windows, doesn't do anything unless window is frameless. ##### getOpacity `macos` `windows` diff --git a/lib/src/window_manager.dart b/lib/src/window_manager.dart index 13695e1e..f9ae8e51 100644 --- a/lib/src/window_manager.dart +++ b/lib/src/window_manager.dart @@ -531,16 +531,16 @@ class WindowManager { await _channel.invokeMethod('setProgressBar', arguments); } - /// Returns `bool` - Whether the window has a shadow. + /// Returns `bool` - Whether the window has a shadow. On Windows, always returns true unless window is frameless. /// - /// @platforms macos + /// @platforms macos,windows Future hasShadow() async { return await _channel.invokeMethod('hasShadow'); } - /// Sets whether the window should have a shadow. + /// Sets whether the window should have a shadow. On Windows, doesn't do anything unless window is frameless. /// - /// @platforms macos + /// @platforms macos,windows Future setHasShadow(bool hasShadow) async { final Map arguments = { 'hasShadow': hasShadow, diff --git a/windows/window_manager.cpp b/windows/window_manager.cpp index e1b5892e..827b064f 100644 --- a/windows/window_manager.cpp +++ b/windows/window_manager.cpp @@ -35,6 +35,7 @@ class WindowManager { int last_state = STATE_NORMAL; + bool has_shadow_ = false; bool is_frameless_ = false; bool is_prevent_close_ = false; double aspect_ratio_ = 0; @@ -127,28 +128,12 @@ void WindowManager::SetAsFrameless() { HWND hWnd = GetMainWindow(); RECT rect; - MARGINS margins = {0, 0, 0, 0}; GetWindowRect(hWnd, &rect); - SetWindowLong(hWnd, GWL_STYLE, - WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | - WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_VISIBLE); - DwmExtendFrameIntoClientArea(hWnd, &margins); SetWindowPos(hWnd, nullptr, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); - - flutter::EncodableMap args = flutter::EncodableMap(); - args[flutter::EncodableValue("backgroundColorA")] = - flutter::EncodableValue(0); - args[flutter::EncodableValue("backgroundColorR")] = - flutter::EncodableValue(0); - args[flutter::EncodableValue("backgroundColorG")] = - flutter::EncodableValue(0); - args[flutter::EncodableValue("backgroundColorB")] = - flutter::EncodableValue(0); - SetBackgroundColor(args); } void WindowManager::WaitUntilReadyToShow() { @@ -569,31 +554,15 @@ void WindowManager::SetTitle(const flutter::EncodableMap& args) { void WindowManager::SetTitleBarStyle(const flutter::EncodableMap& args) { title_bar_style_ = std::get(args.at(flutter::EncodableValue("titleBarStyle"))); - - HWND hWnd = GetMainWindow(); - DWORD gwlStyle = GetWindowLong(hWnd, GWL_STYLE); // Enables the ability to go from setAsFrameless() to // TitleBarStyle.normal/hidden is_frameless_ = false; - if (title_bar_style_ == "hidden") { - gwlStyle = WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | - WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_VISIBLE; - SetWindowLong(hWnd, GWL_STYLE, gwlStyle); - BOOL composition_enabled = FALSE; - bool success = DwmIsCompositionEnabled(&composition_enabled) == S_OK; - if (composition_enabled && success) { - static const MARGINS shadow_state[2]{{0, 0, 0, 0}, {1, 1, 1, 1}}; - DwmExtendFrameIntoClientArea(hWnd, &shadow_state[0]); - ShowWindow(hWnd, SW_SHOW); - } - } else { - gwlStyle = WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | - WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE; - SetWindowLong(hWnd, GWL_STYLE, gwlStyle); - } + MARGINS margins = {0, 0, 0, 0}; + HWND hWnd = GetMainWindow(); RECT rect; GetWindowRect(hWnd, &rect); + DwmExtendFrameIntoClientArea(hWnd, &margins); SetWindowPos(hWnd, nullptr, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); @@ -653,6 +622,24 @@ void WindowManager::SetProgressBar(const flutter::EncodableMap& args) { } } +bool WindowManager::HasShadow() { + if (is_frameless_) + return has_shadow_; + return true; +} + +void WindowManager::SetHasShadow(const flutter::EncodableMap& args) { + if (is_frameless_) { + has_shadow_ = std::get(args.at(flutter::EncodableValue("hasShadow"))); + + HWND hWnd = GetMainWindow(); + + MARGINS margins[2]{{0, 0, 0, 0}, {0, 0, 1, 0}}; + + DwmExtendFrameIntoClientArea(hWnd, &margins[has_shadow_]); + } +} + double WindowManager::GetOpacity() { return opacity_; } diff --git a/windows/window_manager_plugin.cpp b/windows/window_manager_plugin.cpp index 7664b747..ff8502cd 100644 --- a/windows/window_manager_plugin.cpp +++ b/windows/window_manager_plugin.cpp @@ -109,48 +109,51 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, std::optional result = std::nullopt; if (message == WM_NCCALCSIZE) { + // This must always be first or else the one of other two ifs will execute + // when window is in full screen and we don't want that if (wParam && window_manager->IsFullScreen()) { NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); sz->rgrc[0].bottom -= 3; - return (WVR_HREDRAW | WVR_VREDRAW); + return 0; } + // This must always be before handling title_bar_style_ == "hidden" so + // the if TitleBarStyle.hidden doesn't get executed. if (wParam && window_manager->is_frameless_) { - SetWindowLong(hWnd, 0, 0); NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); + // Add borders when maximized so app doesn't get cut off. if (window_manager->IsMaximized()) { sz->rgrc[0].left += 8; sz->rgrc[0].top += 8; sz->rgrc[0].right -= 8; sz->rgrc[0].bottom -= 9; } + // This cuts the app at the bottom by one pixel but that's necessary to + // prevent jitter when resizing the app sz->rgrc[0].bottom += 1; - return (WVR_HREDRAW | WVR_VREDRAW); + return 0; } + // This must always be last. if (wParam && window_manager->title_bar_style_ == "hidden") { - WINDOWPLACEMENT wPos; - wPos.length = sizeof(wPos); - GetWindowPlacement(hWnd, &wPos); - RECT borderThickness; - SetRectEmpty(&borderThickness); - AdjustWindowRectEx(&borderThickness, - GetWindowLongPtr(hWnd, GWL_STYLE) & WS_POPUP, FALSE, - NULL); NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); // Add 8 pixel to the top border when maximized so the app isn't cut off - // Top resize border is still not working. if (window_manager->IsMaximized()) { sz->rgrc[0].top += 8; } else { + // on windows 10, if set to 0, there's a white line at the top + // of the app and I've yet to find a way to remove that. sz->rgrc[0].top += IsWindows11OrGreater() ? 0 : 1; } sz->rgrc[0].right -= 8; sz->rgrc[0].bottom -= 8; sz->rgrc[0].left -= -8; - return (WVR_HREDRAW | WVR_VREDRAW); + // Previously (WVR_HREDRAW | WVR_VREDRAW), but returning 0 or 1 doesn't + // actually break anything so I've set it to 0. Unless someone pointed a + // problem in the future. + return 0; } } else if (message == WM_NCHITTEST) { if (!window_manager->is_resizable_) { @@ -458,6 +461,14 @@ void WindowManagerPlugin::HandleMethodCall( std::get(*method_call.arguments()); window_manager->SetProgressBar(args); result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("hasShadow") == 0) { + bool value = window_manager->HasShadow(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setHasShadow") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetHasShadow(args); + result->Success(flutter::EncodableValue(true)); } else if (method_name.compare("getOpacity") == 0) { double value = window_manager->GetOpacity(); result->Success(flutter::EncodableValue(value));