diff --git a/src/PowderToy.cpp b/src/PowderToy.cpp index e595e7129..32a28f8b1 100644 --- a/src/PowderToy.cpp +++ b/src/PowderToy.cpp @@ -96,26 +96,21 @@ void TickClient() void BlueScreen(String detailMessage) { auto &engine = ui::Engine::Ref(); - engine.g->fillrect(0, 0, engine.GetWidth(), engine.GetHeight(), 17, 114, 169, 210); + engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2)); String errorTitle = "ERROR"; String errorDetails = "Details: " + detailMessage; String errorHelp = String("An unrecoverable fault has occurred, please report the error by visiting the website below\n") + SCHEME + SERVER; int currentY = 0, width, height; int errorWidth = 0; - Graphics::textsize(errorHelp, errorWidth, height); - - engine.g->drawtext((engine.GetWidth()/2)-(errorWidth/2), ((engine.GetHeight()/2)-100) + currentY, errorTitle.c_str(), 255, 255, 255, 255); - Graphics::textsize(errorTitle, width, height); + Graphics::textsize(errorTitle, errorWidth, height); + engine.g->BlendText(engine.g->Size() / 2 - Vec2(errorWidth / 2, 100 - currentY), errorTitle, 0xFFFFFF_rgb .WithAlpha(0xFF)); currentY += height + 4; - - engine.g->drawtext((engine.GetWidth()/2)-(errorWidth/2), ((engine.GetHeight()/2)-100) + currentY, errorDetails.c_str(), 255, 255, 255, 255); - Graphics::textsize(errorTitle, width, height); - currentY += height + 4; - - engine.g->drawtext((engine.GetWidth()/2)-(errorWidth/2), ((engine.GetHeight()/2)-100) + currentY, errorHelp.c_str(), 255, 255, 255, 255); - Graphics::textsize(errorTitle, width, height); + Graphics::textsize(errorDetails, width, height); + engine.g->BlendText(engine.g->Size() / 2 - Vec2(errorWidth / 2, 100 - currentY), errorDetails, 0xFFFFFF_rgb .WithAlpha(0xFF)); currentY += height + 4; + Graphics::textsize(errorHelp, width, height); + engine.g->BlendText(engine.g->Size() / 2 - Vec2(errorWidth / 2, 100 - currentY), errorHelp, 0xFFFFFF_rgb .WithAlpha(0xFF)); //Death loop SDL_Event event; @@ -383,8 +378,7 @@ int main(int argc, char * argv[]) engine.SetForceIntegerScaling(forceIntegerScaling); engine.MomentumScroll = momentumScroll; engine.ShowAvatars = showAvatars; - engine.SetMaxSize(desktopWidth, desktopHeight); - engine.Begin(WINDOWW, WINDOWH); + engine.Begin(); engine.SetFastQuit(prefs.Get("FastQuit", true)); bool enableBluescreen = !DEBUG && !true_arg(arguments["disable-bluescreen"]); @@ -448,9 +442,9 @@ int main(int argc, char * argv[]) if (ptsaveArg.has_value()) { engine.g->Clear(); - engine.g->fillrect((engine.GetWidth()/2)-101, (engine.GetHeight()/2)-26, 202, 52, 0, 0, 0, 210); - engine.g->drawrect((engine.GetWidth()/2)-100, (engine.GetHeight()/2)-25, 200, 50, 255, 255, 255, 180); - engine.g->drawtext((engine.GetWidth()/2)-(Graphics::textwidth("Loading save...")/2), (engine.GetHeight()/2)-5, "Loading save...", style::Colour::InformationTitle.Red, style::Colour::InformationTitle.Green, style::Colour::InformationTitle.Blue, 255); + engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb); + String loadingText = "Loading save..."; + engine.g->BlendText(engine.g->Size() / 2 - Vec2(Graphics::textwidth(loadingText) / 2, 5), loadingText, style::Colour::InformationTitle); blit(engine.g->vid); try diff --git a/src/common/Vec2.h b/src/common/Vec2.h index b98a638e0..e39276fe0 100644 --- a/src/common/Vec2.h +++ b/src/common/Vec2.h @@ -345,7 +345,17 @@ private: }; public: - constexpr operator bool() const + constexpr bool operator==(Rect other) const + { + return TopLeft == other.TopLeft && BottomRight == other.BottomRight; + } + + constexpr bool operator!=(Rect other) const + { + return TopLeft != other.TopLeft || BottomRight != other.BottomRight; + } + + constexpr explicit operator bool() const { return BottomRight.X >= TopLeft.X || BottomRight.Y >= TopLeft.Y; } diff --git a/src/graphics/Graphics.h b/src/graphics/Graphics.h index b4f725439..80b0fb3cc 100644 --- a/src/graphics/Graphics.h +++ b/src/graphics/Graphics.h @@ -67,11 +67,21 @@ class Graphics: public RasterDrawMethods friend struct RasterDrawMethods; public: + Vec2 Size() const + { + return video.Size(); + } + pixel const *Data() const { return video.data(); } + pixel *Data() + { + return video.data(); + } + [[deprecated("Use Data()")]] pixel *vid = video.data(); diff --git a/src/gui/dialogues/ConfirmPrompt.cpp b/src/gui/dialogues/ConfirmPrompt.cpp index 091b5d5f1..b13073534 100644 --- a/src/gui/dialogues/ConfirmPrompt.cpp +++ b/src/gui/dialogues/ConfirmPrompt.cpp @@ -36,7 +36,7 @@ ConfirmPrompt::ConfirmPrompt(String title, String message, ResultCallback callba if (messageLabel->Size.Y < messagePanel->Size.Y) messagePanel->Size.Y = messageLabel->Size.Y+4; Size.Y += messagePanel->Size.Y+12; - Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2; + Position.Y = (GetGraphics()->Size().Y - Size.Y)/2; ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X-75, 16), "Cancel"); cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; diff --git a/src/gui/dialogues/ErrorMessage.cpp b/src/gui/dialogues/ErrorMessage.cpp index 52165c15f..824bc5d8c 100644 --- a/src/gui/dialogues/ErrorMessage.cpp +++ b/src/gui/dialogues/ErrorMessage.cpp @@ -27,7 +27,7 @@ ErrorMessage::ErrorMessage(String title, String message, DismissCallback callbac AddComponent(messageLabel); Size.Y += messageLabel->Size.Y+12; - Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2; + Position.Y = (GetGraphics()->Size().Y - Size.Y)/2; ui::Button * okayButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "Dismiss"); okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; diff --git a/src/gui/dialogues/InformationMessage.cpp b/src/gui/dialogues/InformationMessage.cpp index 3257a610d..b92aa1e54 100644 --- a/src/gui/dialogues/InformationMessage.cpp +++ b/src/gui/dialogues/InformationMessage.cpp @@ -46,7 +46,7 @@ InformationMessage::InformationMessage(String title, String message, bool large) if (messageLabel->Size.Y < messagePanel->Size.Y) messagePanel->Size.Y = messageLabel->Size.Y+4; Size.Y += messagePanel->Size.Y+12; - Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2; + Position.Y = (GetGraphics()->Size().Y - Size.Y) / 2; } ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 16), title); diff --git a/src/gui/interface/Engine.cpp b/src/gui/interface/Engine.cpp index d33a3cd1e..431782cad 100644 --- a/src/gui/interface/Engine.cpp +++ b/src/gui/interface/Engine.cpp @@ -17,7 +17,6 @@ Engine::Engine(): FrameIndex(0), altFullscreen(false), resizable(false), - lastBuffer(NULL), state_(NULL), windowTargetPosition(0, 0), break_(false), @@ -27,9 +26,7 @@ Engine::Engine(): mousex_(0), mousey_(0), mousexp_(0), - mouseyp_(0), - maxWidth(0), - maxHeight(0) + mouseyp_(0) { SetFps(FpsLimit); // populate dt with whatever that makes any sort of sense } @@ -38,21 +35,17 @@ Engine::~Engine() { delete state_; //Dispose of any Windows. - while(!windows.empty()) + while (!windows.empty()) { delete windows.top(); windows.pop(); } - free(lastBuffer); } -void Engine::Begin(int width, int height) +void Engine::Begin() { //engine is now ready running_ = true; - - width_ = width; - height_ = height; } void Engine::Break() @@ -80,16 +73,15 @@ void Engine::ConfirmExit() void Engine::ShowWindow(Window * window) { - windowOpenState = 0; if (state_) ignoreEvents = true; if(window->Position.X==-1) { - window->Position.X = (width_-window->Size.X)/2; + window->Position.X = (g->Size().X - window->Size.X) / 2; } if(window->Position.Y==-1) { - window->Position.Y = (height_-window->Size.Y)/2; + window->Position.Y = (g->Size().Y - window->Size.Y) / 2; } /*if(window->Position.Y > 0) { @@ -98,13 +90,8 @@ void Engine::ShowWindow(Window * window) }*/ if(state_) { - if(lastBuffer) - { - prevBuffers.push(lastBuffer); - } - lastBuffer = (pixel*)malloc((width_ * height_) * PIXELSIZE); - - memcpy(lastBuffer, g->vid, (width_ * height_) * PIXELSIZE); + frozenGraphics.emplace(FrozenGraphics{0, std::make_unique(g->Size().X * g->Size().Y)}); + std::copy_n(g->Data(), g->Size().X * g->Size().Y, frozenGraphics.top().screen.get()); windows.push(state_); mousePositions.push(ui::Point(mousex_, mousey_)); @@ -120,16 +107,7 @@ int Engine::CloseWindow() { if(!windows.empty()) { - if (lastBuffer) - { - free(lastBuffer); - lastBuffer = NULL; - } - if(!prevBuffers.empty()) - { - lastBuffer = prevBuffers.top(); - prevBuffers.pop(); - } + frozenGraphics.pop(); state_ = windows.top(); windows.pop(); @@ -168,17 +146,6 @@ int Engine::CloseWindow() } }*/ -void Engine::SetSize(int width, int height) -{ - width_ = width; - height_ = height; -} - -void Engine::SetMaxSize(int width, int height) -{ - maxWidth = width; - maxHeight = height; -} void Engine::Tick() { @@ -207,13 +174,21 @@ void Engine::Tick() void Engine::Draw() { - if(lastBuffer && !(state_ && state_->Position.X == 0 && state_->Position.Y == 0 && state_->Size.X == width_ && state_->Size.Y == height_)) + if (!frozenGraphics.empty() && !(state_ && RectSized(state_->Position, state_->Size) == g->Size().OriginRect())) { - g->Clear(); - memcpy(g->vid, lastBuffer, (width_ * height_) * PIXELSIZE); - if(windowOpenState < 20) - windowOpenState++; - g->fillrect(0, 0, width_, height_, 0, 0, 0, int(255-std::pow(.98, windowOpenState)*255)); + auto &frozen = frozenGraphics.top(); + std::copy_n(frozen.screen.get(), g->Size().X * g->Size().Y, g->Data()); + if (frozen.fadeTicks <= maxFadeTicks) + { + // from 0x00 at 0 to about 0x54 at 20 + uint8_t alpha = (1 - std::pow(0.98, frozen.fadeTicks)) * 0xFF; + g->BlendFilledRect(g->Size().OriginRect(), 0x000000_rgb .WithAlpha(alpha)); + } + // If this is the last frame in the fade, save what the faded image looks like + if (frozen.fadeTicks == maxFadeTicks) + std::copy_n(g->Data(), g->Size().X * g->Size().Y, frozen.screen.get()); + if (frozen.fadeTicks <= maxFadeTicks) + frozen.fadeTicks++; } else { @@ -322,11 +297,6 @@ void Engine::onMouseWheel(int x, int y, int delta) state_->DoMouseWheel(x, y, delta); } -void Engine::onResize(int newWidth, int newHeight) -{ - SetSize(newWidth, newHeight); -} - void Engine::onClose() { if (state_) diff --git a/src/gui/interface/Engine.h b/src/gui/interface/Engine.h index 9656527ff..2ce8f37c0 100644 --- a/src/gui/interface/Engine.h +++ b/src/gui/interface/Engine.h @@ -1,9 +1,10 @@ #pragma once +#include +#include #include "common/String.h" #include "common/ExplicitSingleton.h" #include "graphics/Pixel.h" #include "gui/interface/Point.h" -#include class Graphics; namespace ui @@ -33,11 +34,10 @@ namespace ui void onKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt); void onTextInput(String text); void onTextEditing(String text, int start); - void onResize(int newWidth, int newHeight); void onClose(); void onFileDrop(ByteString filename); - void Begin(int width, int height); + void Begin(); inline bool Running() { return running_; } inline bool Broken() { return break_; } inline long unsigned int LastTick() { return lastTick; } @@ -70,14 +70,6 @@ namespace ui inline int GetMouseButton() { return mouseb_; } inline int GetMouseX() { return mousex_; } inline int GetMouseY() { return mousey_; } - inline int GetWidth() { return width_; } - inline int GetHeight() { return height_; } - inline int GetMaxWidth() { return maxWidth; } - inline int GetMaxHeight() { return maxHeight; } - - void SetMaxSize(int width, int height); - - inline void SetSize(int width, int height); void StartTextInput(); void StopTextInput(); @@ -102,16 +94,23 @@ namespace ui float dt; float fps; - pixel * lastBuffer; - std::stack prevBuffers; std::stack windows; std::stack mousePositions; //Window* statequeued_; Window* state_; Point windowTargetPosition; - int windowOpenState; bool ignoreEvents = false; + // saved appearances of windows that are in the backround and + // thus are not currently being redrawn + struct FrozenGraphics + { + int fadeTicks; + std::unique_ptr screen; + }; + constexpr static int maxFadeTicks = 20; + std::stack frozenGraphics; + bool running_; bool break_; bool FastQuit; @@ -122,11 +121,6 @@ namespace ui int mousey_; int mousexp_; int mouseyp_; - int width_; - int height_; - - int maxWidth; - int maxHeight; String textEditingBuf; diff --git a/src/gui/interface/Panel.cpp b/src/gui/interface/Panel.cpp index 4eb0828bf..71d51e994 100644 --- a/src/gui/interface/Panel.cpp +++ b/src/gui/interface/Panel.cpp @@ -67,31 +67,21 @@ void Panel::Draw(const Point& screenPos) // draw ourself first XDraw(screenPos); - int x = screenPos.X; - int y = screenPos.Y; - int w = Size.X; - int h = Size.Y; - ui::Engine::Ref().g->SetClipRect(x, y, w, h); // old cliprect is now in x, y, w, h + auto clip = RectSized(screenPos, Size); + GetGraphics()->SwapClipRect(clip); // attempt to draw all children - for (size_t i = 0; i < children.size(); ++i) - { + for (auto const child : children) // the component must be visible - if (children[i]->Visible) + if (child->Visible) { + auto rect = RectSized(child->Position + ViewportPosition, child->Size); //check if the component is in the screen, draw if it is - if (children[i]->Position.X + ViewportPosition.X + children[i]->Size.X >= 0 && - children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y >= 0 && - children[i]->Position.X + ViewportPosition.X < ui::Engine::Ref().GetWidth() && - children[i]->Position.Y + ViewportPosition.Y < ui::Engine::Ref().GetHeight() ) - { - Point scrpos = screenPos + children[i]->Position + ViewportPosition; - children[i]->Draw(scrpos); - } + if (rect & Size.OriginRect()) + child->Draw(screenPos + rect.TopLeft); } - } - ui::Engine::Ref().g->SetClipRect(x, y, w, h); // apply old cliprect + GetGraphics()->SwapClipRect(clip); // apply old cliprect } void Panel::Tick(float dt) diff --git a/src/gui/interface/Window.cpp b/src/gui/interface/Window.cpp index 45507ce07..b6db0099a 100644 --- a/src/gui/interface/Window.cpp +++ b/src/gui/interface/Window.cpp @@ -179,85 +179,53 @@ void Window::DoFileDrop(ByteString filename) void Window::DoDraw() { OnDraw(); - for (int i = 0, sz = Components.size(); i < sz; ++i) - if (Components[i]->Visible && ((Components[i] != focusedComponent_ && Components[i] != hoverComponent) || Components[i]->GetParent())) + auto drawChild = [this](Component *child) { + if (child->Visible) { - Point scrpos(Components[i]->Position.X + Position.X, Components[i]->Position.Y + Position.Y); - if (AllowExclusiveDrawing) - { - Components[i]->Draw(scrpos); - } - else - { - if (scrpos.X + Components[i]->Size.X >= 0 && - scrpos.Y + Components[i]->Size.Y >= 0 && - scrpos.X < ui::Engine::Ref().GetWidth() && - scrpos.Y < ui::Engine::Ref().GetHeight()) - { - Components[i]->Draw(scrpos); - } - } + auto rect = RectSized(Position + child->Position, child->Size); + if (AllowExclusiveDrawing || bool(rect & GetGraphics()->Size().OriginRect())) + child->Draw(rect.TopLeft); + } + }; + for (auto child : Components) + if ((child != focusedComponent_ && child != hoverComponent) || child->GetParent()) + { + drawChild(child); + if (debugMode) - { - if (focusedComponent_==Components[i]) - { - ui::Engine::Ref().g->fillrect(Components[i]->Position.X+Position.X, Components[i]->Position.Y+Position.Y, Components[i]->Size.X, Components[i]->Size.Y, 0, 255, 0, 90); - } - else - { - ui::Engine::Ref().g->fillrect(Components[i]->Position.X+Position.X, Components[i]->Position.Y+Position.Y, Components[i]->Size.X, Components[i]->Size.Y, 255, 0, 0, 90); - } - } + GetGraphics()->BlendFilledRect(RectSized(Position + child->Position, child->Size), + (focusedComponent_ == child ? 0x00FF00_rgb : 0xFF0000_rgb).WithAlpha(0x5A)); } // the component the mouse is hovering over and the focused component are always drawn last - if (hoverComponent && hoverComponent->Visible && hoverComponent->GetParent() == NULL) + if (hoverComponent && hoverComponent->GetParent() == NULL) + drawChild(hoverComponent); + if (focusedComponent_ && focusedComponent_ != hoverComponent && focusedComponent_->GetParent() == NULL) + drawChild(focusedComponent_); + if (debugMode && focusedComponent_) { - Point scrpos(hoverComponent->Position.X + Position.X, hoverComponent->Position.Y + Position.Y); - if ((scrpos.X + hoverComponent->Size.X >= 0 && - scrpos.Y + hoverComponent->Size.Y >= 0 && - scrpos.X < ui::Engine::Ref().GetWidth() && - scrpos.Y < ui::Engine::Ref().GetHeight() - ) || AllowExclusiveDrawing) - { - hoverComponent->Draw(scrpos); - } + Graphics *g = ui::Engine::Ref().g; + + auto invPos = Size - (focusedComponent_->Position + focusedComponent_->Size); + String posText = String::Build( + "Position: L ", focusedComponent_->Position.X, + ", R ", invPos.X, + ", T: ", focusedComponent_->Position.Y, + ", B: ", invPos.Y + ); + String sizeText = String::Build( + "Size: ", focusedComponent_->Size.X, + ", ", focusedComponent_->Size.Y + ); + + auto pos = focusedComponent_->Position + Position + Vec2(focusedComponent_->Size.X + 5, 0); + pos.X = std::min(pos.X, g->Size().X - Graphics::textwidth(posText) - 5); + pos.X = std::min(pos.X, g->Size().X - Graphics::textwidth(sizeText) - 5); + + g->BlendText(pos + Vec2(0, 1), posText, 0x000000_rgb .WithAlpha(0xC8)); + g->BlendText(pos + Vec2(0, 0), posText, 0xFFFFFF_rgb .WithAlpha(0xFF)); + g->BlendText(pos + Vec2(0, 13), sizeText, 0x000000_rgb .WithAlpha(0xC8)); + g->BlendText(pos + Vec2(0, 12), sizeText, 0xFFFFFF_rgb .WithAlpha(0xFF)); } - if (focusedComponent_ && focusedComponent_ != hoverComponent && focusedComponent_->Visible && focusedComponent_->GetParent() == NULL) - { - Point scrpos(focusedComponent_->Position.X + Position.X, focusedComponent_->Position.Y + Position.Y); - if ((scrpos.X + focusedComponent_->Size.X >= 0 && - scrpos.Y + focusedComponent_->Size.Y >= 0 && - scrpos.X < ui::Engine::Ref().GetWidth() && - scrpos.Y < ui::Engine::Ref().GetHeight() - ) || AllowExclusiveDrawing) - { - focusedComponent_->Draw(scrpos); - } - } - if (debugMode) - { - if (focusedComponent_) - { - int xPos = focusedComponent_->Position.X+focusedComponent_->Size.X+5+Position.X; - Graphics * g = ui::Engine::Ref().g; - String tempString, tempString2; - - tempString = String::Build("Position: L ", focusedComponent_->Position.X, ", R ", Size.X-(focusedComponent_->Position.X+focusedComponent_->Size.X), ", T: ", focusedComponent_->Position.Y, ", B: ", Size.Y-(focusedComponent_->Position.Y+focusedComponent_->Size.Y)); - tempString2 = String::Build("Size: ", focusedComponent_->Size.X, ", ", focusedComponent_->Size.Y); - - if (Graphics::textwidth(tempString)+xPos > WINDOWW) - xPos = WINDOWW-(Graphics::textwidth(tempString)+5); - if (Graphics::textwidth(tempString2)+xPos > WINDOWW) - xPos = WINDOWW-(Graphics::textwidth(tempString2)+5); - - g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+1, tempString, 0, 0, 0, 200); - g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y, tempString, 255, 255, 255, 255); - g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+13, tempString2, 0, 0, 0, 200); - g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+12, tempString2, 255, 255, 255, 255); - } - return; - } - } void Window::DoTick(float dt) diff --git a/src/gui/options/OptionsView.cpp b/src/gui/options/OptionsView.cpp index 0c87cd376..221e4e4c8 100644 --- a/src/gui/options/OptionsView.cpp +++ b/src/gui/options/OptionsView.cpp @@ -17,6 +17,7 @@ #include "gui/interface/Label.h" #include "gui/interface/Textbox.h" #include "gui/interface/DirectionSelector.h" +#include "PowderToySDL.h" #include #include #include @@ -264,7 +265,7 @@ OptionsView::OptionsView(): scale->AddOption(std::pair(String::Build(ix_scale), ix_scale)); ix_scale += 1; } - while (ui::Engine::Ref().GetMaxWidth() >= ui::Engine::Ref().GetWidth() * ix_scale && ui::Engine::Ref().GetMaxHeight() >= ui::Engine::Ref().GetHeight() * ix_scale); + while (desktopWidth >= GetGraphics()->Size().X * ix_scale && desktopHeight >= GetGraphics()->Size().Y * ix_scale); if (!current_scale_valid) scale->AddOption(std::pair("current", current_scale)); }