Separate UI code into own file

This commit is contained in:
Doyle Thai 2016-08-17 18:04:51 +10:00
parent 5cd7239c8a
commit e90b31de55
10 changed files with 368 additions and 311 deletions

View File

@ -129,6 +129,7 @@
<ClCompile Include="src\Platform.c" />
<ClCompile Include="src\Renderer.c" />
<ClCompile Include="src\Shader.c" />
<ClCompile Include="src\UserInterface.c" />
<ClCompile Include="src\WorldTraveller.c" />
<ClCompile Include="src\Texture.c" />
</ItemGroup>
@ -153,6 +154,7 @@
<ClInclude Include="src\include\Dengine\Renderer.h" />
<ClInclude Include="src\include\Dengine\Shader.h" />
<ClInclude Include="src\include\Dengine\Texture.h" />
<ClInclude Include="src\include\Dengine\UserInterface.h" />
<ClInclude Include="src\include\Dengine\WorldTraveller.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -48,6 +48,9 @@
<ClCompile Include="src\dengine.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\UserInterface.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="data\shaders\default.vert.glsl" />
@ -101,5 +104,8 @@
<ClInclude Include="src\include\Dengine\WorldTraveller.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\include\Dengine\UserInterface.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -36,7 +36,8 @@ AudioVorbis *asset_getVorbis(AssetManager *assetManager,
return NULL;
}
Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type)
Texture *asset_getTexture(AssetManager *const assetManager,
const enum TexList type)
{
if (type < texlist_count)
return &assetManager->textures[type];

View File

@ -168,7 +168,7 @@ INTERNAL v2 mapWorldToCameraSpace(v2 worldPos, v4 cameraBounds)
return posInCameraSpace;
}
RenderTex renderer_createNullRenderTex(AssetManager *assetManager)
RenderTex renderer_createNullRenderTex(AssetManager *const assetManager)
{
Texture *emptyTex = asset_getTexture(assetManager, texlist_empty);
RenderTex result = {emptyTex, V4(0, 1, 1, 0)};

295
src/UserInterface.c Normal file
View File

@ -0,0 +1,295 @@
#include "Dengine/UserInterface.h"
#include "Dengine/Assets.h"
#include "Dengine/Renderer.h"
i32 userInterface_button(UiState *const uiState,
AssetManager *const assetManager,
Renderer *const renderer, const KeyInput input,
const i32 id, const Rect rect)
{
if (math_pointInRect(rect, input.mouseP))
{
uiState->hotItem = id;
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
uiState->activeItem = id;
}
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
/* If no widget has keyboard focus, take it */
if (uiState->kbdItem == 0)
uiState->kbdItem = id;
/* If we have keyboard focus, show it */
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,
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));
if (uiState->hotItem == id)
{
if (uiState->activeItem == id)
{
renderer_staticRect(renderer, v2_add(V2(2, 2), rect.pos), rect.size,
V2(0, 0), 0, renderTex, V4(1, 1, 1, 1));
}
else
{
renderer_staticRect(renderer, v2_add(V2(0, 0), rect.pos), rect.size,
V2(0, 0), 0, renderTex, V4(1, 1, 1, 1));
}
}
else
{
renderer_staticRect(renderer, v2_add(V2(0, 0), rect.pos), rect.size,
V2(0, 0), 0, renderTex, V4(0.5f, 0.5f, 0.5f, 1));
}
// After renderering before click check, see if we need to process keys
if (uiState->kbdItem == id)
{
switch (uiState->keyEntered)
{
case keycode_tab:
// Set focus to nothing and let next widget get focus
uiState->kbdItem = 0;
if (uiState->keyMod == keycode_leftShift)
uiState->kbdItem = uiState->lastWidget;
// Clear key state so next widget doesn't auto grab
uiState->keyEntered = keycode_null;
break;
case keycode_enter:
return 1;
default:
break;
}
}
uiState->lastWidget = id;
if (!input.keys[keycode_mouseLeft].endedDown &&
uiState->hotItem == id &&
uiState->activeItem == id)
{
return 1;
}
return 0;
}
i32 userInterface_scrollBar(UiState *const uiState,
AssetManager *const assetManager,
Renderer *const renderer, const KeyInput input,
const i32 id, const Rect scrollBarRect,
i32 *const value, const i32 maxValue)
{
#ifdef DENGINE_DEBUG
ASSERT(*value <= maxValue);
#endif
if (math_pointInRect(scrollBarRect, input.mouseP))
{
uiState->hotItem = id;
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
uiState->activeItem = id;
}
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
/* If no widget has keyboard focus, take it */
if (uiState->kbdItem == 0)
uiState->kbdItem = id;
/* If we have keyboard focus, show it */
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,
renderTex, V4(1.0f, 0, 0, 1));
}
// Render scroll bar background
renderer_staticRect(renderer, scrollBarRect.pos, scrollBarRect.size,
V2(0, 0), 0, renderTex, V4(0.75f, 0.5f, 0.5f, 1));
// Render scroll bar slider
v2 sliderSize = V2(16, 16);
v4 sliderColor = V4(0, 0, 0, 1);
f32 sliderPercentageOffset = (CAST(f32) *value / CAST(f32) maxValue);
f32 sliderYOffsetToBar =
(scrollBarRect.size.h - sliderSize.h) * sliderPercentageOffset;
v2 sliderPos = v2_add(scrollBarRect.pos, V2(0, sliderYOffsetToBar));
if (uiState->hotItem == id || uiState->activeItem == id)
sliderColor = V4(1.0f, 0, 0, 1);
else
sliderColor = V4(0.0f, 1.0f, 0, 1);
renderer_staticRect(renderer, sliderPos, sliderSize, V2(0, 0), 0, renderTex,
sliderColor);
if (uiState->kbdItem == id)
{
switch (uiState->keyEntered)
{
case keycode_tab:
uiState->kbdItem = 0;
if (uiState->keyMod == keycode_leftShift)
uiState->kbdItem = uiState->lastWidget;
// Clear key state so next widget doesn't auto grab
uiState->keyEntered = keycode_null;
break;
case keycode_up:
// TODO(doyle): Fix input for this to work, i.e. proper rate limited input poll
if (*value < maxValue)
{
(*value)++;
return 1;
}
case keycode_down:
if (*value > 0)
{
(*value)--;
return 1;
}
default:
break;
}
}
uiState->lastWidget = id;
if (uiState->activeItem == id)
{
f32 mouseYRelToRect = input.mouseP.y - scrollBarRect.pos.y;
// Bounds check
if (mouseYRelToRect < 0)
mouseYRelToRect = 0;
else if (mouseYRelToRect > scrollBarRect.size.h)
mouseYRelToRect = scrollBarRect.size.h;
f32 newSliderPercentOffset =
(CAST(f32) mouseYRelToRect / scrollBarRect.size.h);
i32 newValue = CAST(i32)(newSliderPercentOffset * CAST(f32)maxValue);
if (newValue != *value)
{
*value = newValue;
return 1;
}
}
return 0;
}
i32 userInterface_textField(UiState *const uiState, MemoryArena *arena,
AssetManager *const assetManager,
Renderer *const renderer, Font *const font,
KeyInput input, const i32 id, v2 pos,
char *const string)
{
i32 strLen = common_strlen(string);
b32 changed = FALSE;
Rect textRect = {0};
textRect.pos = pos;
textRect.size = V2(30 * font->maxSize.w, font->maxSize.h);
if (math_pointInRect(textRect, input.mouseP))
{
uiState->hotItem = id;
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
{
uiState->activeItem = id;
}
}
/* If no widget has keyboard focus, take it */
if (uiState->kbdItem == 0)
uiState->kbdItem = id;
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
/* If we have keyboard focus, show it */
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,
renderTex, V4(1.0f, 0, 0, 1));
}
// Render text field
renderer_staticRect(renderer, textRect.pos, textRect.size, V2(0, 0), 0,
renderTex, V4(0.75f, 0.5f, 0.5f, 1));
if (uiState->activeItem == id || uiState->hotItem == id)
{
renderer_staticRect(renderer, textRect.pos, textRect.size, V2(0, 0), 0,
renderTex, V4(0.75f, 0.75f, 0.0f, 1));
}
else
{
renderer_staticRect(renderer, textRect.pos, textRect.size, V2(0, 0), 0,
renderTex, V4(0.5f, 0.5f, 0.5f, 1));
}
// TODO(doyle): Figure out why we need to offset text ..
v2 strPos = v2_add(textRect.pos, V2(0, -font->maxSize.h));
renderer_staticString(renderer, arena, font, string, strPos, V2(0, 0), 0,
V4(0, 0, 0, 1));
if (uiState->kbdItem == id)
{
switch (uiState->keyEntered)
{
case keycode_tab:
uiState->kbdItem = 0;
if (uiState->keyMod == keycode_leftShift)
uiState->kbdItem = uiState->lastWidget;
uiState->keyEntered = keycode_null;
break;
case keycode_backspace:
if (strLen > 0)
{
string[--strLen] = 0;
changed = TRUE;
}
break;
default:
break;
}
if (uiState->keyChar >= keycode_space &&
uiState->keyChar <= keycode_tilda && strLen < 30)
{
string[strLen++] = uiState->keyChar + ' ';
string[strLen] = 0;
changed = TRUE;
}
}
if (!input.keys[keycode_mouseLeft].endedDown &&
uiState->hotItem == id &&
uiState->activeItem == id)
{
uiState->kbdItem = id;
}
uiState->lastWidget = id;
return changed;
}

