diff --git a/src/app/nebula_controller.cpp b/src/app/nebula_controller.cpp index da7c373..33bfbe2 100644 --- a/src/app/nebula_controller.cpp +++ b/src/app/nebula_controller.cpp @@ -211,6 +211,12 @@ void NebulaController::OnBrowserClosing(nebula::cef::BrowserRole role, CefRefPtr menu_popup_browser_ = nullptr; menu_popup_client_ = nullptr; } else { + if (content_fullscreen_) { + const auto* active_tab = tabs_.ActiveTab(); + if (active_tab && active_tab->browser && active_tab->browser->IsSame(browser)) { + SetContentFullscreen(false); + } + } tabs_.ClearBrowser(browser); } MaybeFinishShutdown(); @@ -308,6 +314,15 @@ void NebulaController::OnContentFaviconChanged(CefRefPtr browser, co tabs_.UpdateFavicon(browser, urls); } +void NebulaController::OnContentFullscreenChanged(CefRefPtr browser, bool fullscreen) { + const auto* active_tab = tabs_.ActiveTab(); + if (!active_tab || !active_tab->browser || !active_tab->browser->IsSame(browser)) { + return; + } + + SetContentFullscreen(fullscreen); +} + void NebulaController::OnPopupRequested(CefRefPtr browser, const std::string& target_url) { if (!tabs_.OwnsBrowser(browser)) { return; @@ -445,7 +460,7 @@ void NebulaController::CreateMenuPopupBrowser() { } void NebulaController::PositionMenuPopup() { - if (!window_ || !window_->hwnd() || !menu_popup_browser_) { + if (content_fullscreen_ || !window_ || !window_->hwnd() || !menu_popup_browser_) { return; } @@ -493,19 +508,38 @@ void NebulaController::FreshReload() { tabs_.LoadURL(WithCacheBuster(tab->url)); } +void NebulaController::SetContentFullscreen(bool fullscreen) { + if (content_fullscreen_ == fullscreen) { + return; + } + + content_fullscreen_ = fullscreen; + if (fullscreen) { + CloseMenuPopup(); + } + + SetBrowserVisible(chrome_browser_, !fullscreen); + if (window_) { + window_->SetFullscreen(fullscreen); + } + ResizeBrowsers(); +} + void NebulaController::ResizeBrowsers() { if (!window_) { return; } - const auto layout = window_->CurrentLayout(); + const auto layout = window_->CurrentLayout(!content_fullscreen_); if (chrome_browser_) { window_->ResizeChild(chrome_browser_->GetHost()->GetWindowHandle(), layout.chrome); } if (const auto* tab = tabs_.ActiveTab(); tab && tab->browser) { window_->ResizeChild(tab->browser->GetHost()->GetWindowHandle(), layout.content); } - PositionMenuPopup(); + if (!content_fullscreen_) { + PositionMenuPopup(); + } } void NebulaController::SendChromeState(const nebula::browser::NebulaTab& tab) { diff --git a/src/app/nebula_controller.h b/src/app/nebula_controller.h index 05d14ab..6a37737 100644 --- a/src/app/nebula_controller.h +++ b/src/app/nebula_controller.h @@ -34,6 +34,7 @@ public: void OnContentLoadingStateChanged(CefRefPtr browser, bool is_loading) override; void OnContentLoadProgressChanged(CefRefPtr browser, double progress) override; void OnContentFaviconChanged(CefRefPtr browser, const std::vector& urls) override; + void OnContentFullscreenChanged(CefRefPtr browser, bool fullscreen) override; void OnPopupRequested(CefRefPtr browser, const std::string& target_url) override; bool ShouldBypassInsecureWarning(CefRefPtr browser, const std::string& target_url) override; @@ -50,6 +51,7 @@ private: void ToggleDevTools(); void AdjustZoom(double delta); void FreshReload(); + void SetContentFullscreen(bool fullscreen); void ResizeBrowsers(); void SendChromeState(const nebula::browser::NebulaTab& tab); void MaybeFinishShutdown(); @@ -59,6 +61,7 @@ private: int show_command_ = SW_SHOWDEFAULT; bool closing_ = false; bool chrome_ready_ = false; + bool content_fullscreen_ = false; std::unique_ptr window_; nebula::browser::TabManager tabs_; diff --git a/src/cef/browser_client.cpp b/src/cef/browser_client.cpp index f1453b8..b3360dd 100644 --- a/src/cef/browser_client.cpp +++ b/src/cef/browser_client.cpp @@ -89,6 +89,13 @@ void NebulaBrowserClient::OnFaviconURLChange(CefRefPtr browser, } } +void NebulaBrowserClient::OnFullscreenModeChange(CefRefPtr browser, bool fullscreen) { + CEF_REQUIRE_UI_THREAD(); + if (role_ == BrowserRole::Content && delegate_) { + delegate_->OnContentFullscreenChanged(browser, fullscreen); + } +} + bool NebulaBrowserClient::OnPreKeyEvent(CefRefPtr browser, const CefKeyEvent& event, CefEventHandle os_event, diff --git a/src/cef/browser_client.h b/src/cef/browser_client.h index 6347433..e401cd0 100644 --- a/src/cef/browser_client.h +++ b/src/cef/browser_client.h @@ -30,6 +30,7 @@ public: virtual void OnContentLoadingStateChanged(CefRefPtr browser, bool is_loading) = 0; virtual void OnContentLoadProgressChanged(CefRefPtr browser, double progress) = 0; virtual void OnContentFaviconChanged(CefRefPtr browser, const std::vector& urls) = 0; + virtual void OnContentFullscreenChanged(CefRefPtr browser, bool fullscreen) = 0; virtual void OnPopupRequested(CefRefPtr browser, const std::string& target_url) = 0; virtual bool ShouldBypassInsecureWarning(CefRefPtr browser, const std::string& target_url) = 0; }; @@ -63,6 +64,7 @@ public: const CefString& title) override; void OnFaviconURLChange(CefRefPtr browser, const std::vector& icon_urls) override; + void OnFullscreenModeChange(CefRefPtr browser, bool fullscreen) override; bool OnPreKeyEvent(CefRefPtr browser, const CefKeyEvent& event, diff --git a/src/window/nebula_window.cpp b/src/window/nebula_window.cpp index 8384bc0..f08c623 100644 --- a/src/window/nebula_window.cpp +++ b/src/window/nebula_window.cpp @@ -23,6 +23,30 @@ RECT GetWorkArea() { return work_area; } +RECT GetMonitorWorkArea(HWND hwnd) { + MONITORINFO monitor_info = {}; + monitor_info.cbSize = sizeof(monitor_info); + + const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + if (monitor && GetMonitorInfoW(monitor, &monitor_info)) { + return monitor_info.rcWork; + } + + return GetWorkArea(); +} + +RECT GetMonitorArea(HWND hwnd) { + MONITORINFO monitor_info = {}; + monitor_info.cbSize = sizeof(monitor_info); + + const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + if (monitor && GetMonitorInfoW(monitor, &monitor_info)) { + return monitor_info.rcMonitor; + } + + return GetWorkArea(); +} + bool IsResizeHit(LRESULT hit) { return hit == HTLEFT || hit == HTRIGHT || hit == HTTOP || hit == HTBOTTOM || hit == HTTOPLEFT || hit == HTTOPRIGHT || hit == HTBOTTOMLEFT || hit == HTBOTTOMRIGHT; @@ -125,14 +149,16 @@ bool NebulaWindow::Create(HINSTANCE instance, int show_command) { return true; } -BrowserLayout NebulaWindow::CurrentLayout() const { +BrowserLayout NebulaWindow::CurrentLayout(bool show_chrome) const { RECT client = {}; if (hwnd_) { GetClientRect(hwnd_, &client); } BrowserLayout layout; - layout.chrome = {0, 0, client.right, std::min(ScaleForDpi(chrome_height_dip_), client.bottom)}; + layout.chrome = show_chrome + ? RECT{0, 0, client.right, std::min(ScaleForDpi(chrome_height_dip_), client.bottom)} + : RECT{0, 0, 0, 0}; layout.content = {0, layout.chrome.bottom, client.right, client.bottom}; return layout; } @@ -160,13 +186,55 @@ void NebulaWindow::Minimize() { } void NebulaWindow::ToggleMaximize() { - if (!hwnd_) { + if (!hwnd_ || fullscreen_) { return; } ShowWindow(hwnd_, IsZoomed(hwnd_) ? SW_RESTORE : SW_MAXIMIZE); } +void NebulaWindow::SetFullscreen(bool fullscreen) { + if (!hwnd_ || fullscreen_ == fullscreen) { + return; + } + + if (fullscreen) { + restore_style_ = GetWindowLongPtrW(hwnd_, GWL_STYLE); + restore_ex_style_ = GetWindowLongPtrW(hwnd_, GWL_EXSTYLE); + restore_placement_.length = sizeof(restore_placement_); + GetWindowPlacement(hwnd_, &restore_placement_); + + fullscreen_ = true; + const RECT monitor = GetMonitorArea(hwnd_); + SetWindowLongPtrW(hwnd_, GWL_STYLE, restore_style_ & ~(WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX)); + SetWindowLongPtrW(hwnd_, GWL_EXSTYLE, restore_ex_style_); + SetWindowPos( + hwnd_, + HWND_TOPMOST, + monitor.left, + monitor.top, + monitor.right - monitor.left, + monitor.bottom - monitor.top, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + } else { + fullscreen_ = false; + SetWindowLongPtrW(hwnd_, GWL_STYLE, restore_style_); + SetWindowLongPtrW(hwnd_, GWL_EXSTYLE, restore_ex_style_); + SetWindowPlacement(hwnd_, &restore_placement_); + SetWindowPos( + hwnd_, + HWND_NOTOPMOST, + 0, + 0, + 0, + 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + ApplyWindowFrameStyle(hwnd_); + } + + NotifyResize(); +} + void NebulaWindow::Close() { if (hwnd_) { SendMessageW(hwnd_, WM_CLOSE, 0, 0); @@ -335,6 +403,18 @@ LRESULT NebulaWindow::WndProc(UINT message, WPARAM wparam, LPARAM lparam) { return 0; } + case WM_GETMINMAXINFO: { + const RECT work_area = GetMonitorWorkArea(hwnd_); + const RECT monitor_area = GetMonitorArea(hwnd_); + + auto* minmax = reinterpret_cast(lparam); + minmax->ptMaxPosition.x = work_area.left - monitor_area.left; + minmax->ptMaxPosition.y = work_area.top - monitor_area.top; + minmax->ptMaxSize.x = work_area.right - work_area.left; + minmax->ptMaxSize.y = work_area.bottom - work_area.top; + return 0; + } + case WM_CLOSE: if (delegate_) { delegate_->OnWindowCloseRequested(); @@ -409,6 +489,10 @@ LRESULT NebulaWindow::HitTestPoint(POINT point) const { RECT window = {}; GetWindowRect(hwnd_, &window); + if (fullscreen_ || IsZoomed(hwnd_)) { + return HTCLIENT; + } + const int resize_border = ScaleForDpi(resize_border_dip_); const bool left = point.x >= window.left && point.x < window.left + resize_border; const bool right = point.x < window.right && point.x >= window.right - resize_border; diff --git a/src/window/nebula_window.h b/src/window/nebula_window.h index 83bcb36..c971c62 100644 --- a/src/window/nebula_window.h +++ b/src/window/nebula_window.h @@ -26,11 +26,12 @@ public: bool Create(HINSTANCE instance, int show_command); HWND hwnd() const { return hwnd_; } - BrowserLayout CurrentLayout() const; + BrowserLayout CurrentLayout(bool show_chrome = true) const; void ResizeChild(HWND child, const RECT& rect) const; void Minimize(); void ToggleMaximize(); + void SetFullscreen(bool fullscreen); void Close(); void BeginDrag(); void SetTitle(const std::wstring& title); @@ -53,6 +54,10 @@ private: WindowDelegate* delegate_ = nullptr; HINSTANCE instance_ = nullptr; HWND hwnd_ = nullptr; + bool fullscreen_ = false; + LONG_PTR restore_style_ = 0; + LONG_PTR restore_ex_style_ = 0; + WINDOWPLACEMENT restore_placement_ = {sizeof(WINDOWPLACEMENT)}; UINT dpi_ = 96; int resize_border_dip_ = 8; int chrome_height_dip_ = 104;