Add debug window, parent buttons to debug window
This commit is contained in:
parent
aa34bbefc3
commit
70a3032155
@ -4,33 +4,63 @@
|
||||
#include "Dengine/Renderer.h"
|
||||
#include "Dengine/Debug.h"
|
||||
|
||||
i32 userInterface_window(UiState *const uiState,
|
||||
MemoryArena *const arena,
|
||||
i32 userInterface_window(UiState *const uiState, MemoryArena *const arena,
|
||||
AssetManager *const assetManager,
|
||||
Renderer *const renderer,
|
||||
Font *const font,
|
||||
const KeyInput input,
|
||||
const i32 id, const Rect rect, const char *const title)
|
||||
Renderer *const renderer, Font *const font,
|
||||
const KeyInput input, WindowState *window)
|
||||
{
|
||||
if (math_pointInRect(rect, input.mouseP))
|
||||
if (math_pointInRect(window->rect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
uiState->hotItem = window->id;
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
|
||||
uiState->activeItem = id;
|
||||
uiState->activeItem = window->id;
|
||||
}
|
||||
|
||||
Rect rect = window->rect;
|
||||
RenderTex nullRenderTex = renderer_createNullRenderTex(assetManager);
|
||||
renderer_staticRect(renderer, rect.pos, rect.size, V2(0, 0), 0,
|
||||
nullRenderTex, V4(0.25f, 0.25f, 0.5f, 1.0f));
|
||||
nullRenderTex, V4(0.25f, 0.25f, 0.5f, 0.5f));
|
||||
|
||||
char *menuTitle = "Stat Menu";
|
||||
v2 menuTitleP = v2_add(rect.pos, V2(0, rect.size.h - 10));
|
||||
renderer_staticString(renderer, arena, font, menuTitle, menuTitleP,
|
||||
v2 menuTitleP = v2_add(rect.pos, V2(0, rect.size.h - 10));
|
||||
renderer_staticString(renderer, arena, font, window->title, menuTitleP,
|
||||
V2(0, 0), 0, V4(0, 0, 0, 1));
|
||||
|
||||
if (input.keys[keycode_mouseLeft].endedDown &&
|
||||
uiState->hotItem == id &&
|
||||
uiState->activeItem == id)
|
||||
// NOTE(doyle): activeItem captures mouse click within the UI bounds, but if
|
||||
// the user drags the mouse outside the bounds quicker than the game updates
|
||||
// then we use a second flag which only "unclicks" when the mouse is let go
|
||||
if (uiState->activeItem == window->id) window->windowHeld = TRUE;
|
||||
|
||||
if (window->windowHeld)
|
||||
{
|
||||
if (!input.keys[keycode_mouseLeft].endedDown)
|
||||
{
|
||||
window->windowHeld = FALSE;
|
||||
window->prevFrameWindowHeld = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (window->windowHeld)
|
||||
{
|
||||
// NOTE(doyle): If this is the first window click we don't process
|
||||
// movement and store the current position to delta from next cycle
|
||||
if (window->prevFrameWindowHeld)
|
||||
{
|
||||
// NOTE(doyle): Window clicked and held
|
||||
v2 deltaP = v2_sub(input.mouseP, window->prevMouseP);
|
||||
DEBUG_PUSH_VAR("Delta Pos %4.2f, %4.2f", deltaP, "v2");
|
||||
window->rect.pos = v2_add(deltaP, window->rect.pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
window->prevFrameWindowHeld = TRUE;
|
||||
}
|
||||
|
||||
window->prevMouseP = input.mouseP;
|
||||
}
|
||||
|
||||
if (!input.keys[keycode_mouseLeft].endedDown &&
|
||||
uiState->hotItem == window->id &&
|
||||
uiState->activeItem == window->id)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@ -49,12 +79,52 @@ i32 userInterface_button(UiState *const uiState,
|
||||
if (math_pointInRect(rect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
|
||||
uiState->activeItem = id;
|
||||
|
||||
// NOTE(doyle): UI windows are drawn first, they steal focus on mouse
|
||||
// click since window logic is paired with the window rendering. If
|
||||
// a UI element resides over our mouse, we allow the element to override
|
||||
// the windows focus
|
||||
// TODO(doyle): Make a window list to iterate over window ids
|
||||
if (uiState->activeItem == uiState->statWindow.id ||
|
||||
uiState->activeItem == uiState->debugWindow.id ||
|
||||
uiState->activeItem == 0)
|
||||
{
|
||||
if (input.keys[keycode_mouseLeft].endedDown)
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
|
||||
|
||||
#if 0
|
||||
// Draw shadow
|
||||
renderer_staticRect(renderer, v2_add(V2(1, 1), rect.pos), rect.size,
|
||||
V2(0, 0), 0, renderTex, V4(0, 0, 0, 1));
|
||||
#endif
|
||||
|
||||
v2 buttonOffset = V2(0, 0);
|
||||
v4 buttonColor = {0};
|
||||
if (uiState->hotItem == id)
|
||||
{
|
||||
if (uiState->activeItem == id)
|
||||
{
|
||||
buttonOffset = V2(1, 1);
|
||||
buttonColor = V4(1, 1, 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(doyle): Optional add effect on button hover
|
||||
buttonColor = V4(1, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonColor = V4(0.5f, 0.5f, 0.5f, 1);
|
||||
}
|
||||
|
||||
/* If no widget has keyboard focus, take it */
|
||||
if (uiState->kbdItem == 0)
|
||||
uiState->kbdItem = id;
|
||||
@ -63,37 +133,14 @@ i32 userInterface_button(UiState *const uiState,
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
// Draw outline
|
||||
renderer_staticRect(renderer, v2_add(V2(-6, -6), rect.pos),
|
||||
v2_add(V2(20, 20), rect.size), V2(0, 0), 0, renderTex,
|
||||
renderer_staticRect(renderer,
|
||||
v2_add(V2(-2, -2), v2_add(buttonOffset, rect.pos)),
|
||||
v2_add(V2(4, 4), rect.size), V2(0, 0), 0, renderTex,
|
||||
V4(1.0f, 0, 0, 1));
|
||||
}
|
||||
|
||||
// Draw shadow
|
||||
renderer_staticRect(renderer, v2_add(V2(8, 8), rect.pos), rect.size,
|
||||
V2(0, 0), 0, renderTex, V4(0, 0, 0, 1));
|
||||
|
||||
v2 buttonOffset = V2(0, 0);
|
||||
if (uiState->hotItem == id)
|
||||
{
|
||||
if (uiState->activeItem == id)
|
||||
{
|
||||
buttonOffset = V2(2, 2);
|
||||
renderer_staticRect(renderer, v2_add(buttonOffset, rect.pos),
|
||||
rect.size, V2(0, 0), 0, renderTex,
|
||||
V4(1, 1, 1, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer_staticRect(renderer, v2_add(buttonOffset, rect.pos),
|
||||
rect.size, V2(0, 0), 0, renderTex,
|
||||
V4(1, 1, 1, 1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer_staticRect(renderer, v2_add(buttonOffset, rect.pos), rect.size,
|
||||
V2(0, 0), 0, renderTex, V4(0.5f, 0.5f, 0.5f, 1));
|
||||
}
|
||||
renderer_staticRect(renderer, v2_add(buttonOffset, rect.pos), rect.size,
|
||||
V2(0, 0), 0, renderTex, buttonColor);
|
||||
|
||||
if (label)
|
||||
{
|
||||
@ -166,8 +213,15 @@ i32 userInterface_scrollBar(UiState *const uiState,
|
||||
if (math_pointInRect(scrollBarRect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
|
||||
uiState->activeItem = id;
|
||||
if (uiState->activeItem == uiState->statWindow.id ||
|
||||
uiState->activeItem == uiState->debugWindow.id ||
|
||||
uiState->activeItem == 0)
|
||||
{
|
||||
if (input.keys[keycode_mouseLeft].endedDown)
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
|
||||
@ -180,8 +234,8 @@ i32 userInterface_scrollBar(UiState *const uiState,
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
// Draw outline
|
||||
renderer_staticRect(renderer, v2_add(V2(-6, -6), scrollBarRect.pos),
|
||||
v2_add(V2(20, 20), scrollBarRect.size), V2(0, 0), 0,
|
||||
renderer_staticRect(renderer, v2_add(V2(-2, -2), scrollBarRect.pos),
|
||||
v2_add(V2(4, 4), scrollBarRect.size), V2(0, 0), 0,
|
||||
renderTex, V4(1.0f, 0, 0, 1));
|
||||
}
|
||||
|
||||
@ -278,9 +332,14 @@ i32 userInterface_textField(UiState *const uiState, MemoryArena *const arena,
|
||||
if (math_pointInRect(textRect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
|
||||
if (uiState->activeItem == uiState->statWindow.id ||
|
||||
uiState->activeItem == uiState->debugWindow.id ||
|
||||
uiState->activeItem == 0)
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
if (input.keys[keycode_mouseLeft].endedDown)
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,8 +352,8 @@ i32 userInterface_textField(UiState *const uiState, MemoryArena *const arena,
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
// Draw outline
|
||||
renderer_staticRect(renderer, v2_add(V2(-4, -4), textRect.pos),
|
||||
v2_add(V2(16, 16), textRect.size), V2(0, 0), 0,
|
||||
renderer_staticRect(renderer, v2_add(V2(-2, -2), textRect.pos),
|
||||
v2_add(V2(4, 4), textRect.size), V2(0, 0), 0,
|
||||
renderTex, V4(1.0f, 0, 0, 1));
|
||||
}
|
||||
|
||||
|
@ -343,13 +343,21 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
|
||||
state->uiState.keyMod = keycode_null;
|
||||
state->uiState.keyChar = keycode_null;
|
||||
|
||||
common_strncpy(state->uiState.statMenuState.title, "Stat Menu",
|
||||
common_strncpy(state->uiState.statWindow.title, "Stat Menu",
|
||||
common_strlen("Stat Menu"));
|
||||
state->uiState.statMenuState.id = 99;
|
||||
state->uiState.statMenuState.rect.pos = V2(300, 400);
|
||||
state->uiState.statMenuState.rect.size = V2(300, 400);
|
||||
state->uiState.statMenuState.prevFrameWindowHeld = FALSE;
|
||||
state->uiState.statMenuState.windowHeld = FALSE;
|
||||
state->uiState.statWindow.id = 99;
|
||||
state->uiState.statWindow.rect.pos = V2(300, 400);
|
||||
state->uiState.statWindow.rect.size = V2(300, 400);
|
||||
state->uiState.statWindow.prevFrameWindowHeld = FALSE;
|
||||
state->uiState.statWindow.windowHeld = FALSE;
|
||||
|
||||
common_strncpy(state->uiState.debugWindow.title, "Debug Menu",
|
||||
common_strlen("Debug Menu"));
|
||||
state->uiState.debugWindow.id = 98;
|
||||
state->uiState.debugWindow.rect.pos = V2(800, 400);
|
||||
state->uiState.debugWindow.rect.size = V2(750, 400);
|
||||
state->uiState.debugWindow.prevFrameWindowHeld = FALSE;
|
||||
state->uiState.debugWindow.windowHeld = FALSE;
|
||||
|
||||
state->config.playWorldAudio = FALSE;
|
||||
state->config.showDebugDisplay = TRUE;
|
||||
@ -1031,6 +1039,71 @@ typedef struct BattleState
|
||||
|
||||
GLOBAL_VAR BattleState battleState = {0};
|
||||
|
||||
enum RectBaseline
|
||||
{
|
||||
rectbaseline_top,
|
||||
rectbaseline_topLeft,
|
||||
rectbaseline_topRight,
|
||||
rectbaseline_bottom,
|
||||
rectbaseline_bottomRight,
|
||||
rectbaseline_bottomLeft,
|
||||
rectbaseline_left,
|
||||
rectbaseline_right,
|
||||
rectbaseline_center,
|
||||
rectbaseline_count,
|
||||
|
||||
};
|
||||
|
||||
INTERNAL v2 getPosRelativeToRect(Rect rect, v2 offset,
|
||||
enum RectBaseline baseline)
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(baseline < rectbaseline_count);
|
||||
#endif
|
||||
v2 result = {0};
|
||||
|
||||
v2 posToOffsetFrom = rect.pos;
|
||||
switch (baseline)
|
||||
{
|
||||
case rectbaseline_top:
|
||||
posToOffsetFrom.y += (rect.size.h);
|
||||
posToOffsetFrom.x += (rect.size.w * 0.5f);
|
||||
break;
|
||||
case rectbaseline_topLeft:
|
||||
posToOffsetFrom.y += (rect.size.h);
|
||||
break;
|
||||
case rectbaseline_topRight:
|
||||
posToOffsetFrom.y += (rect.size.h);
|
||||
posToOffsetFrom.x += (rect.size.w);
|
||||
break;
|
||||
case rectbaseline_bottom:
|
||||
posToOffsetFrom.x += (rect.size.w * 0.5f);
|
||||
break;
|
||||
case rectbaseline_bottomRight:
|
||||
posToOffsetFrom.x += (rect.size.w);
|
||||
break;
|
||||
case rectbaseline_left:
|
||||
posToOffsetFrom.y += (rect.size.h * 0.5f);
|
||||
break;
|
||||
case rectbaseline_right:
|
||||
posToOffsetFrom.x += (rect.size.w);
|
||||
posToOffsetFrom.y += (rect.size.h * 0.5f);
|
||||
break;
|
||||
|
||||
case rectbaseline_bottomLeft:
|
||||
break;
|
||||
default:
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG(
|
||||
"getPosRelativeToRect() warning: baseline enum not recognised");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
result = v2_add(posToOffsetFrom, offset);
|
||||
return result;
|
||||
}
|
||||
|
||||
void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
{
|
||||
if (dt >= 1.0f) dt = 1.0f;
|
||||
@ -1324,8 +1397,30 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
state->uiState.hotItem = 0;
|
||||
|
||||
/* Draw ui */
|
||||
if (state->uiState.keyChar == keycode_i)
|
||||
{
|
||||
state->config.showStatMenu =
|
||||
(state->config.showStatMenu == TRUE) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
if (state->config.showStatMenu)
|
||||
{
|
||||
WindowState *statWindow = &state->uiState.statWindow;
|
||||
userInterface_window(&state->uiState, &state->arena, assetManager,
|
||||
renderer, font, state->input, statWindow);
|
||||
}
|
||||
|
||||
/* Draw debug window */
|
||||
WindowState *debugWindow = &state->uiState.debugWindow;
|
||||
userInterface_window(&state->uiState, &state->arena, assetManager, renderer,
|
||||
font, state->input, debugWindow);
|
||||
|
||||
// TODO(doyle): Bug in font rendering once button reaches 700-800+ pixels
|
||||
Rect toggleAudioButtonRect = {V2(1000, 800), V2(100, 50)};
|
||||
Rect toggleAudioButtonRect = {0};
|
||||
toggleAudioButtonRect.size = V2(100, 50);
|
||||
toggleAudioButtonRect.pos = getPosRelativeToRect(
|
||||
debugWindow->rect, V2(10, -65.0f), rectbaseline_topLeft);
|
||||
|
||||
b32 toggleAudioClicked = userInterface_button(
|
||||
&state->uiState, &state->arena, assetManager, renderer, font,
|
||||
state->input, 1, toggleAudioButtonRect, "Toggle Music");
|
||||
@ -1336,7 +1431,10 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
(state->config.playWorldAudio == TRUE) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
Rect toggleDebugButtonRect = {V2(1150, 800), V2(100, 50)};
|
||||
Rect toggleDebugButtonRect = {0};
|
||||
toggleDebugButtonRect.size = V2(100, 50);
|
||||
toggleDebugButtonRect.pos = getPosRelativeToRect(
|
||||
toggleAudioButtonRect, V2(25, 0), rectbaseline_bottomRight);
|
||||
b32 toggleDebugClicked = userInterface_button(
|
||||
&state->uiState, &state->arena, assetManager, renderer, font,
|
||||
state->input, 2, toggleDebugButtonRect, "Toggle Debug Display");
|
||||
@ -1347,66 +1445,18 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
(state->config.showDebugDisplay == TRUE) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
LOCAL_PERSIST toggleShowingStatMenu = FALSE;
|
||||
if (state->uiState.keyChar == keycode_i)
|
||||
{
|
||||
toggleShowingStatMenu =
|
||||
(toggleShowingStatMenu == TRUE) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
if (toggleShowingStatMenu)
|
||||
{
|
||||
WindowState *statMenu = &state->uiState.statMenuState;
|
||||
// TODO(doyle): Define pushing/placing text within a coordinate system,
|
||||
// i.e. relative to an elements position
|
||||
b32 windowClickedAndHeld = userInterface_window(
|
||||
&state->uiState, &state->arena, assetManager, renderer, font,
|
||||
state->input, statMenu->id, statMenu->rect, statMenu->title);
|
||||
|
||||
// NOTE(doyle): windowClickedAndHeld captures mouse click within the UI
|
||||
// bounds, but if the user drags the mouse outside the bounds quicker
|
||||
// than the game updates then we use a second flag which only
|
||||
// "unclicks" when the mouse is let go
|
||||
if (windowClickedAndHeld) statMenu->windowHeld = TRUE;
|
||||
|
||||
if (statMenu->windowHeld)
|
||||
{
|
||||
if (!state->input.keys[keycode_mouseLeft].endedDown)
|
||||
{
|
||||
statMenu->windowHeld = FALSE;
|
||||
statMenu->prevFrameWindowHeld = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (statMenu->windowHeld)
|
||||
{
|
||||
// NOTE(doyle): If this is the first window click we don't process
|
||||
// movement and instead store the frame position to delta from it
|
||||
if (statMenu->prevFrameWindowHeld)
|
||||
{
|
||||
// NOTE(doyle): Window clicked and held
|
||||
v2 deltaP = v2_sub(state->input.mouseP, statMenu->prevFramePos);
|
||||
DEBUG_PUSH_VAR("Delta Pos %4.2f, %4.2f", deltaP, "v2");
|
||||
statMenu->rect.pos = v2_add(deltaP, statMenu->rect.pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
statMenu->prevFrameWindowHeld = TRUE;
|
||||
}
|
||||
|
||||
statMenu->prevFramePos = state->input.mouseP;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LOCAL_PERSIST i32 scrollValue = 30;
|
||||
Rect scrollRectA = {V2(1500, 600), V2(16, 255)};
|
||||
Rect scrollRectA = {V2(0, 0), V2(16, 255)};
|
||||
scrollRectA.pos = getPosRelativeToRect(debugWindow->rect, V2(-20, -260),
|
||||
rectbaseline_topRight);
|
||||
userInterface_scrollBar(&state->uiState, assetManager, renderer,
|
||||
state->input, 3, scrollRectA, &scrollValue, 160);
|
||||
|
||||
LOCAL_PERSIST char fieldString[80] = "Hello world";
|
||||
v2 textFieldP = getPosRelativeToRect(toggleAudioButtonRect, V2(0, -20),
|
||||
rectbaseline_bottomLeft);
|
||||
userInterface_textField(&state->uiState, &state->arena, assetManager,
|
||||
renderer, font, state->input, 4, V2(1000, 750),
|
||||
renderer, font, state->input, 4, textFieldP,
|
||||
fieldString);
|
||||
|
||||
// RESET IMGUI
|
||||
|
@ -33,11 +33,11 @@ typedef struct WindowState
|
||||
char title[64];
|
||||
|
||||
Rect rect;
|
||||
v2 prevFramePos;
|
||||
// TODO(doyle): Store this in the input data not window?
|
||||
v2 prevMouseP;
|
||||
|
||||
b32 prevFrameWindowHeld;
|
||||
b32 windowHeld;
|
||||
|
||||
} WindowState;
|
||||
|
||||
typedef struct UiState
|
||||
@ -54,14 +54,14 @@ typedef struct UiState
|
||||
enum KeyCode keyMod;
|
||||
enum KeyCode keyChar;
|
||||
|
||||
WindowState statMenuState;
|
||||
WindowState statWindow;
|
||||
WindowState debugWindow;
|
||||
} UiState;
|
||||
|
||||
i32 userInterface_window(UiState *const uiState, MemoryArena *const arena,
|
||||
AssetManager *const assetManager,
|
||||
Renderer *const renderer, Font *const font,
|
||||
const KeyInput input, const i32 id, const Rect rect,
|
||||
const char *const title);
|
||||
const KeyInput input, WindowState *window);
|
||||
|
||||
i32 userInterface_button(UiState *const uiState,
|
||||
MemoryArena *const arena,
|
||||
|
@ -19,6 +19,7 @@ typedef struct Entity Entity;
|
||||
typedef struct Config
|
||||
{
|
||||
b32 playWorldAudio;
|
||||
b32 showStatMenu;
|
||||
b32 showDebugDisplay;
|
||||
} Config;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user