View File

@ -3,6 +3,7 @@
#include "Dengine/Debug.h"
#include "Dengine/Entity.h"
#include "Dengine/Platform.h"
#include "Dengine/UserInterface.h"
enum State
{
@ -1001,289 +1002,6 @@ INTERNAL void sortWorldEntityList(World *world, i32 numDeadEntities)
world->freeEntityIndex -= numDeadEntities;
}
INTERNAL i32 button(UiState *uiState, AssetManager *assetManager,
Renderer *renderer, KeyInput input, i32 id, Rect rect)
{
if (math_pointInRect(rect, input.mouseP))
{
uiState->hotItem = id;
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
uiState->activeItem = id;
}
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
/* If no widget has keyboard focus, take it */
if (uiState->kbdItem == 0)
uiState->kbdItem = id;
/* If we have keyboard focus, show it */
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,
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));
if (uiState->hotItem == id)
{
if (uiState->activeItem == id)
{
renderer_staticRect(renderer, v2_add(V2(2, 2), rect.pos), rect.size,
V2(0, 0), 0, renderTex, V4(1, 1, 1, 1));
}
else
{
renderer_staticRect(renderer, v2_add(V2(0, 0), rect.pos), rect.size,
V2(0, 0), 0, renderTex, V4(1, 1, 1, 1));
}
}
else
{
renderer_staticRect(renderer, v2_add(V2(0, 0), rect.pos), rect.size,
V2(0, 0), 0, renderTex, V4(0.5f, 0.5f, 0.5f, 1));
}
// After renderering before click check, see if we need to process keys
if (uiState->kbdItem == id)
{
switch (uiState->keyEntered)
{
case keycode_tab:
// Set focus to nothing and let next widget get focus
uiState->kbdItem = 0;
if (uiState->keyMod == keycode_leftShift)
uiState->kbdItem = uiState->lastWidget;
// Clear key state so next widget doesn't auto grab
uiState->keyEntered = keycode_null;
break;
case keycode_enter:
return 1;
default:
break;
}
}
uiState->lastWidget = id;
if (!input.keys[keycode_mouseLeft].endedDown &&
uiState->hotItem == id &&
uiState->activeItem == id)
{
return 1;
}
return 0;
}
INTERNAL i32 scrollBar(UiState *uiState, AssetManager *assetManager,
Renderer *renderer, KeyInput input, i32 id,
Rect scrollBarRect, i32 *value, i32 maxValue)
{
#ifdef DENGINE_DEBUG
ASSERT(*value <= maxValue);
#endif
if (math_pointInRect(scrollBarRect, input.mouseP))
{
uiState->hotItem = id;
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
uiState->activeItem = id;
}
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
/* If no widget has keyboard focus, take it */
if (uiState->kbdItem == 0)
uiState->kbdItem = id;
/* If we have keyboard focus, show it */
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,
renderTex, V4(1.0f, 0, 0, 1));
}
// Render scroll bar background
renderer_staticRect(renderer, scrollBarRect.pos, scrollBarRect.size,
V2(0, 0), 0, renderTex, V4(0.75f, 0.5f, 0.5f, 1));
// Render scroll bar slider
v2 sliderSize = V2(16, 16);
v4 sliderColor = V4(0, 0, 0, 1);
f32 sliderPercentageOffset = (CAST(f32) *value / CAST(f32) maxValue);
f32 sliderYOffsetToBar =
(scrollBarRect.size.h - sliderSize.h) * sliderPercentageOffset;
v2 sliderPos = v2_add(scrollBarRect.pos, V2(0, sliderYOffsetToBar));
if (uiState->hotItem == id || uiState->activeItem == id)
sliderColor = V4(1.0f, 0, 0, 1);
else
sliderColor = V4(0.0f, 1.0f, 0, 1);
renderer_staticRect(renderer, sliderPos, sliderSize, V2(0, 0), 0, renderTex,
sliderColor);
if (uiState->kbdItem == id)
{
switch (uiState->keyEntered)
{
case keycode_tab:
uiState->kbdItem = 0;
if (uiState->keyMod == keycode_leftShift)
uiState->kbdItem = uiState->lastWidget;
// Clear key state so next widget doesn't auto grab
uiState->keyEntered = keycode_null;
break;
case keycode_up:
// TODO(doyle): Fix input for this to work, i.e. proper rate limited input poll
if (*value < maxValue)
{
(*value)++;
return 1;
}
case keycode_down:
if (*value > 0)
{
(*value)--;
return 1;
}
default:
break;
}
}
uiState->lastWidget = id;
if (uiState->activeItem == id)
{
f32 mouseYRelToRect = input.mouseP.y - scrollBarRect.pos.y;
// Bounds check
if (mouseYRelToRect < 0)
mouseYRelToRect = 0;
else if (mouseYRelToRect > scrollBarRect.size.h)
mouseYRelToRect = scrollBarRect.size.h;
f32 newSliderPercentOffset =
(CAST(f32) mouseYRelToRect / scrollBarRect.size.h);
i32 newValue = CAST(i32)(newSliderPercentOffset * CAST(f32)maxValue);
if (newValue != *value)
{
*value = newValue;
return 1;
}
}
return 0;
}
INTERNAL i32 textField(UiState *const uiState, MemoryArena *arena,
AssetManager *const assetManager,
Renderer *const renderer, Font *const font,
KeyInput input, const i32 id, v2 pos, char *const string)
{
i32 strLen = common_strlen(string);
b32 changed = FALSE;
Rect textRect = {0};
textRect.pos = pos;
textRect.size = V2(30 * font->maxSize.w, font->maxSize.h);
if (math_pointInRect(textRect, input.mouseP))
{
uiState->hotItem = id;
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
{
uiState->activeItem = id;
}
}
/* If no widget has keyboard focus, take it */
if (uiState->kbdItem == 0)
uiState->kbdItem = id;
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
/* If we have keyboard focus, show it */
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,
renderTex, V4(1.0f, 0, 0, 1));
}
// Render
renderer_staticRect(renderer, textRect.pos, textRect.size, V2(0, 0), 0,
renderTex, V4(0.75f, 0.5f, 0.5f, 1));
if (uiState->activeItem == id || uiState->hotItem == id)
{
renderer_staticRect(renderer, textRect.pos, textRect.size, V2(0, 0), 0,
renderTex, V4(0.75f, 0.75f, 0.0f, 1));
}
else
{
renderer_staticRect(renderer, textRect.pos, textRect.size, V2(0, 0), 0,
renderTex, V4(0.5f, 0.5f, 0.5f, 1));
}
renderer_staticString(renderer, arena, font, string, textRect.pos, V2(0, 0),
0, V4(0, 0, 0, 1));
if (uiState->kbdItem == id)
{
switch (uiState->keyEntered)
{
case keycode_tab:
uiState->kbdItem = 0;
if (uiState->keyMod == keycode_leftShift)
uiState->kbdItem = uiState->lastWidget;
uiState->keyEntered = keycode_null;
break;
case keycode_backspace:
if (strLen > 0)
{
string[--strLen] = 0;
changed = TRUE;
}
break;
default:
break;
}
if (uiState->keyChar >= keycode_space &&
uiState->keyChar <= keycode_tilda && strLen < 30)
{
string[strLen++] = uiState->keyChar + ' ';
string[strLen] = 0;
changed = TRUE;
}
}
if (!input.keys[keycode_mouseLeft].endedDown &&
uiState->hotItem == id &&
uiState->activeItem == id)
{
uiState->kbdItem = id;
}
uiState->lastWidget = id;
return changed;
}
void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
{
if (dt >= 1.0f) dt = 1.0f;
@ -1535,25 +1253,26 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
/* Draw ui */
Rect buttonRectA = {V2(300, 500), V2(100, 50)};
button(&state->uiState, assetManager, renderer, state->input, 1,
buttonRectA);
userInterface_button(&state->uiState, assetManager, renderer, state->input,
1, buttonRectA);
Rect buttonRectB = {V2(500, 500), V2(100, 50)};
button(&state->uiState, assetManager, renderer, state->input, 2,
buttonRectB);
userInterface_button(&state->uiState, assetManager, renderer, state->input,
2, buttonRectB);
Rect buttonRectC = {V2(700, 500), V2(100, 50)};
button(&state->uiState, assetManager, renderer, state->input, 3,
buttonRectC);
userInterface_button(&state->uiState, assetManager, renderer, state->input,
3, buttonRectC);
LOCAL_PERSIST i32 scrollValue = 30;
Rect scrollRectA = {V2(900, 500), V2(16, 255)};
scrollBar(&state->uiState, assetManager, renderer, state->input, 4,
scrollRectA, &scrollValue, 160);
Rect scrollRectA = {V2(900, 500), V2(16, 255)};
userInterface_scrollBar(&state->uiState, assetManager, renderer,
state->input, 4, scrollRectA, &scrollValue, 160);
LOCAL_PERSIST char fieldString[80] = "Hello world";
textField(&state->uiState, &state->arena, assetManager, renderer, font,
state->input, 5, V2(1000, 500), fieldString);
userInterface_textField(&state->uiState, &state->arena, assetManager,
renderer, font, state->input, 5, V2(1000, 500),
fieldString);
// RESET IMGUI
if (!state->input.keys[keycode_mouseLeft].endedDown)

View File

@ -22,7 +22,8 @@ typedef struct AssetManager
AudioVorbis *asset_getVorbis(AssetManager *assetManager,
const enum AudioList type);
Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type);
Texture *asset_getTexture(AssetManager *const assetManager,
const enum TexList type);
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type);
TexAtlas *asset_getTextureAtlas(AssetManager *assetManager,
const enum TexList type);

