Merge branch 'main' into debugging-client

This commit is contained in:
MihailRis 2025-11-14 21:18:16 +03:00
commit 091542da39
32 changed files with 220 additions and 166 deletions

View File

@ -192,7 +192,7 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
gui.add(contentAccessPanel); gui.add(contentAccessPanel);
auto dplotter = std::make_shared<Plotter>(gui, 350, 250, 2000, 16); auto dplotter = std::make_shared<Plotter>(gui, 350, 250, 2000, 16);
dplotter->setGravity(Gravity::bottom_right); dplotter->setGravity(Gravity::BOTTOM_RIGHT);
dplotter->setInteractive(false); dplotter->setInteractive(false);
add(HudElement(HudElementMode::PERMANENT, nullptr, dplotter, true)); add(HudElement(HudElementMode::PERMANENT, nullptr, dplotter, true));

View File

@ -470,6 +470,32 @@ void ImageData::addColor(const ImageData& other, int multiplier) {
} }
} }
void ImageData::extend(int newWidth, int newHeight) {
size_t comps;
switch (format) {
case ImageFormat::rgb888: comps = 3; break;
case ImageFormat::rgba8888: comps = 4; break;
default:
throw std::runtime_error("only unsigned byte formats supported");
}
auto newData = std::make_unique<ubyte[]>(newWidth * newHeight * comps);
for (uint y = 0; y < newHeight; y++) {
for (uint x = 0; x < newWidth; x++) {
for (size_t c = 0; c < comps; c++) {
if (x < width && y < height) {
newData[(y * newWidth + x) * comps + c] =
data[(y * width + x) * comps + c];
} else {
newData[(y * newWidth + x) * comps + c] = 0;
}
}
}
}
data = std::move(newData);
width = newWidth;
height = newHeight;
}
void ImageData::addColor(const glm::ivec4& color, int multiplier) { void ImageData::addColor(const glm::ivec4& color, int multiplier) {
uint comps; uint comps;
switch (format) { switch (format) {

View File

@ -36,6 +36,7 @@ public:
void mulColor(const ImageData& other); void mulColor(const ImageData& other);
void addColor(const glm::ivec4& color, int multiplier); void addColor(const glm::ivec4& color, int multiplier);
void addColor(const ImageData& other, int multiplier); void addColor(const ImageData& other, int multiplier);
void extend(int newWidth, int newHeight);
std::unique_ptr<ImageData> cropped(int x, int y, int width, int height) const; std::unique_ptr<ImageData> cropped(int x, int y, int width, int height) const;

View File

@ -44,10 +44,10 @@ void Texture::unbind() const {
void Texture::reload(const ImageData& image) { void Texture::reload(const ImageData& image) {
width = image.getWidth(); width = image.getWidth();
height = image.getHeight(); height = image.getHeight();
reload(image.getData()); reload(image.getData(), width, height);
} }
void Texture::reload(const ubyte* data) { void Texture::reload(const ubyte* data, uint width, uint height) {
glBindTexture(GL_TEXTURE_2D, id); glBindTexture(GL_TEXTURE_2D, id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, static_cast<const GLvoid*>(data)); GL_RGBA, GL_UNSIGNED_BYTE, static_cast<const GLvoid*>(data));

View File

@ -18,7 +18,7 @@ public:
virtual void bind() const; virtual void bind() const;
virtual void unbind() const; virtual void unbind() const;
void reload(const ubyte* data); void reload(const ubyte* data, uint w, uint h);
void reloadPartial(const ImageData& image, uint x, uint y, uint w, uint h); void reloadPartial(const ImageData& image, uint x, uint y, uint w, uint h);
void setNearestFilter(); void setNearestFilter();

View File

@ -30,14 +30,14 @@ namespace gui {
glm::vec2 size, glm::vec2 size,
glm::vec4 padding = glm::vec4(0.0f), glm::vec4 padding = glm::vec4(0.0f),
float interval = 2.0f, float interval = 2.0f,
Orientation orientation = Orientation::vertical Orientation orientation = Orientation::VERTICAL
) )
: Container(gui, std::move(size)), : Container(gui, std::move(size)),
padding(std::move(padding)), padding(std::move(padding)),
interval(interval) { interval(interval) {
} }
Orientation orientation = Orientation::vertical; Orientation orientation = Orientation::VERTICAL;
glm::vec4 padding; glm::vec4 padding;
float interval = 2.0f; float interval = 2.0f;
}; };

View File

@ -31,7 +31,7 @@ Button::Button(
GUI& gui, GUI& gui,
const std::wstring& text, const std::wstring& text,
glm::vec4 padding, glm::vec4 padding,
const onaction& action, const OnAction& action,
glm::vec2 size glm::vec2 size
) )
: Panel(gui, size, padding, 0.0f) { : Panel(gui, size, padding, 0.0f) {
@ -42,12 +42,12 @@ Button::Button(
} }
if (action) { if (action) {
listenAction(action); listenClick(action);
} }
setScrollable(false); setScrollable(false);
label = std::make_shared<Label>(gui, text); label = std::make_shared<Label>(gui, text);
label->setAlign(Align::center); label->setAlign(Align::CENTER);
label->setSize(getContentSize()); label->setSize(getContentSize());
label->setInteractive(false); label->setInteractive(false);
add(label); add(label);
@ -95,5 +95,5 @@ Align Button::getTextAlign() const {
if (label) { if (label) {
return label->getAlign(); return label->getAlign();
} }
return Align::left; return Align::LEFT;
} }

View File

@ -19,7 +19,7 @@ namespace gui {
GUI& gui, GUI& gui,
const std::wstring& text, const std::wstring& text,
glm::vec4 padding, glm::vec4 padding,
const onaction& action, const OnAction& action,
glm::vec2 size = glm::vec2(-1) glm::vec2 size = glm::vec2(-1)
); );

View File

@ -4,11 +4,11 @@
#include "graphics/core/DrawContext.hpp" #include "graphics/core/DrawContext.hpp"
#include "graphics/core/Texture.hpp" #include "graphics/core/Texture.hpp"
gui::Canvas::Canvas(GUI& gui, ImageFormat inFormat, glm::uvec2 inSize) gui::Canvas::Canvas(GUI& gui, ImageFormat format, glm::uvec2 size)
: UINode(gui, inSize) { : UINode(gui, size) {
auto data = std::make_shared<ImageData>(inFormat, inSize.x, inSize.y); auto data = std::make_shared<ImageData>(format, size.x, size.y);
mTexture = Texture::from(data.get()); texture = Texture::from(data.get());
mData = std::move(data); this->data = std::move(data);
} }
void gui::Canvas::draw(const DrawContext& pctx, const Assets& assets) { void gui::Canvas::draw(const DrawContext& pctx, const Assets& assets) {
@ -16,6 +16,12 @@ void gui::Canvas::draw(const DrawContext& pctx, const Assets& assets) {
auto col = calcColor(); auto col = calcColor();
auto batch = pctx.getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(mTexture.get()); batch->texture(texture.get());
batch->rect(pos.x, pos.y, size.x, size.y, 0, 0, 0, {}, false, false, col); batch->rect(pos.x, pos.y, size.x, size.y, 0, 0, 0, {}, false, false, col);
} }
void gui::Canvas::setSize(const glm::vec2& size) {
UINode::setSize(size);
data->extend(size.x, size.y);
texture->reload(*data);
}

View File

@ -9,21 +9,23 @@ class Texture;
namespace gui { namespace gui {
class Canvas final : public UINode { class Canvas final : public UINode {
public: public:
explicit Canvas(GUI& gui, ImageFormat inFormat, glm::uvec2 inSize); explicit Canvas(GUI& gui, ImageFormat format, glm::uvec2 size);
~Canvas() override = default; ~Canvas() override = default;
void draw(const DrawContext& pctx, const Assets& assets) override; void draw(const DrawContext& pctx, const Assets& assets) override;
[[nodiscard]] auto texture() const { void setSize(const glm::vec2& size) override;
return mTexture;
[[nodiscard]] auto getTexture() const {
return texture;
} }
[[nodiscard]] auto data() const { [[nodiscard]] auto getData() const {
return mData; return data;
} }
private: private:
std::shared_ptr<::Texture> mTexture; std::shared_ptr<::Texture> texture;
std::shared_ptr<ImageData> mData; std::shared_ptr<ImageData> data;
}; };
} }

View File

@ -52,7 +52,7 @@ FullCheckBox::FullCheckBox(
checkbox(std::make_shared<CheckBox>(gui, checked)), checkbox(std::make_shared<CheckBox>(gui, checked)),
label(std::make_shared<Label>(gui, text)) { label(std::make_shared<Label>(gui, text)) {
setColor(glm::vec4(0.0f)); setColor(glm::vec4(0.0f));
setOrientation(Orientation::horizontal); setOrientation(Orientation::HORIZONTAL);
add(checkbox); add(checkbox);

View File

@ -64,7 +64,7 @@ namespace gui {
checkbox->setTooltip(text); checkbox->setTooltip(text);
} }
virtual void setSize(glm::vec2 new_size) override { virtual void setSize(const glm::vec2& new_size) override {
Panel::setSize(new_size); Panel::setSize(new_size);
checkbox->setSize(glm::vec2(size.y, size.y)); checkbox->setSize(glm::vec2(size.y, size.y));
} }

View File

@ -211,11 +211,11 @@ void Container::clear() {
refresh(); refresh();
} }
void Container::listenInterval(float interval, ontimeout callback, int repeat) { void Container::listenInterval(float interval, OnTimeOut callback, int repeat) {
intervalEvents.push_back({std::move(callback), interval, 0.0f, repeat}); intervalEvents.push_back({std::move(callback), interval, 0.0f, repeat});
} }
void Container::setSize(glm::vec2 size) { void Container::setSize(const glm::vec2& size) {
if (size == getSize()) { if (size == getSize()) {
return; return;
} }

View File

@ -36,9 +36,9 @@ namespace gui {
virtual void remove(const std::string& id); virtual void remove(const std::string& id);
virtual void scrolled(int value) override; virtual void scrolled(int value) override;
virtual void setScrollable(bool flag); virtual void setScrollable(bool flag);
void listenInterval(float interval, ontimeout callback, int repeat=-1); void listenInterval(float interval, OnTimeOut callback, int repeat=-1);
virtual glm::vec2 getContentOffset() override {return glm::vec2(0.0f, scroll);}; virtual glm::vec2 getContentOffset() override {return glm::vec2(0.0f, scroll);};
virtual void setSize(glm::vec2 size) override; virtual void setSize(const glm::vec2& size) override;
virtual int getScrollStep() const; virtual int getScrollStep() const;
virtual void setScrollStep(int step); virtual void setScrollStep(int step);
virtual void refresh() override; virtual void refresh() override;

View File

@ -43,7 +43,7 @@ void InlineFrame::act(float delta) {
Container::act(delta); Container::act(delta);
} }
void InlineFrame::setSize(glm::vec2 size) { void InlineFrame::setSize(const glm::vec2& size) {
Container::setSize(size); Container::setSize(size);
if (root) { if (root) {
root->setSize(size); root->setSize(size);

View File

@ -5,7 +5,7 @@
class UiDocument; class UiDocument;
namespace gui { namespace gui {
class InlineFrame : public Container { class InlineFrame final : public Container {
public: public:
explicit InlineFrame(GUI& gui); explicit InlineFrame(GUI& gui);
virtual ~InlineFrame(); virtual ~InlineFrame();
@ -14,7 +14,7 @@ namespace gui {
void setDocument(const std::shared_ptr<UiDocument>& document); void setDocument(const std::shared_ptr<UiDocument>& document);
void act(float delta) override; void act(float delta) override;
void setSize(glm::vec2 size) override; void setSize(const glm::vec2& size) override;
const std::string& getSrc() const; const std::string& getSrc() const;
private: private:

View File

@ -5,7 +5,7 @@
namespace gui { namespace gui {
class Label; class Label;
class InputBindBox : public Panel { class InputBindBox final : public Panel {
protected: protected:
Binding& binding; Binding& binding;
glm::vec4 focusedColor {0.1f, 0.15f, 0.35f, 0.75f}; glm::vec4 focusedColor {0.1f, 0.15f, 0.35f, 0.75f};
@ -15,13 +15,13 @@ namespace gui {
GUI& gui, Binding& binding, glm::vec4 padding = glm::vec4(6.0f) GUI& gui, Binding& binding, glm::vec4 padding = glm::vec4(6.0f)
); );
virtual void drawBackground( void drawBackground(
const DrawContext& pctx, const Assets& assets const DrawContext& pctx, const Assets& assets
) override; ) override;
virtual void clicked(Mousecode button) override; void clicked(Mousecode button) override;
virtual void keyPressed(Keycode key) override; void keyPressed(Keycode key) override;
virtual bool isFocuskeeper() const override { bool isFocuskeeper() const override {
return true; return true;
} }
}; };

View File

@ -528,14 +528,14 @@ void InventoryView::setSelected(int index) {
} }
} }
void InventoryView::setPos(glm::vec2 pos) { void InventoryView::setPos(const glm::vec2& pos) {
Container::setPos(pos - origin); Container::setPos(pos - origin);
} }
void InventoryView::setOrigin(glm::vec2 origin) { void InventoryView::setOrigin(const glm::vec2& origin) {
this->origin = origin; this->origin = origin;
} }
glm::vec2 InventoryView::getOrigin() const { const glm::vec2& InventoryView::getOrigin() const {
return origin; return origin;
} }

View File

@ -109,7 +109,7 @@ namespace gui {
static inline std::string EXCHANGE_SLOT_NAME = "exchange-slot"; static inline std::string EXCHANGE_SLOT_NAME = "exchange-slot";
}; };
class InventoryView : public gui::Container { class InventoryView final : public gui::Container {
const Content* content = nullptr; const Content* content = nullptr;
std::shared_ptr<Inventory> inventory; std::shared_ptr<Inventory> inventory;
@ -120,10 +120,10 @@ namespace gui {
InventoryView(GUI& gui); InventoryView(GUI& gui);
virtual ~InventoryView(); virtual ~InventoryView();
virtual void setPos(glm::vec2 pos) override; void setPos(const glm::vec2& pos) override;
void setOrigin(glm::vec2 origin); void setOrigin(const glm::vec2& origin);
glm::vec2 getOrigin() const; const glm::vec2& getOrigin() const;
void setSelected(int index); void setSelected(int index);

View File

@ -230,14 +230,14 @@ void Label::draw(const DrawContext& pctx, const Assets& assets) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
switch (align) { switch (align) {
case Align::left: break; case Align::LEFT: break;
case Align::center: pos.x += (size.x-newsize.x)*0.5f; break; case Align::CENTER: pos.x += (size.x-newsize.x)*0.5f; break;
case Align::right: pos.x += size.x-newsize.x; break; case Align::RIGHT: pos.x += size.x-newsize.x; break;
} }
switch (valign) { switch (valign) {
case Align::top: break; case Align::TOP: break;
case Align::center: pos.y += (size.y-newsize.y)*0.5f; break; case Align::CENTER: pos.y += (size.y-newsize.y)*0.5f; break;
case Align::bottom: pos.y += size.y-newsize.y; break; case Align::BOTTOM: pos.y += size.y-newsize.y; break;
} }
textYOffset = pos.y-calcPos().y; textYOffset = pos.y-calcPos().y;
totalLineHeight = lineHeight; totalLineHeight = lineHeight;

View File

@ -44,7 +44,7 @@ namespace gui {
float lineInterval = 1.5f; float lineInterval = 1.5f;
/// @brief Vertical alignment (only when multiline is set to false) /// @brief Vertical alignment (only when multiline is set to false)
Align valign = Align::center; Align valign = Align::CENTER;
/// @brief Line separators and wrapping will be ignored if set to false /// @brief Line separators and wrapping will be ignored if set to false
bool multiline = false; bool multiline = false;

View File

@ -72,7 +72,7 @@ void Panel::refresh() {
float x = padding.x; float x = padding.x;
float y = padding.y; float y = padding.y;
glm::vec2 size = getSize(); glm::vec2 size = getSize();
if (orientation == Orientation::vertical) { if (orientation == Orientation::VERTICAL) {
float maxw = size.x; float maxw = size.x;
for (auto& node : nodes) { for (auto& node : nodes) {
const glm::vec4 margin = node->getMargin(); const glm::vec4 margin = node->getMargin();

View File

@ -19,7 +19,7 @@ SelectBox::SelectBox(
: Button(gui, selected.text, padding, nullptr, glm::vec2(contentWidth, -1)), : Button(gui, selected.text, padding, nullptr, glm::vec2(contentWidth, -1)),
options(std::move(options)) { options(std::move(options)) {
listenAction([this](GUI& gui) { listenClick([this](GUI& gui) {
auto panel = std::make_shared<Panel>(gui, getSize()); auto panel = std::make_shared<Panel>(gui, getSize());
panel->setPos(calcPos() + glm::vec2(0, size.y)); panel->setPos(calcPos() + glm::vec2(0, size.y));
for (const auto& option : this->options) { for (const auto& option : this->options) {
@ -41,7 +41,7 @@ SelectBox::SelectBox(
}); });
} }
void SelectBox::listenChange(onstringchange&& callback) { void SelectBox::listenChange(OnStringChange&& callback) {
changeCallbacks.listen(std::move(callback)); changeCallbacks.listen(std::move(callback));
} }

View File

@ -24,7 +24,7 @@ namespace gui {
const glm::vec4& padding const glm::vec4& padding
); );
void listenChange(onstringchange&& callback); void listenChange(OnStringChange&& callback);
void setSelected(const Option& selected); void setSelected(const Option& selected);

View File

@ -7,7 +7,7 @@ using namespace gui;
SplitBox::SplitBox(GUI& gui, const glm::vec2& size, float splitPos, Orientation orientation) SplitBox::SplitBox(GUI& gui, const glm::vec2& size, float splitPos, Orientation orientation)
: BasePanel(gui, size, glm::vec4(), 4.0f, orientation), splitPos(splitPos) { : BasePanel(gui, size, glm::vec4(), 4.0f, orientation), splitPos(splitPos) {
setCursor( setCursor(
orientation == Orientation::vertical ? CursorShape::NS_RESIZE orientation == Orientation::VERTICAL ? CursorShape::NS_RESIZE
: CursorShape::EW_RESIZE : CursorShape::EW_RESIZE
); );
} }
@ -17,7 +17,7 @@ void SplitBox::mouseMove(int x, int y) {
auto size = getSize(); auto size = getSize();
glm::ivec2 cursor(x - pos.x, y - pos.y); glm::ivec2 cursor(x - pos.x, y - pos.y);
int axis = orientation == Orientation::vertical; int axis = orientation == Orientation::VERTICAL;
int v = cursor[axis]; int v = cursor[axis];
v = std::max(std::min(static_cast<int>(size[axis]) - 10, v), 10); v = std::max(std::min(static_cast<int>(size[axis]) - 10, v), 10);
@ -61,7 +61,7 @@ void SplitBox::refresh() {
nodeA->setPos(glm::vec2(padding)); nodeA->setPos(glm::vec2(padding));
const auto& p = padding; const auto& p = padding;
if (orientation == Orientation::vertical) { if (orientation == Orientation::VERTICAL) {
float splitPos = this->splitPos * size.y; float splitPos = this->splitPos * size.y;
nodeA->setSize({size.x-p.x-p.z, splitPos - sepRadius - p.y}); nodeA->setSize({size.x-p.x-p.z, splitPos - sepRadius - p.y});
nodeB->setSize({size.x-p.x-p.z, size.y - splitPos - sepRadius - p.w}); nodeB->setSize({size.x-p.x-p.z, size.y - splitPos - sepRadius - p.w});

View File

@ -216,7 +216,7 @@ TextBox::TextBox(GUI& gui, std::wstring placeholder, glm::vec4 padding)
lineNumbersLabel->setSize( lineNumbersLabel->setSize(
size - glm::vec2(padding.z + padding.x, padding.w + padding.y) size - glm::vec2(padding.z + padding.x, padding.w + padding.y)
); );
lineNumbersLabel->setVerticalAlign(Align::top); lineNumbersLabel->setVerticalAlign(Align::TOP);
add(lineNumbersLabel); add(lineNumbersLabel);
setHoverColor(glm::vec4(0.05f, 0.1f, 0.2f, 0.75f)); setHoverColor(glm::vec4(0.05f, 0.1f, 0.2f, 0.75f));
@ -565,7 +565,7 @@ bool TextBox::isValid() const {
void TextBox::setMultiline(bool multiline) { void TextBox::setMultiline(bool multiline) {
this->multiline = multiline; this->multiline = multiline;
label->setMultiline(multiline); label->setMultiline(multiline);
label->setVerticalAlign(multiline ? Align::top : Align::center); label->setVerticalAlign(multiline ? Align::TOP : Align::CENTER);
} }
bool TextBox::isMultiline() const { bool TextBox::isMultiline() const {

View File

@ -64,29 +64,24 @@ UINode* UINode::getParent() const {
return parent; return parent;
} }
UINode* UINode::listenAction(const onaction& action) { void UINode::listenClick(OnAction action) {
actions.listen(action); actions.listen(UIAction::CLICK, std::move(action));
return this;
} }
UINode* UINode::listenRightClick(const onaction& action) { void UINode::listenRightClick(OnAction action) {
rightClickCallbacks.listen(action); actions.listen(UIAction::RIGHT_CLICK, std::move(action));
return this;
} }
UINode* UINode::listenDoubleClick(const onaction& action) { void UINode::listenDoubleClick(OnAction action) {
doubleClickCallbacks.listen(action); actions.listen(UIAction::DOUBLE_CLICK, std::move(action));
return this;
} }
UINode* UINode::listenFocus(const onaction& action) { void UINode::listenFocus(OnAction action) {
focusCallbacks.listen(action); actions.listen(UIAction::FOCUS, std::move(action));
return this;
} }
UINode* UINode::listenDefocus(const onaction& action) { void UINode::listenDefocus(OnAction action) {
defocusCallbacks.listen(action); actions.listen(UIAction::DEFOCUS, std::move(action));
return this;
} }
void UINode::click(int, int) { void UINode::click(int, int) {
@ -95,21 +90,21 @@ void UINode::click(int, int) {
void UINode::clicked(Mousecode button) { void UINode::clicked(Mousecode button) {
if (button == Mousecode::BUTTON_2) { if (button == Mousecode::BUTTON_2) {
rightClickCallbacks.notify(gui); actions.notify(UIAction::RIGHT_CLICK, gui);
} }
} }
void UINode::doubleClick(int x, int y) { void UINode::doubleClick(int x, int y) {
pressed = true; pressed = true;
if (isInside(glm::vec2(x, y))) { if (isInside(glm::vec2(x, y))) {
doubleClickCallbacks.notify(gui); actions.notify(UIAction::DOUBLE_CLICK, gui);
} }
} }
void UINode::mouseRelease(int x, int y) { void UINode::mouseRelease(int x, int y) {
pressed = false; pressed = false;
if (isInside(glm::vec2(x, y))) { if (isInside(glm::vec2(x, y))) {
actions.notify(gui); actions.notify(UIAction::CLICK, gui);
} }
} }
@ -119,12 +114,12 @@ bool UINode::isPressed() const {
void UINode::onFocus() { void UINode::onFocus() {
focused = true; focused = true;
focusCallbacks.notify(gui); actions.notify(UIAction::FOCUS, gui);
} }
void UINode::defocus() { void UINode::defocus() {
focused = false; focused = false;
defocusCallbacks.notify(gui); actions.notify(UIAction::DEFOCUS, gui);
} }
bool UINode::isFocused() const { bool UINode::isFocused() const {
@ -208,7 +203,7 @@ glm::vec4 UINode::calcColor() const {
return color; return color;
} }
void UINode::setPos(glm::vec2 pos) { void UINode::setPos(const glm::vec2& pos) {
this->pos = pos; this->pos = pos;
} }
@ -220,7 +215,7 @@ glm::vec2 UINode::getSize() const {
return size; return size;
} }
void UINode::setSize(glm::vec2 size) { void UINode::setSize(const glm::vec2& size) {
this->size = glm::vec2( this->size = glm::vec2(
glm::max(minSize.x, glm::min(maxSize.x, size.x)), glm::max(minSize.x, glm::min(maxSize.x, size.x)),
glm::max(minSize.y, glm::min(maxSize.y, size.y)) glm::max(minSize.y, glm::min(maxSize.y, size.y))
@ -231,7 +226,7 @@ glm::vec2 UINode::getMinSize() const {
return minSize; return minSize;
} }
void UINode::setMinSize(glm::vec2 minSize) { void UINode::setMinSize(const glm::vec2& minSize) {
this->minSize = minSize; this->minSize = minSize;
setSize(getSize()); setSize(getSize());
} }
@ -240,7 +235,7 @@ glm::vec2 UINode::getMaxSize() const {
return maxSize; return maxSize;
} }
void UINode::setMaxSize(glm::vec2 maxSize) { void UINode::setMaxSize(const glm::vec2& maxSize) {
this->maxSize = maxSize; this->maxSize = maxSize;
setSize(getSize()); setSize(getSize());
} }
@ -345,7 +340,7 @@ void UINode::reposition() {
} }
void UINode::setGravity(Gravity gravity) { void UINode::setGravity(Gravity gravity) {
if (gravity == Gravity::none) { if (gravity == Gravity::NONE) {
setPositionFunc(nullptr); setPositionFunc(nullptr);
return; return;
} }
@ -360,27 +355,27 @@ void UINode::setGravity(Gravity gravity) {
float x = 0.0f, y = 0.0f; float x = 0.0f, y = 0.0f;
switch (gravity) { switch (gravity) {
case Gravity::top_left: case Gravity::TOP_LEFT:
case Gravity::center_left: case Gravity::CENTER_LEFT:
case Gravity::bottom_left: x = margin.x; break; case Gravity::BOTTOM_LEFT: x = margin.x; break;
case Gravity::top_center: case Gravity::TOP_CENTER:
case Gravity::center_center: case Gravity::CENTER_CENTER:
case Gravity::bottom_center: x = (parentSize.x-size.x)/2.0f; break; case Gravity::BOTTOM_CENTER: x = (parentSize.x-size.x)/2.0f; break;
case Gravity::top_right: case Gravity::TOP_RIGHT:
case Gravity::center_right: case Gravity::CENTER_RIGHT:
case Gravity::bottom_right: x = parentSize.x-size.x-margin.z; break; case Gravity::BOTTOM_RIGHT: x = parentSize.x-size.x-margin.z; break;
default: break; default: break;
} }
switch (gravity) { switch (gravity) {
case Gravity::top_left: case Gravity::TOP_LEFT:
case Gravity::top_center: case Gravity::TOP_CENTER:
case Gravity::top_right: y = margin.y; break; case Gravity::TOP_RIGHT: y = margin.y; break;
case Gravity::center_left: case Gravity::CENTER_LEFT:
case Gravity::center_center: case Gravity::CENTER_CENTER:
case Gravity::center_right: y = (parentSize.y-size.y)/2.0f; break; case Gravity::CENTER_RIGHT: y = (parentSize.y-size.y)/2.0f; break;
case Gravity::bottom_left: case Gravity::BOTTOM_LEFT:
case Gravity::bottom_center: case Gravity::BOTTOM_CENTER:
case Gravity::bottom_right: y = parentSize.y-size.y-margin.w; break; case Gravity::BOTTOM_RIGHT: y = parentSize.y-size.y-margin.w; break;
default: break; default: break;
} }
return glm::vec2(x, y); return glm::vec2(x, y);

View File

@ -19,9 +19,9 @@ namespace gui {
class GUI; class GUI;
class Container; class Container;
using onaction = std::function<void(GUI&)>; using OnAction = std::function<void(GUI&)>;
using onnumberchange = std::function<void(GUI&, double)>; using OnNumberChange = std::function<void(GUI&, double)>;
using onstringchange = std::function<void(GUI&, const std::string&)>; using OnStringChange = std::function<void(GUI&, const std::string&)>;
template<typename... Args> template<typename... Args>
class CallbacksSet { class CallbacksSet {
@ -46,28 +46,60 @@ namespace gui {
} }
}; };
using ActionsSet = CallbacksSet<GUI&>; template<class TagT, typename... Args>
class TaggedCallbacksSet {
public:
using Func = std::function<void(Args...)>;
private:
std::unique_ptr<std::vector<std::pair<TagT, Func>>> callbacks;
public:
void listen(TagT tag, Func&& callback) {
if (callbacks == nullptr) {
callbacks =
std::make_unique<std::vector<std::pair<TagT, Func>>>();
}
callbacks->push_back({tag, std::move(callback)});
}
void notify(TagT notifyTag, Args&&... args) {
if (callbacks == nullptr) {
return;
}
for (const auto& [tag, callback] : * callbacks) {
if (tag != notifyTag) {
continue;
}
callback(args...);
}
}
};
enum class UIAction {
CLICK, DOUBLE_CLICK, FOCUS, DEFOCUS, RIGHT_CLICK
};
using ActionsSet = TaggedCallbacksSet<UIAction, GUI&>;
using StringCallbacksSet = CallbacksSet<GUI&, const std::string&>; using StringCallbacksSet = CallbacksSet<GUI&, const std::string&>;
enum class Align { enum class Align {
left, center, right, LEFT, CENTER, RIGHT,
top=left, bottom=right, TOP=LEFT, BOTTOM=RIGHT,
}; };
enum class Gravity { enum class Gravity {
none, NONE,
top_left, TOP_LEFT,
top_center, TOP_CENTER,
top_right, TOP_RIGHT,
center_left, CENTER_LEFT,
center_center, CENTER_CENTER,
center_right, CENTER_RIGHT,
bottom_left, BOTTOM_LEFT,
bottom_center, BOTTOM_CENTER,
bottom_right BOTTOM_RIGHT
}; };
/// @brief Base abstract class for all UI elements /// @brief Base abstract class for all UI elements
@ -112,23 +144,15 @@ namespace gui {
/// @brief z-index property specifies the stack order of an element /// @brief z-index property specifies the stack order of an element
int zindex = 0; int zindex = 0;
/// @brief element content alignment (supported by Label only) /// @brief element content alignment (supported by Label only)
Align align = Align::left; Align align = Align::LEFT;
/// @brief parent element /// @brief parent element
UINode* parent = nullptr; UINode* parent = nullptr;
/// @brief position supplier for the element (called on parent element size update) /// @brief position supplier for the element (called on parent element size update)
vec2supplier positionfunc = nullptr; vec2supplier positionfunc = nullptr;
/// @brief size supplier for the element (called on parent element size update) /// @brief size supplier for the element (called on parent element size update)
vec2supplier sizefunc = nullptr; vec2supplier sizefunc = nullptr;
/// @brief 'onclick' callbacks /// @brief parameterless callbacks
ActionsSet actions; ActionsSet actions;
/// @brief 'onrightclick' callbacks
ActionsSet rightClickCallbacks;
/// @brief 'ondoubleclick' callbacks
ActionsSet doubleClickCallbacks;
/// @brief 'onfocus' callbacks
ActionsSet focusCallbacks;
/// @brief 'ondefocus' callbacks
ActionsSet defocusCallbacks;
/// @brief element tooltip text /// @brief element tooltip text
std::wstring tooltip; std::wstring tooltip;
/// @brief element tooltip delay /// @brief element tooltip delay
@ -189,11 +213,11 @@ namespace gui {
/// @brief Get element z-index /// @brief Get element z-index
int getZIndex() const; int getZIndex() const;
virtual UINode* listenAction(const onaction& action); virtual void listenClick(OnAction action);
virtual UINode* listenRightClick(const onaction& action); virtual void listenRightClick(OnAction action);
virtual UINode* listenDoubleClick(const onaction& action); virtual void listenDoubleClick(OnAction action);
virtual UINode* listenFocus(const onaction& action); virtual void listenFocus(OnAction action);
virtual UINode* listenDefocus(const onaction& action); virtual void listenDefocus(OnAction action);
virtual void onFocus(); virtual void onFocus();
virtual void doubleClick(int x, int y); virtual void doubleClick(int x, int y);
@ -246,14 +270,14 @@ namespace gui {
virtual glm::vec2 getContentOffset() {return glm::vec2(0.0f);}; virtual glm::vec2 getContentOffset() {return glm::vec2(0.0f);};
/// @brief Calculate screen position of the element /// @brief Calculate screen position of the element
virtual glm::vec2 calcPos() const; virtual glm::vec2 calcPos() const;
virtual void setPos(glm::vec2 pos); virtual void setPos(const glm::vec2& pos);
virtual glm::vec2 getPos() const; virtual glm::vec2 getPos() const;
glm::vec2 getSize() const; glm::vec2 getSize() const;
virtual void setSize(glm::vec2 size); virtual void setSize(const glm::vec2& size);
glm::vec2 getMinSize() const; glm::vec2 getMinSize() const;
virtual void setMinSize(glm::vec2 size); virtual void setMinSize(const glm::vec2& size);
glm::vec2 getMaxSize() const; glm::vec2 getMaxSize() const;
virtual void setMaxSize(glm::vec2 size); virtual void setMaxSize(const glm::vec2& size);
/// @brief Called in containers when new element added /// @brief Called in containers when new element added
virtual void refresh() {}; virtual void refresh() {};
virtual void fullRefresh() { virtual void fullRefresh() {

View File

@ -3,12 +3,12 @@
#include <functional> #include <functional>
namespace gui { namespace gui {
enum class Orientation { vertical, horizontal }; enum class Orientation { VERTICAL, HORIZONTAL };
using ontimeout = std::function<void()>; using OnTimeOut = std::function<void()>;
struct IntervalEvent { struct IntervalEvent {
ontimeout callback; OnTimeOut callback;
float interval; float interval;
float timer; float timer;
// -1 - infinity, 1 - one time event // -1 - infinity, 1 - one time event

View File

@ -101,7 +101,7 @@ void guiutil::confirm(
gui, glm::vec2(600, 200), glm::vec4(8.0f), 8.0f gui, glm::vec2(600, 200), glm::vec4(8.0f), 8.0f
); );
panel->setGravity(Gravity::center_center); panel->setGravity(Gravity::CENTER_CENTER);
container->add(panel); container->add(panel);
panel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f)); panel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f));

View File

@ -30,31 +30,31 @@
using namespace gui; using namespace gui;
static Align align_from_string(std::string_view str, Align def) { static Align align_from_string(std::string_view str, Align def) {
if (str == "left") return Align::left; if (str == "left") return Align::LEFT;
if (str == "center") return Align::center; if (str == "center") return Align::CENTER;
if (str == "right") return Align::right; if (str == "right") return Align::RIGHT;
if (str == "top") return Align::top; if (str == "top") return Align::TOP;
if (str == "bottom") return Align::bottom; if (str == "bottom") return Align::BOTTOM;
return def; return def;
} }
static Gravity gravity_from_string(const std::string& str) { static Gravity gravity_from_string(const std::string& str) {
static const std::unordered_map<std::string, Gravity> gravity_names { static const std::unordered_map<std::string, Gravity> gravity_names {
{"top-left", Gravity::top_left}, {"top-left", Gravity::TOP_LEFT},
{"top-center", Gravity::top_center}, {"top-center", Gravity::TOP_CENTER},
{"top-right", Gravity::top_right}, {"top-right", Gravity::TOP_RIGHT},
{"center-left", Gravity::center_left}, {"center-left", Gravity::CENTER_LEFT},
{"center-center", Gravity::center_center}, {"center-center", Gravity::CENTER_CENTER},
{"center-right", Gravity::center_right}, {"center-right", Gravity::CENTER_RIGHT},
{"bottom-left", Gravity::bottom_left}, {"bottom-left", Gravity::BOTTOM_LEFT},
{"bottom-center", Gravity::bottom_center}, {"bottom-center", Gravity::BOTTOM_CENTER},
{"bottom-right", Gravity::bottom_right}, {"bottom-right", Gravity::BOTTOM_RIGHT},
}; };
auto found = gravity_names.find(str); auto found = gravity_names.find(str);
if (found != gravity_names.end()) { if (found != gravity_names.end()) {
return found->second; return found->second;
} }
return Gravity::none; return Gravity::NONE;
} }
static runnable create_runnable( static runnable create_runnable(
@ -73,7 +73,7 @@ static runnable create_runnable(
return nullptr; return nullptr;
} }
static onaction create_action( static OnAction create_action(
const UiXmlReader& reader, const UiXmlReader& reader,
const xml::xmlelement& element, const xml::xmlelement& element,
const std::string& name const std::string& name
@ -178,7 +178,7 @@ static void read_uinode(
} }
if (auto onclick = create_action(reader, element, "onclick")) { if (auto onclick = create_action(reader, element, "onclick")) {
node.listenAction(onclick); node.listenClick(onclick);
} }
if (auto onclick = create_action(reader, element, "onrightclick")) { if (auto onclick = create_action(reader, element, "onrightclick")) {
@ -252,7 +252,7 @@ static void read_base_panel_impl(
if (element.has("orientation")) { if (element.has("orientation")) {
auto& oname = element.attr("orientation").getText(); auto& oname = element.attr("orientation").getText();
if (oname == "horizontal") { if (oname == "horizontal") {
panel.setOrientation(Orientation::horizontal); panel.setOrientation(Orientation::HORIZONTAL);
} }
} }
} }
@ -277,7 +277,7 @@ static void read_panel_impl(
if (element.has("orientation")) { if (element.has("orientation")) {
auto& oname = element.attr("orientation").getText(); auto& oname = element.attr("orientation").getText();
if (oname == "horizontal") { if (oname == "horizontal") {
panel.setOrientation(Orientation::horizontal); panel.setOrientation(Orientation::HORIZONTAL);
} }
} }
if (subnodes) { if (subnodes) {
@ -338,7 +338,7 @@ static std::shared_ptr<UINode> read_label(
if (element.has("multiline")) { if (element.has("multiline")) {
label->setMultiline(element.attr("multiline").asBool()); label->setMultiline(element.attr("multiline").asBool());
if (!element.has("valign")) { if (!element.has("valign")) {
label->setVerticalAlign(Align::top); label->setVerticalAlign(Align::TOP);
} }
} }
if (element.has("text-wrap")) { if (element.has("text-wrap")) {
@ -364,8 +364,8 @@ static std::shared_ptr<UINode> read_split_box(
float splitPos = element.attr("split-pos", "0.5").asFloat(); float splitPos = element.attr("split-pos", "0.5").asFloat();
Orientation orientation = Orientation orientation =
element.attr("orientation", "vertical").getText() == "horizontal" element.attr("orientation", "vertical").getText() == "horizontal"
? Orientation::horizontal ? Orientation::HORIZONTAL
: Orientation::vertical; : Orientation::VERTICAL;
auto splitBox = std::make_shared<SplitBox>( auto splitBox = std::make_shared<SplitBox>(
reader.getGUI(), glm::vec2(), splitPos, orientation reader.getGUI(), glm::vec2(), splitPos, orientation
); );

View File

@ -380,7 +380,7 @@ static int p_get_region(UINode* node, lua::State* L) {
static int p_get_data(UINode* node, lua::State* L) { static int p_get_data(UINode* node, lua::State* L) {
if (auto canvas = dynamic_cast<Canvas*>(node)) { if (auto canvas = dynamic_cast<Canvas*>(node)) {
return lua::newuserdata<lua::LuaCanvas>(L, canvas->texture(), canvas->data()); return lua::newuserdata<lua::LuaCanvas>(L, canvas->getTexture(), canvas->getData());
} }
return 0; return 0;
} }