#include "ScreenLayer.h" #include "../GPU.h" #include "../Input/InputHandler.h" #include "../UserInterface/Engine/Scale.h" #include #include #include std::stack> ScreenLayer::screens; std::list> ScreenLayer::pointerStreams; std::unique_ptr ScreenLayer::overlay; std::unique_ptr ScreenLayer::statusbar; bool ScreenLayer::pointerDispatchActive = false; bool ScreenLayer::closeRequested = false; UI::Thickness ScreenLayer::safeInsets; float ScreenLayer::uiScale = 1.0f; // SDL reports the safe area in window coordinates, the pixel rect is derived // from that and then converted into logical units, which is the space the ui // lays out in. void ScreenLayer::updateSafeAreaInsets(SDL_Window* window, int pixelWidth, int pixelHeight) { int windowWidth = 0; int windowHeight = 0; SDL_Rect safe = {}; if (!window || !SDL_GetWindowSize(window, &windowWidth, &windowHeight) || !SDL_GetWindowSafeArea(window, &safe) || windowWidth <= 0 || windowHeight <= 0) { safeInsets = UI::Thickness(); return; } const float scaleX = static_cast(pixelWidth) / static_cast(windowWidth); const float scaleY = static_cast(pixelHeight) / static_cast(windowHeight); safeInsets = UI::Thickness( UI::PixelsToUnits(std::max(0.0f, static_cast(safe.x) * scaleX)), UI::PixelsToUnits(std::max(0.0f, static_cast(safe.y) * scaleY)), UI::PixelsToUnits(std::max(0.0f, static_cast(windowWidth - safe.x - safe.w) * scaleX)), UI::PixelsToUnits(std::max(0.0f, static_cast(windowHeight - safe.y - safe.h) * scaleY)) ); } void ScreenLayer::updateUIScale(SDL_Window* window) { float scale = 1.0f; if (window) { scale = SDL_GetWindowDisplayScale(window); // NOTE: sdl on wayland does support fractional scaling yet, see: libsdl-org/SDL #11775 and #12140 } SetUIScale(scale); } float ScreenLayer::GetUIScale() { return uiScale; } void ScreenLayer::SetUIScale(float scale) { if (scale <= 0.0f) { uiScale = 1.0f; } else { uiScale = scale; } UI::SetScale(uiScale); } ScreenLayer::ScreenLayer() { overlay = std::make_unique(); statusbar = std::make_unique(); statusbar->SetBgColor(UI::Color(0.0f, 0.0f, 0.0f, 1.0f)); } void ScreenLayer::OnActivate() { isActive = true; if (!screens.empty()) { screens.top()->OnActivate(); } } void ScreenLayer::OnDeactivate() { if (!screens.empty()) { screens.top()->OnDeactivate(); } isActive = false; } void ScreenLayer::OpenScreen(std::unique_ptr screen) { if (!screen) { return; } if (!screens.empty()) { screens.top()->OnDeactivate(); } screens.push(std::move(screen)); screens.top()->OnActivate(); } void ScreenLayer::CloseScreen() { if (pointerDispatchActive) { closeRequested = true; return; } if (screens.empty()) { return; } screens.top()->OnDeactivate(); screens.pop(); if (!screens.empty()) { screens.top()->OnActivate(); } } void ScreenLayer::FlushDeferredActions() { if (!closeRequested) { return; } closeRequested = false; CloseScreen(); } void ScreenLayer::SetDebugVisible(bool visible) { if (overlay) { overlay->SetVisible(visible); } } void ScreenLayer::SetFps(double fps) { if (overlay) { overlay->SetFps(fps); } } bool ScreenLayer::OnDown(std::shared_ptr stream) { if (!isActive || screens.empty()) { return false; } pointerDispatchActive = true; bool handled = screens.top()->ProcessPointerStream(stream); pointerDispatchActive = false; FlushDeferredActions(); if (!handled || screens.empty()) { return false; } pointerStreams.push_back(std::move(stream)); return true; } void ScreenLayer::OnRender() { SDL_Window* window = GPU::GetWindow(); int width = 0; int height = 0; SDL_GetWindowSizeInPixels(window, &width, &height); updateUIScale(window); updateSafeAreaInsets(window, width, height); const float uiWidth = UI::PixelsToUnits(static_cast(width)); const float uiHeight = UI::PixelsToUnits(static_cast(height)); if (!screens.empty()) { screens.top()->SetSafeAreaInsets(safeInsets); screens.top()->UpdateLayout(UI::Size(uiWidth, uiHeight)); } if (!screens.empty()) { pointerDispatchActive = true; for (auto it = pointerStreams.begin(); it != pointerStreams.end();) { screens.top()->ProcessPointerStream(*it); if ((*it)->GetComplete()) { it = pointerStreams.erase(it); } else { ++it; } } pointerDispatchActive = false; FlushDeferredActions(); } if (screens.empty()) { // screens may change screen state SDL_Event e = {}; e.type = SDL_EVENT_QUIT; SDL_PushEvent(&e); return; } if (!screens.empty()) { screens.top()->ProcessPointerHover(UI::Point( static_cast(InputHandler::CursorScaledX), static_cast(InputHandler::CursorScaledY) )); screens.top()->Render(); } if (overlay && overlay->IsVisible()) { overlay->Measure(UI::Size(uiWidth, uiHeight)); const UI::Size desired = overlay->GetDesiredSize(); const float x = safeInsets.Left + (uiWidth - safeInsets.HorizontalTotal() - desired.width) * 0.5f; overlay->Arrange(UI::Rect(x, safeInsets.Top, desired.width, desired.height)); overlay->Draw(); } statusbar->SetSize(UI::Size(uiWidth, safeInsets.Top)); statusbar->Arrange(UI::Rect(0,0,uiWidth, safeInsets.Top)); statusbar->Draw(); }