#include "ui.h" FloatingWindow createFloatingWindow(const char* title, Rectangle rect) { FloatingWindow window = (FloatingWindow){ .rect = rect, .minimized = false, .moving = false, .resizing = false, .callback = NULL, .contentSize = Vector2Zero(), .scroll = Vector2Zero(), .hasFocus = false, .data = NULL }; memcpy(window.title, title, UI_WINDOW_TITLE_MAX * sizeof(char)); return window; } void floatingWindowTransformCollisionCheck(FloatingWindow* window) { int closeTitleSizeDeltaHalf = (RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - RAYGUI_WINDOW_CLOSEBUTTON_SIZE) / 2; Vector2 mousePosition = GetMousePosition(); Rectangle titleCollisionRect = (Rectangle){ window->rect.x, window->rect.y, window->rect.width - (RAYGUI_WINDOW_CLOSEBUTTON_SIZE + closeTitleSizeDeltaHalf), RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT }; Rectangle resizeCollisionRect = (Rectangle){ window->rect.x + window->rect.width - 20, window->rect.y + window->rect.height - 20, 20, 20 }; if (CheckCollisionPointRec(mousePosition, titleCollisionRect)) { window->moving = true; } else if (!window->minimized && CheckCollisionPointRec(mousePosition, resizeCollisionRect)) { window->resizing = true; } } void updateFloatingWindowMoving(FloatingWindow* window) { Vector2 mouseDelta = GetMouseDelta(); window->rect.x += mouseDelta.x; window->rect.y += mouseDelta.y; if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { window->moving = false; // Clamp window position keep it inside the application area. if (window->rect.x < 0) { window->rect.x = 0; } else if (window->rect.x > GetScreenWidth() - window->rect.width) { window->rect.x = GetScreenWidth() - window->rect.width; } if (window->rect.y < 0) { window->rect.y = 0; } else if (window->rect.y > GetScreenHeight()) { window->rect.y = GetScreenHeight() - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT; } } } void updateFloatingWindowResizing(FloatingWindow* window) { Vector2 mousePosition = GetMousePosition(); if (mousePosition.x > window->rect.x) { window->rect.width = mousePosition.x - window->rect.x; } if (mousePosition.y > window->rect.y) { window->rect.height = mousePosition.y - window->rect.y; } // Clamp window size to an arbitrary minimum value and the window size as // the maximum. if (window->rect.width < 100) { window->rect.width = 100; } else if (window->rect.width > GetScreenWidth()) { window->rect.width = GetScreenWidth(); } if (window->rect.height < 100) { window->rect.height = 100; } else if (window->rect.height > GetScreenHeight()) { window->rect.height = GetScreenHeight(); } if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { window->resizing = false; } } void updateFloatingWindowMinimized(FloatingWindow* window) { int closeTitleSizeDeltaHalf = (RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - RAYGUI_WINDOW_CLOSEBUTTON_SIZE) / 2; GuiStatusBar( (Rectangle){window->rect.x, window->rect.y, window->rect.width, RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT}, window->title); if (GuiButton( (Rectangle){window->rect.x + window->rect.width - RAYGUI_WINDOW_CLOSEBUTTON_SIZE - closeTitleSizeDeltaHalf, window->rect.y + closeTitleSizeDeltaHalf, RAYGUI_WINDOW_CLOSEBUTTON_SIZE, RAYGUI_WINDOW_CLOSEBUTTON_SIZE}, "#120#")) { window->minimized = false; } } FocusCommand updateFloatingWindowNotMinimized(FloatingWindow* window, WindowManager* wm, Game* game) { FocusCommand focus = NO_FOCUS_ACTION; if (wm->enabled) { window->minimized = GuiWindowBox( (Rectangle){window->rect.x, window->rect.y, window->rect.width, window->rect.height}, window->title); } // Scissor and draw content within a scroll panel. if (window->callback != NULL) { Rectangle scissor; // Handle scrolling. if (wm->enabled) { GuiScrollPanel( (Rectangle){ window->rect.x, window->rect.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT, window->rect.width, window->rect.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT}, NULL, (Rectangle){ window->rect.x, window->rect.y, window->contentSize.x, window->contentSize.y}, &window->scroll, &scissor); } else { scissor = (Rectangle){ window->rect.x, window->rect.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT, window->rect.width, window->rect.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT }; } bool requireScissor = window->rect.width < window->contentSize.x || window->rect.height < window->contentSize.x; if (requireScissor) { BeginScissorMode(scissor.x, scissor.y, scissor.width, scissor.height); } focus = window->callback(window, game); if (requireScissor) { EndScissorMode(); } } // Draw the resize button/icon. if (wm->enabled) { GuiDrawIcon(71, window->rect.x + window->rect.width - 20, window->rect.y + window->rect.height - 20, 1, GREEN); } return focus; } FocusCommand updateFloatingWindow(FloatingWindow* window, WindowManager* wm, Game* game) { FocusCommand focus = NO_FOCUS_ACTION; bool isMouseLeftClick = IsMouseButtonPressed(MOUSE_LEFT_BUTTON); if (wm->enabled && !window->hasFocus) { GuiSetState(STATE_DISABLED); } // Window movement and resize input and collision check. if (isMouseLeftClick && !window->moving && !window->resizing) { floatingWindowTransformCollisionCheck(window); } // Focus on window. if (!window->hasFocus && isMouseLeftClick && CheckCollisionPointRec(GetMousePosition(), window->rect)) { focus = REQUEST_FOCUS; } // Window movement and resize update. if (window->moving && window->hasFocus) { updateFloatingWindowMoving(window); } else if (window->resizing && window->hasFocus) { updateFloatingWindowResizing(window); } // Window and content drawing with scissor and scroll area. if(window->minimized) { if (wm->enabled) { updateFloatingWindowMinimized(window); } } else { FocusCommand result = updateFloatingWindowNotMinimized(window, wm, game); focus = result == NO_FOCUS_ACTION ? focus : result; } if (wm->enabled && !window->hasFocus) { GuiSetState(STATE_NORMAL); } return focus; } void initWindowManager(WindowManager* wm) { memset(wm, 0, sizeof(WindowManager)); wm->enabled = true; } void updateWindowManager(WindowManager* wm, Game* game) { Vector2 mousePosition = GetMousePosition(); int focusOnto = -1; if (!wm->enabled) { GuiSetState(STATE_DISABLED); } for (int index = 0; index < wm->windowCount; ++index) { FocusCommand focus = updateFloatingWindow(&wm->windows[index], wm, game); if (focusOnto != -1) { continue; } switch (focus) { case NO_FOCUS_ACTION: break; case REQUEST_FOCUS: { bool canFocus = true; // Windows ontop of it prevent focus. for (int innerIndex = index + 1; innerIndex < wm->windowCount; ++innerIndex) { if (CheckCollisionPointRec(mousePosition, wm->windows[innerIndex].rect)) { canFocus = false; break; } } if (canFocus) { focusOnto = index; } } break; case DEMAND_FOCUS: focusOnto = index; break; default: break; } } if (!wm->enabled) { GuiSetState(STATE_NORMAL); } if (focusOnto != -1) { focusOnWindow(wm, focusOnto); } } void addWindowToWindowManager(WindowManager* wm, FloatingWindow window) { if (wm->windowCount < UI_WINDOW_MAX) { if (wm->windowCount > 0) { wm->windows[wm->windowCount - 1].hasFocus = false; } wm->windows[wm->windowCount] = window; wm->windows[wm->windowCount].hasFocus = true; ++wm->windowCount; } else { TraceLog(LOG_ERROR, "'wm->windowCount' went over the limit"); } } void focusOnWindow(WindowManager* wm, int windowIndex) { if (wm->windowCount <= 1) { return; } wm->windows[wm->windowCount - 1].hasFocus = false; FloatingWindow window = wm->windows[windowIndex]; for (int index = windowIndex + 1; index < wm->windowCount; ++index) { wm->windows[index - 1] = wm->windows[index]; } window.hasFocus = true; wm->windows[wm->windowCount - 1] = window; } void enableWindowManager(WindowManager* wm) { wm->enabled = true; } void disableWindowManager(WindowManager* wm) { wm->enabled = false; }