Added quad rendering and a (WIP) button type.
This commit is contained in:
parent
c469772afa
commit
eb47d92099
BIN
assets/bitmaps/button.png
Normal file
BIN
assets/bitmaps/button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 849 B |
@ -1,2 +1,2 @@
|
||||
symtex_glyphs ../fonts/symtex.png
|
||||
sdl_logo sdl.png
|
||||
button button.png
|
@ -78,6 +78,11 @@ void UIApp::init(const AppInitArgs& args)
|
||||
.posX = 100,
|
||||
.posY = 100
|
||||
});
|
||||
mButton = mWidgetTree.getRootWidget().emplaceChild<Button>({
|
||||
.text = "Click Me!",
|
||||
.posX = 100,
|
||||
.posY = 500
|
||||
});
|
||||
}
|
||||
|
||||
void UIApp::update(const AppUpdateArgs& args)
|
||||
|
@ -4,9 +4,8 @@
|
||||
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_6_UI_APP_HPP_INCLUDED)
|
||||
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_6_UI_APP_HPP_INCLUDED 1
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
#include "../application.hpp"
|
||||
#include "../gui/button.hpp"
|
||||
#include "../gui/label.hpp"
|
||||
#include "../gui/widget.hpp"
|
||||
#include "../gui/ui_renderer.hpp"
|
||||
@ -27,6 +26,7 @@ private:
|
||||
WidgetTree mWidgetTree;
|
||||
|
||||
Label* mLabel;
|
||||
Button* mButton;
|
||||
|
||||
sdlpp::Gamepad mGamepad;
|
||||
Uint32 mNumVertices = 0;
|
||||
|
@ -5,6 +5,7 @@ src_files = Split("""
|
||||
main.cpp
|
||||
|
||||
application.cpp
|
||||
gui/button.cpp
|
||||
gui/label.cpp
|
||||
gui/ui_renderer.cpp
|
||||
gui/widget.cpp
|
||||
|
93
private/sdl_gpu_test/gui/button.cpp
Normal file
93
private/sdl_gpu_test/gui/button.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
#include "./button.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
Button::Button(ButtonCreateArgs args) : mText(std::move(args.text)), mTextColor(args.textColor),
|
||||
mBackgroundColor(args.backgroundColor), mPosX(args.posX), mPosY(args.posY), mWidth(args.width), mHeight(args.height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Button::setText(std::string text)
|
||||
{
|
||||
if (text != mText)
|
||||
{
|
||||
mText = std::move(text);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setTextColor(const glm::vec4& color)
|
||||
{
|
||||
if (color != mTextColor)
|
||||
{
|
||||
mTextColor = color;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setBackgroundColor(const glm::vec4& color)
|
||||
{
|
||||
if (color != mBackgroundColor)
|
||||
{
|
||||
mBackgroundColor = color;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setPosX(int posX)
|
||||
{
|
||||
if (posX != mPosX)
|
||||
{
|
||||
mPosX = posX;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setPosY(int posY)
|
||||
{
|
||||
if (posY != mPosY)
|
||||
{
|
||||
mPosY = posY;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::handleEnteredTree()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void Button::revalidate()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void Button::update()
|
||||
{
|
||||
if (mTree == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPrimitiveID != UIRenderer::UNSET_PRIMITIVE_ID)
|
||||
{
|
||||
mTree->getRenderer().removePrimitive(mPrimitiveID);
|
||||
}
|
||||
mTree->getRenderer().drawQuad({
|
||||
.texture = "button",
|
||||
.x = mPosX,
|
||||
.y = mPosY,
|
||||
.width = mWidth,
|
||||
.height = mHeight,
|
||||
.color = mBackgroundColor
|
||||
}, &mPrimitiveID);
|
||||
mTree->getRenderer().drawTextCentered({
|
||||
.x = mPosX + (mWidth / 2),
|
||||
.y = mPosY + (mHeight / 2),
|
||||
.text = mText,
|
||||
.color = mTextColor
|
||||
}, &mPrimitiveID);
|
||||
}
|
||||
}
|
68
private/sdl_gpu_test/gui/button.hpp
Normal file
68
private/sdl_gpu_test/gui/button.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_GUI_BUTTON_HPP_INCLUDED)
|
||||
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_GUI_BUTTON_HPP_INCLUDED 1
|
||||
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#include "./widget.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
struct ButtonCreateArgs
|
||||
{
|
||||
std::string text;
|
||||
glm::vec4 textColor = {1.f, 1.f, 1.f, 1.f};
|
||||
glm::vec4 backgroundColor = {1.f, 0.f, 0.f, 1.f};
|
||||
int posX = 0;
|
||||
int posY = 0;
|
||||
int width = 256;
|
||||
int height = 84;
|
||||
};
|
||||
|
||||
class Button : public Widget
|
||||
{
|
||||
public:
|
||||
using create_args_t = ButtonCreateArgs;
|
||||
private:
|
||||
std::string mText;
|
||||
glm::vec4 mTextColor = {1.f, 1.f, 1.f, 1.f};
|
||||
glm::vec4 mBackgroundColor = {1.f, 1.f, 1.f, 1.f};
|
||||
int mPosX = 0;
|
||||
int mPosY = 0;
|
||||
int mWidth = 0;
|
||||
int mHeight = 0;
|
||||
UIRenderer::primitive_id_t mPrimitiveID = UIRenderer::UNSET_PRIMITIVE_ID;
|
||||
public:
|
||||
explicit Button(ButtonCreateArgs args);
|
||||
|
||||
[[nodiscard]]
|
||||
const std::string& getText() const noexcept { return mText; }
|
||||
|
||||
[[nodiscard]]
|
||||
const glm::vec4& getTextColor() const noexcept { return mTextColor; }
|
||||
|
||||
[[nodiscard]]
|
||||
const glm::vec4& getBackgroundTextColor() const noexcept { return mBackgroundColor; }
|
||||
|
||||
[[nodiscard]]
|
||||
int getPosX() const noexcept { return mPosX; }
|
||||
|
||||
[[nodiscard]]
|
||||
int getPosY() const noexcept { return mPosY; }
|
||||
|
||||
void setText(std::string text);
|
||||
void setTextColor(const glm::vec4& color);
|
||||
void setBackgroundColor(const glm::vec4& color);
|
||||
void setPosX(int posX);
|
||||
void setPosY(int posY);
|
||||
|
||||
void handleEnteredTree() override;
|
||||
void revalidate() override;
|
||||
private:
|
||||
void update();
|
||||
};
|
||||
} // namespace sdl_gpu_test
|
||||
|
||||
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_GUI_BUTTON_HPP_INCLUDED)
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include <mijin/util/string.hpp>
|
||||
|
||||
#include "../util/spdlog_wrapper.hpp"
|
||||
|
||||
namespace sdl_gpu_test::inline app6
|
||||
{
|
||||
namespace
|
||||
@ -151,6 +153,69 @@ void UIRenderer::render(const UIRendererRenderArgs& args)
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> UIRenderer::measureText(const MeasureTextArgs& args) const
|
||||
{
|
||||
unsigned width = 0;
|
||||
unsigned height = mFontMap.lineHeight;
|
||||
unsigned lineWidth = 0;
|
||||
|
||||
for (std::size_t pos = 0; pos < args.text.size(); ++pos)
|
||||
{
|
||||
char chr = args.text[pos];
|
||||
switch (chr)
|
||||
{
|
||||
case '\n':
|
||||
width = std::max(lineWidth, width);
|
||||
lineWidth = 0;
|
||||
height += mFontMap.lineHeight;
|
||||
break;
|
||||
case '$':
|
||||
++pos;
|
||||
if (pos >= args.text.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
chr = args.text[pos];
|
||||
switch (chr)
|
||||
{
|
||||
case 'r':
|
||||
break;
|
||||
default:
|
||||
if (pos < args.text.size() - 2
|
||||
&& mijin::isHexadecimalChar(chr)
|
||||
&& mijin::isHexadecimalChar(args.text[pos + 1])
|
||||
&& mijin::isHexadecimalChar(args.text[pos + 2]))
|
||||
{
|
||||
pos += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// what?
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
++pos;
|
||||
if (pos >= args.text.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
chr = args.text[pos];
|
||||
[[fallthrough]];
|
||||
default:
|
||||
{
|
||||
const UVFontMapEntry& entry = mFontMap.entries[chr < 0 ? '_' : chr]; // TODO: more chars
|
||||
lineWidth += entry.xAdvance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {std::max(width, lineWidth), height};
|
||||
}
|
||||
|
||||
|
||||
void UIRenderer::drawText(const DrawTextArgs& args, primitive_id_t* outPrimitiveId)
|
||||
{
|
||||
glm::vec4 color = args.color;
|
||||
@ -169,7 +234,7 @@ void UIRenderer::drawText(const DrawTextArgs& args, primitive_id_t* outPrimitive
|
||||
{
|
||||
case '\n':
|
||||
posX = args.x;
|
||||
posY += mFontMap.lineHeight;
|
||||
posY += static_cast<int>(mFontMap.lineHeight);
|
||||
break;
|
||||
case '$':
|
||||
++pos;
|
||||
@ -215,17 +280,28 @@ void UIRenderer::drawText(const DrawTextArgs& args, primitive_id_t* outPrimitive
|
||||
chr = args.text[pos];
|
||||
[[fallthrough]];
|
||||
default:
|
||||
posX += drawChar({
|
||||
posX += static_cast<int>(drawChar({
|
||||
.x = posX,
|
||||
.y = posY,
|
||||
.chr = chr,
|
||||
.color = color
|
||||
}, outPrimitiveId);
|
||||
}, outPrimitiveId));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIRenderer::drawTextCentered(DrawTextArgs args, primitive_id_t* outPrimitiveId)
|
||||
{
|
||||
const auto [width, height] = measureText({
|
||||
.text = args.text
|
||||
});
|
||||
args.x -= static_cast<int>(width / 2);
|
||||
args.y -= static_cast<int>(height + mFontMap.lineHeight - mFontMap.base) / 2;
|
||||
drawText(args, outPrimitiveId);
|
||||
}
|
||||
|
||||
|
||||
unsigned UIRenderer::drawChar(const DrawCharArgs& args, primitive_id_t* outPrimitiveId)
|
||||
{
|
||||
if (outPrimitiveId != nullptr && *outPrimitiveId == UNSET_PRIMITIVE_ID)
|
||||
@ -246,6 +322,31 @@ unsigned UIRenderer::drawChar(const DrawCharArgs& args, primitive_id_t* outPrimi
|
||||
return entry.xAdvance;
|
||||
}
|
||||
|
||||
void UIRenderer::drawQuad(const DrawQuadArgs& args, primitive_id_t* outPrimitiveId)
|
||||
{
|
||||
auto itEntry = mTextureAtlas.entries.find(args.texture);
|
||||
if (itEntry == mTextureAtlas.entries.end())
|
||||
{
|
||||
spdlog::warn("Attempt to draw quad with unknown texture: {}.", args.texture);
|
||||
return;
|
||||
}
|
||||
const UVTextureAtlasEntry& entry = itEntry->second;
|
||||
|
||||
if (outPrimitiveId != nullptr && *outPrimitiveId == UNSET_PRIMITIVE_ID)
|
||||
{
|
||||
*outPrimitiveId = mNextOwner++;
|
||||
}
|
||||
|
||||
drawQuadInternal({
|
||||
.topLeft = {args.x, args.y},
|
||||
.bottomRight = {args.x + args.width, args.y + args.height},
|
||||
.uvTopLeft = {entry.uvX, entry.uvY},
|
||||
.uvBottomRight = {entry.uvX + entry.uvWidth, entry.uvY + entry.uvHeight},
|
||||
.color = args.color
|
||||
}, outPrimitiveId == nullptr ? UNSET_PRIMITIVE_ID : *outPrimitiveId);
|
||||
}
|
||||
|
||||
|
||||
bool UIRenderer::removePrimitive(primitive_id_t primitiveId)
|
||||
{
|
||||
bool didRemove = false;
|
||||
@ -254,7 +355,8 @@ bool UIRenderer::removePrimitive(primitive_id_t primitiveId)
|
||||
if (mTriangleOwners[triangleIdx] == primitiveId)
|
||||
{
|
||||
mTriangleOwners.erase(mTriangleOwners.begin() + triangleIdx);
|
||||
mVertices.erase(mVertices.begin() + 3 * triangleIdx, mVertices.begin() + 3 * (triangleIdx + 1));
|
||||
mVertices.erase(mVertices.begin() + static_cast<std::ptrdiff_t>(3) * triangleIdx,
|
||||
mVertices.begin() + static_cast<std::ptrdiff_t>(3) * (triangleIdx + 1));
|
||||
didRemove = true;
|
||||
}
|
||||
}
|
||||
@ -294,11 +396,11 @@ void UIRenderer::drawQuadInternal(const DrawQuadInternalArgs& args, primitive_id
|
||||
mVerticesDirty = true;
|
||||
}
|
||||
|
||||
void UIRenderer::addTriangleInternal(const UIVertex& v0, const UIVertex& v1, const UIVertex& v2, UIRenderer::primitive_id_t primitiveId)
|
||||
void UIRenderer::addTriangleInternal(const UIVertex& vtx0, const UIVertex& vtx1, const UIVertex& vtx2, primitive_id_t primitiveId)
|
||||
{
|
||||
mVertices.push_back(v0);
|
||||
mVertices.push_back(v1);
|
||||
mVertices.push_back(v2);
|
||||
mVertices.push_back(vtx0);
|
||||
mVertices.push_back(vtx1);
|
||||
mVertices.push_back(vtx2);
|
||||
mTriangleOwners.push_back(primitiveId);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,11 @@ struct UIVertex
|
||||
glm::vec4 color = glm::vec4(1.f);
|
||||
};
|
||||
|
||||
struct MeasureTextArgs
|
||||
{
|
||||
std::string_view text;
|
||||
};
|
||||
|
||||
struct DrawTextArgs
|
||||
{
|
||||
int x = 0;
|
||||
@ -36,6 +41,16 @@ struct DrawCharArgs
|
||||
glm::vec4 color = glm::vec4(1.f);
|
||||
};
|
||||
|
||||
struct DrawQuadArgs
|
||||
{
|
||||
std::string texture;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int width;
|
||||
int height;
|
||||
glm::vec4 color = glm::vec4(1.f);
|
||||
};
|
||||
|
||||
struct UIRendererRenderArgs
|
||||
{
|
||||
const sdlpp::GPUCommandBuffer* cmdBuffer;
|
||||
@ -67,8 +82,12 @@ public:
|
||||
void init(Application& application);
|
||||
void render(const UIRendererRenderArgs& args);
|
||||
|
||||
[[nodiscard]]
|
||||
std::pair<unsigned, unsigned> measureText(const MeasureTextArgs& args) const;
|
||||
void drawText(const DrawTextArgs& args, primitive_id_t* outPrimitiveId = nullptr);
|
||||
void drawTextCentered(DrawTextArgs args, primitive_id_t* outPrimitiveId = nullptr);
|
||||
unsigned drawChar(const DrawCharArgs& args, primitive_id_t* outPrimitiveId = nullptr);
|
||||
void drawQuad(const DrawQuadArgs& args, primitive_id_t* outPrimitiveId = nullptr);
|
||||
bool removePrimitive(primitive_id_t primitiveId);
|
||||
private:
|
||||
struct DrawQuadInternalArgs
|
||||
@ -80,7 +99,7 @@ private:
|
||||
glm::vec4 color = glm::vec4(1.f);
|
||||
};
|
||||
void drawQuadInternal(const DrawQuadInternalArgs& args, primitive_id_t primitiveId);
|
||||
void addTriangleInternal(const UIVertex& v0, const UIVertex& v1, const UIVertex& v2, primitive_id_t primitiveId);
|
||||
void addTriangleInternal(const UIVertex& vtx0, const UIVertex& vtx1, const UIVertex& vtx2, primitive_id_t primitiveId);
|
||||
void uploadVertices();
|
||||
};
|
||||
} // namespace sdl_gpu_test::inline app6
|
||||
|
Loading…
x
Reference in New Issue
Block a user