View File

@ -28,7 +28,7 @@ typedef struct RenderTex
v4 texRect;
} RenderTex;
RenderTex renderer_createNullRenderTex(AssetManager *assetManager);
RenderTex renderer_createNullRenderTex(AssetManager *const assetManager);
// TODO(doyle): Clean up lines
// Renderer::~Renderer() { glDeleteVertexArrays(1, &this->quadVAO); }

View File

@ -0,0 +1,43 @@
#ifndef DENGINE_USER_INTERFACE_H
#define DENGINE_USER_INTERFACE_H
#include "Dengine/Common.h"
#include "Dengine/Math.h"
#include "Dengine/Platform.h"
/* Forward Declaration */
typedef struct AssetManager AssetManager;
typedef struct Font Font;
typedef struct MemoryArena MemoryArena;
typedef struct Renderer Renderer;
typedef struct UiState
{
i32 hotItem;
i32 activeItem;
i32 kbdItem;
enum KeyCode keyEntered;
enum KeyCode keyMod;
enum KeyCode keyChar;
i32 lastWidget;
} UiState;
i32 userInterface_button(UiState *const uiState,
AssetManager *const assetManager,
Renderer *const renderer, const KeyInput input,
const i32 id, const Rect rect);
i32 userInterface_textField(UiState *const uiState, MemoryArena *arena,
AssetManager *const assetManager,
Renderer *const renderer, Font *const font,
KeyInput input, const i32 id, v2 pos,
char *const string);
i32 userInterface_scrollBar(UiState *const uiState,
AssetManager *const assetManager,
Renderer *const renderer, const KeyInput input,
const i32 id, const Rect scrollBarRect,
i32 *const value, const i32 maxValue);
#endif

View File

@ -4,27 +4,17 @@
#include "Dengine/AssetManager.h"
#include "Dengine/Audio.h"
#include "Dengine/Common.h"
#include "Dengine/Entity.h"
#include "Dengine/Math.h"
#include "Dengine/MemoryArena.h"
#include "Dengine/Platform.h"
#include "Dengine/Renderer.h"
#include "Dengine/UserInterface.h"
#define NUM_KEYS 1024
#define METERS_TO_PIXEL 240
typedef struct UiState
{
i32 hotItem;
i32 activeItem;
i32 kbdItem;
enum KeyCode keyEntered;
enum KeyCode keyMod;
enum KeyCode keyChar;
i32 lastWidget;
} UiState;
/* Forward declaration */
typedef struct Entity Entity;
typedef struct World
{