Something went wrong. Try again.
art software for the rest of us
drawing paint image sdl art
Something went wrong. Try again.
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196#include "ScreenLayer.h"#include "../GPU.h"#include "../Input/InputHandler.h"#include "../UserInterface/Engine/Scale.h"#include <SDL3/SDL_events.h>#include <SDL3/SDL_video.h>#include <algorithm>
std::stack<std::unique_ptr<Screen>> ScreenLayer::screens;std::list<std::shared_ptr<PointerStream>> ScreenLayer::pointerStreams;std::unique_ptr<DebugOverlay> ScreenLayer::overlay;std::unique_ptr<UI::Panel> 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<float>(pixelWidth) / static_cast<float>(windowWidth); const float scaleY = static_cast<float>(pixelHeight) / static_cast<float>(windowHeight); safeInsets = UI::Thickness( UI::PixelsToUnits(std::max(0.0f, static_cast<float>(safe.x) * scaleX)), UI::PixelsToUnits(std::max(0.0f, static_cast<float>(safe.y) * scaleY)), UI::PixelsToUnits(std::max(0.0f, static_cast<float>(windowWidth - safe.x - safe.w) * scaleX)), UI::PixelsToUnits(std::max(0.0f, static_cast<float>(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<DebugOverlay>(); statusbar = std::make_unique<UI::Panel>(); 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> 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<PointerStream> 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<float>(width)); const float uiHeight = UI::PixelsToUnits(static_cast<float>(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<float>(InputHandler::CursorScaledX), static_cast<float>(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();}