Add debug window, parent buttons to debug window

This commit is contained in:
Doyle Thai 2016-08-21 00:21:50 +10:00
parent aa34bbefc3
commit 70a3032155
4 changed files with 231 additions and 121 deletions

View File

@ -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,
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)
// 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));
}
V2(0, 0), 0, renderTex, buttonColor);
if (label)
{
@ -166,9 +213,16 @@ i32 userInterface_scrollBar(UiState *const uiState,
if (math_pointInRect(scrollBarRect, 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)
{
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,11 +332,16 @@ 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)
{
if (input.keys[keycode_mouseLeft].endedDown)
{
uiState->activeItem = id;
}
}
}
/* If no widget has keyboard focus, take it */
if (uiState->kbdItem == 0)
@ -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));
}

View File

@ -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

View File

@ -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,

View File

@ -19,6 +19,7 @@ typedef struct Entity Entity;
typedef struct Config
{
b32 playWorldAudio;
b32 showStatMenu;
b32 showDebugDisplay;
} Config;