#include "mx_window.hpp" #include "dimension.hpp" #include "mx_abstract_control.hpp" #include "mx_system_bar.hpp" namespace mx { extern bool cursor_shown; Window::Window(mxApp &app) : x{0}, y{0}, w{320}, h{240}, shown{false}, minimizeHovered(SDL_FALSE), closeHovered(SDL_FALSE), maximizeHovered(SDL_FALSE), title{"windwow"} { dim_w = app.width; dim_h = app.height; } Window::~Window() { std::cout << "MasterX: Releasing Window: " << title << "\n"; } void Window::create(DimensionContainer *dim, const std::string &n, const int xx, const int yy, const int ww, const int hh) { title = n; x = xx; y = yy; w = ww; h = hh; orig_x = x; orig_y = y; this->dim = dim; } void Window::destroyWindow() { dim->destroyWindow(this); } bool Window::isPointInside(int xx, int yy) { return (xx >= x && xx <= x + w && yy >= y && yy <= y + h); } void Window::draw(mxApp &app) { if (shown == false || (minimized == true && isMinimizing == false)) return; if (isMinimizing) { x += (minTargetX - x) / minAnimationStep; y += (minTargetY - y) / minAnimationStep; w += (minTargetW - w) / minAnimationStep; h += (minTargetH - h) / minAnimationStep; if (abs(x - minTargetX) < 1 && abs(y - minTargetY) < 1 && abs(w - minTargetW) < 1 && abs(h - minTargetH) < 1) { isMinimizing = false; minimized = true; minimizeHovered = SDL_FALSE; maximizeHovered = SDL_FALSE; closeHovered = SDL_FALSE; } } if (isRestoring) { x += (restoreTargetX - x) / restoreAnimationStep; y += (restoreTargetY - y) / restoreAnimationStep; w += (restoreTargetW - w) / restoreAnimationStep; h += (restoreTargetH - h) / restoreAnimationStep; if (abs(x - restoreTargetX) < 1 && abs(y - restoreTargetY) < 1 && abs(w - restoreTargetW) < 1 && abs(h - restoreTargetH) < 1) { x = restoreTargetX; y = restoreTargetY; w = restoreTargetW; h = restoreTargetH; isRestoring = false; minimized = false; minimizeHovered = SDL_FALSE; maximizeHovered = SDL_FALSE; closeHovered = SDL_FALSE; for (auto &c : children) { c->setWindowPos(x, y); c->resizeWindow(w, h); } } x = restoreTargetX; y = restoreTargetY; w = restoreTargetW; h = restoreTargetH; isRestoring = false; dragging = false; } SDL_Rect rc = {x, y, w, h}; SDL_SetRenderDrawColor(app.ren, 205, 205, 205, 255); SDL_RenderFillRect(app.ren, &rc); int startGray = 165; int endGray = 205; for (int i = 0; i < h; ++i) { int grayValue = startGray + (endGray - startGray) * i / h; SDL_SetRenderDrawColor(app.ren, grayValue, grayValue, grayValue, 255); SDL_RenderDrawLine(app.ren, x, y + i, x + w, y + i); } drawMenubar(app); for (auto &c : children) { c->setWindowPos(x, y); c->draw(app); } } bool Window::isVisible() const { return shown; } bool Window::isDraw() const { if (shown == false || (minimized == true && isMinimizing == false)) return false; return true; } void Window::drawMenubar(mxApp &app) { SDL_Color lightBlue = {173, 216, 230}; SDL_Color darkBlue = {0, 0, 139}; int titleBarHeight = 30; for (int i = 0; i < titleBarHeight; ++i) { int red = lightBlue.r + (darkBlue.r - lightBlue.r) * i / titleBarHeight; int green = lightBlue.g + (darkBlue.g - lightBlue.g) * i / titleBarHeight; int blue = lightBlue.b + (darkBlue.b - lightBlue.b) * i / titleBarHeight; SDL_SetRenderDrawColor(app.ren, red, green, blue, 255); SDL_RenderDrawLine(app.ren, x, y + i, x + w, y + i); } int buttonSize = 19; int buttonPadding = 5; minimizeButton = {x + w - 3 * (buttonSize + buttonPadding), y + 5, buttonSize, buttonSize}; maximizeButton = {x + w - 2 * (buttonSize + buttonPadding), y + 5, buttonSize, buttonSize}; closeButton = {x + w - (buttonSize + buttonPadding), y + 5, buttonSize, buttonSize}; auto drawButtonWithBevel = [&](SDL_Rect button, bool hovered) { for (int i = 0; i < button.h; ++i) { int gray = 200 - i * 4; SDL_SetRenderDrawColor(app.ren, gray, gray, gray, 255); SDL_RenderDrawLine(app.ren, button.x, button.y + i, button.x + button.w, button.y + i); } SDL_SetRenderDrawColor(app.ren, 255, 255, 255, 255); SDL_RenderDrawLine(app.ren, button.x, button.y, button.x + button.w - 1, button.y); SDL_RenderDrawLine(app.ren, button.x, button.y, button.x, button.y + button.h - 1); SDL_SetRenderDrawColor(app.ren, 100, 100, 100, 255); SDL_RenderDrawLine(app.ren, button.x, button.y + button.h - 1, button.x + button.w - 1, button.y + button.h - 1); SDL_RenderDrawLine(app.ren, button.x + button.w - 1, button.y, button.x + button.w - 1, button.y + button.h - 1); if (hovered) { SDL_SetRenderDrawColor(app.ren, 255, 0, 0, 255); SDL_RenderDrawRect(app.ren, &button); } }; drawButtonWithBevel(minimizeButton, minimizeHovered); drawButtonWithBevel(maximizeButton, maximizeHovered); drawButtonWithBevel(closeButton, closeHovered); SDL_SetRenderDrawColor(app.ren, 255, 255, 255, 255); int padding = 4; SDL_RenderDrawLine(app.ren, maximizeButton.x + padding - 1, maximizeButton.y + padding, maximizeButton.x + buttonSize - padding, maximizeButton.y + padding); SDL_RenderDrawLine(app.ren, maximizeButton.x + padding - 1, maximizeButton.y + padding, maximizeButton.x + padding - 1, maximizeButton.y + buttonSize - padding); SDL_RenderDrawLine(app.ren, maximizeButton.x + padding - 1, maximizeButton.y + buttonSize - padding, maximizeButton.x + buttonSize - padding, maximizeButton.y + buttonSize - padding); SDL_RenderDrawLine(app.ren, maximizeButton.x + buttonSize - padding, maximizeButton.y + padding, maximizeButton.x + buttonSize - padding, maximizeButton.y + buttonSize - padding); SDL_Surface *minimizeSurface = TTF_RenderText_Blended(app.font, "_", {255, 255, 255}); SDL_Texture *minimizeTexture = SDL_CreateTextureFromSurface(app.ren, minimizeSurface); int minimizeTextW = 0, minimizeTextH = 0; SDL_QueryTexture(minimizeTexture, nullptr, nullptr, &minimizeTextW, &minimizeTextH); SDL_Rect minimizeTextRect = {minimizeButton.x + (buttonSize - minimizeTextW) / 2, minimizeButton.y + (buttonSize - minimizeTextH) / 2, minimizeTextW, minimizeTextH}; SDL_RenderCopy(app.ren, minimizeTexture, nullptr, &minimizeTextRect); SDL_DestroyTexture(minimizeTexture); SDL_FreeSurface(minimizeSurface); SDL_Surface *closeSurface = TTF_RenderText_Blended(app.font, "X", {255, 255, 255}); SDL_Texture *closeTexture = SDL_CreateTextureFromSurface(app.ren, closeSurface); int closeTextW = 0, closeTextH = 0; SDL_QueryTexture(closeTexture, nullptr, nullptr, &closeTextW, &closeTextH); SDL_Rect closeTextRect = {closeButton.x + (buttonSize - closeTextW) / 2, closeButton.y + (buttonSize - closeTextH) / 2, closeTextW, closeTextH}; SDL_RenderCopy(app.ren, closeTexture, nullptr, &closeTextRect); SDL_DestroyTexture(closeTexture); SDL_FreeSurface(closeSurface); SDL_SetRenderDrawColor(app.ren, 255, 255, 255, 255); SDL_RenderDrawLine(app.ren, x, y + titleBarHeight, x + w - 1, y + titleBarHeight); SDL_RenderDrawLine(app.ren, x, y, x + w - 1, y); SDL_RenderDrawLine(app.ren, x, y, x, y + h - 1); SDL_SetRenderDrawColor(app.ren, 192, 192, 192, 255); SDL_RenderDrawLine(app.ren, x + w, y, x + w, y + h - 1); SDL_RenderDrawLine(app.ren, x, y + h - 1, x + w, y + h - 1); SDL_SetRenderDrawColor(app.ren, 192, 192, 192, 255); SDL_RenderDrawLine(app.ren, x + 1, y + titleBarHeight + 1, x + w - 2, y + titleBarHeight + 1); SDL_RenderDrawLine(app.ren, x + w - 1, y + titleBarHeight + 1, x + w - 1, y + h - 2); SDL_SetRenderDrawColor(app.ren, 64, 64, 64, 255); SDL_Surface *surface = TTF_RenderText_Blended(app.font, title.c_str(), {255, 255, 255}); SDL_Texture *texture = SDL_CreateTextureFromSurface(app.ren, surface); int textW = 0, textH = 0; SDL_QueryTexture(texture, nullptr, nullptr, &textW, &textH); SDL_Rect textRect = {x + 25, y + 5, textW, textH}; SDL_RenderCopy(app.ren, texture, nullptr, &textRect); SDL_DestroyTexture(texture); SDL_FreeSurface(surface); int iconWidth = 16; int iconHeight = 16; int iconX = x + 5; int iconY = y + (titleBarHeight - iconHeight) / 2; SDL_Rect iconRect = {iconX, iconY, iconWidth, iconHeight}; SDL_RenderCopy(app.ren, app.icon, nullptr, &iconRect); } void Window::show(bool b) { shown = b; stateChanged(false, false, shown); } void Window::getRect(SDL_Rect &rc) { rc.x = x; rc.y = y; rc.w = w; rc.h = h; } void Window::setRect(const SDL_Rect &rc) { x = rc.x; y = rc.y; w = rc.w; h = rc.h; } void Window::removeAtClose(bool value) { remove_on = value; } bool Window::event(mxApp &app, SDL_Event &e) { static Uint32 lastClickTime = 0; if (e.type == SDL_MOUSEMOTION) { SDL_Point mousePoint = {e.motion.x, e.motion.y}; minimizeHovered = SDL_PointInRect(&mousePoint, &minimizeButton); closeHovered = SDL_PointInRect(&mousePoint, &closeButton); maximizeHovered = SDL_PointInRect(&mousePoint, &maximizeButton); if (dragging) { if (e.motion.y > 0) { x = e.motion.x - dragOffsetX; y = e.motion.y - dragOffsetY; isMinimizing = false; isRestoring = false; return true; } } } if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT) { SDL_Point mousePoint = {e.button.x, e.button.y}; SDL_Rect titleBarRect = {x, y, w, 30}; if (SDL_PointInRect(&mousePoint, &titleBarRect) && !SDL_PointInRect(&mousePoint, &minimizeButton) && !SDL_PointInRect(&mousePoint, &closeButton)) { dragging = true; dragOffsetX = e.button.x - x; dragOffsetY = e.button.y - y; } if (SDL_PointInRect(&mousePoint, &closeButton)) { show(false); if (remove_on) { destroyWindow(); return true; } } if (SDL_PointInRect(&mousePoint, &minimizeButton)) { if (!minimized) { minimize(true); if (systemBar) systemBar->addMinimizedWindow(this); minimizeHovered = SDL_FALSE; } else { systemBar->restoreWindow(this); if (systemBar) systemBar->restoreWindow(this); minimizeHovered = SDL_FALSE; } } if (canResize() == true && minimized == false && SDL_PointInRect(&mousePoint, &maximizeButton)) { maximize(!maximized); shown = true; if (can_resize) { for (auto &c : children) { if (c->show) { c->setWindowPos(x, y); c->resizeWindow(w, h); } } } } } if (e.type == SDL_MOUSEBUTTONUP && e.button.button == SDL_BUTTON_LEFT) { dragging = false; isMinimizing = false; isRestoring = false; } if (!shown || minimized) { if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT) { Uint32 currentTime = SDL_GetTicks(); SDL_Point mousePoint = {e.button.x, e.button.y}; SDL_Rect titleBarRect = {x, y, w, 30}; if (SDL_PointInRect(&mousePoint, &titleBarRect)) { if (currentTime - lastClickTime < 500) { if (minimized) { minimize(false); if (systemBar) systemBar->restoreWindow(this); return true; } } lastClickTime = currentTime; } } return false; } if (e.type == SDL_WINDOWEVENT) { if (e.window.event == SDL_WINDOWEVENT_LEAVE) { minimizeHovered = SDL_FALSE; closeHovered = SDL_FALSE; maximizeHovered = SDL_FALSE; } } for (auto &c : children) { if (c->event(app, e)) return true; } return false; } void Window::setSystemBar(SystemBar *s) { systemBar = s; } Control *Window::getControl() { if (children.size() > 0) return children[children.size() - 1].get(); std::cerr << "MasterX System: Trying to acess out of bounds control.\n"; return nullptr; } Control *Window::getControl(int index) { if (index >= 0 && index < static_cast<int>(children.size())) return children[index].get(); std::cerr << "MasterX System: Trying to acess out of bounds control.\n"; return nullptr; } void Window::minimize(bool m) { if (!systemBar) { std::cerr << "MasterX: Window " << this->title << " missing system bar link\n"; return; } if (m) { if (!minimized) { originalX = x; originalY = y; originalWidth = w; originalHeight = h; } isMinimizing = true; minimized = true; minTargetX = 5; minTargetY = systemBar->yPos - 50; minTargetW = 200; minTargetH = 5; originalX = x; originalY = y; originalWidth = w; originalHeight = h; if (isVisible()) { for (auto &c : children) { c->setShow(false); } } w = 200; h = 25; } else if (!m && minimized) { minimized = false; isRestoring = true; restoreTargetX = originalX; restoreTargetY = originalY; restoreTargetW = originalWidth; restoreTargetH = originalHeight; shown = true; dragging = false; if (isVisible()) { for (auto &c : children) { c->setShow(true); } } } stateChanged(m, false, false); } void Window::maximize(bool m) { if (m && !maximized) { oldX = x; oldY = y; oldW = w; oldH = h; x = 0; y = 0; w = dim_w; h = dim_h - 50; } else if (!m && maximized) { x = oldX; y = oldY; w = oldW; h = oldH; } maximized = m; stateChanged(false, maximized, false); } bool Window::reload() const { return reload_window; } void Window::setReload(bool r) { reload_window = r; } void Window::setCanResize(bool r) { can_resize = r; } bool Window::canResize() const { return can_resize; } } // namespace mx