Files
EVEngine/src/modules/ui/UIHost.cpp
T

268 lines
8.7 KiB
C++

#include "ui/UIHost.h"
#include "ui/Widget.h"
#include <algorithm>
namespace eve::ui {
namespace {
uint32_t g_anonHostSeq = 0;
bool focusableType(NodeType type) {
switch (type) {
case NodeType::Button:
case NodeType::Checkbox:
case NodeType::Slider:
case NodeType::InputText:
case NodeType::ImageButton:
case NodeType::Combo:
case NodeType::Viewport:
case NodeType::SearchField:
case NodeType::Switch:
case NodeType::MenuItem: return true;
default: return false;
}
}
bool eligible(const UINode &node, bool sequential) {
return focusableType(node.type) && !node.id.empty() && node.visible && node.enabled &&
(sequential ? node.focusMode == FocusMode::All
: node.focusMode != FocusMode::None);
}
const std::string &neighbor(const UINode &node, FocusDirection direction) {
switch (direction) {
case FocusDirection::Next: return node.focusNext;
case FocusDirection::Previous: return node.focusPrevious;
case FocusDirection::Left: return node.focusLeft;
case FocusDirection::Right: return node.focusRight;
case FocusDirection::Up: return node.focusUp;
case FocusDirection::Down: return node.focusDown;
}
return node.focusNext;
}
} // namespace
UIHostHandle UIHost::createHost(const std::string &name) {
UIHost *h = UIHost::create();
if (!h) return {};
const UIHostHandle handle = ecs::handle_of(h);
h->meta()->entity = handle;
if (name.empty()) {
h->meta()->name = "host" + std::to_string(++g_anonHostSeq);
} else {
h->meta()->name = name;
}
return handle;
}
eve::OptionalRef<UIHost> UIHost::resolve(UIHostHandle handle) noexcept {
auto *entity = ecs::try_get(handle);
if (auto *host = entity ? dynamic_cast<UIHost *>(entity) : nullptr) return std::ref(*host);
return std::nullopt;
}
void UIHost::setName(const std::string &name) { meta()->name = name; }
const std::string &UIHost::getName() { return meta()->name; }
void UIHost::setTree(WidgetDesc root) { applyTree(this, std::move(root)); }
bool UIHost::setTreeReconcile(WidgetDesc root) { return applyTreeReconcile(this, std::move(root)); }
void UIHost::setSimplePanel(const std::string &title, const std::string &labelText,
const std::string &buttonText, const std::string &labelId,
const std::string &buttonId) {
setTree(window(title,
{
text(labelText, labelId),
button(buttonText, buttonId),
},
"root"));
}
eve::OptionalRef<UINode> UIHost::findById(const std::string &id) {
if (id.empty()) return std::nullopt;
auto t = tree();
for (auto &n : t->nodes) {
if (n.id == id) return std::ref(n);
}
return std::nullopt;
}
eve::OptionalRef<UINode> UIHost::findByKey(const std::string &key) {
if (key.empty()) return std::nullopt;
auto t = tree();
for (auto &n : t->nodes) {
if (n.key == key) return std::ref(n);
}
return std::nullopt;
}
void UIHost::setTextById(const std::string &id, const std::string &text) {
if (auto n = findById(id)) n->get().text = text;
}
void UIHost::setVisibleById(const std::string &id, bool visible) {
if (auto n = findById(id)) n->get().visible = visible;
}
void UIHost::setEnabledById(const std::string &id, bool enabled) {
if (auto n = findById(id)) {
n->get().enabled = enabled;
if (!enabled) {
n->get().focusRequested = false;
n->get().focused = false;
}
}
}
void UIHost::setCheckedById(const std::string &id, bool checked) {
if (auto n = findById(id)) n->get().checked = checked;
}
void UIHost::setValueById(const std::string &id, float value) {
if (auto n = findById(id)) n->get().value = value;
}
void UIHost::setValueTextById(const std::string &id, const std::string &value) {
if (auto n = findById(id)) n->get().valueText = value;
}
bool UIHost::requestFocusById(const std::string &id) {
auto target = findById(id);
if (!target || !eligible(target->get(), false)) return false;
auto t = tree();
for (auto &node : t->nodes) node.focusRequested = false;
target->get().focusRequested = true;
return true;
}
bool UIHost::moveFocus(FocusDirection direction, bool wrap) {
auto t = tree();
UINode *current = nullptr;
for (UINode &node : t->nodes) {
if (node.focused) {
current = &node;
break;
}
if (!current && node.focusRequested) current = &node;
}
if (current) {
const std::string &explicitId = neighbor(*current, direction);
if (!explicitId.empty()) {
auto explicitTarget = findById(explicitId);
if (explicitTarget && eligible(explicitTarget->get(), true)) return requestFocusById(explicitId);
}
}
std::vector<std::pair<int, std::size_t>> order;
order.reserve(t->nodes.size());
for (std::size_t index = 0; index < t->nodes.size(); ++index) {
const UINode &node = t->nodes[index];
if (eligible(node, true) && node.tabIndex >= 0)
order.emplace_back(node.tabIndex, index);
}
if (order.empty()) return false;
std::stable_sort(order.begin(), order.end());
const bool backwards = direction == FocusDirection::Previous ||
direction == FocusDirection::Left ||
direction == FocusDirection::Up;
std::size_t targetOrder = backwards ? order.size() - 1 : 0;
if (current) {
const auto found = std::find_if(order.begin(), order.end(),
[current, &t](const auto &entry) {
return &t->nodes[entry.second] == current;
});
if (found != order.end()) {
const std::size_t position = static_cast<std::size_t>(found - order.begin());
if (backwards) {
if (position == 0 && !wrap) return false;
targetOrder = position == 0 ? order.size() - 1 : position - 1;
} else {
if (position + 1 == order.size() && !wrap) return false;
targetOrder = position + 1 == order.size() ? 0 : position + 1;
}
}
}
return requestFocusById(t->nodes[order[targetOrder].second].id);
}
std::string UIHost::focusedId() {
auto t = tree();
for (const auto &node : t->nodes) {
if (node.focused) return node.id;
}
return {};
}
bool UIHost::setClickHandler(const std::string &id, std::function<void()> fn) {
auto n = findById(id);
if (!n) return false;
auto t = tree();
if (n->get().handlerClick != 0) {
size_t idx = size_t(n->get().handlerClick - 1);
if (idx < t->clickHandlers.size()) {
t->clickHandlers[idx] = std::move(fn);
return true;
}
}
t->clickHandlers.push_back(std::move(fn));
n->get().handlerClick = static_cast<uint32_t>(t->clickHandlers.size());
return true;
}
bool UIHost::setToggleHandler(const std::string &id, std::function<void(bool)> fn) {
auto n = findById(id);
if (!n) return false;
auto t = tree();
if (n->get().handlerToggle != 0) {
size_t idx = size_t(n->get().handlerToggle - 1);
if (idx < t->toggleHandlers.size()) {
t->toggleHandlers[idx] = std::move(fn);
return true;
}
}
t->toggleHandlers.push_back(std::move(fn));
n->get().handlerToggle = static_cast<uint32_t>(t->toggleHandlers.size());
return true;
}
bool UIHost::setValueHandler(const std::string &id, std::function<void(float)> fn) {
auto n = findById(id);
if (!n) return false;
auto t = tree();
if (n->get().handlerValue != 0) {
size_t idx = size_t(n->get().handlerValue - 1);
if (idx < t->valueHandlers.size()) {
t->valueHandlers[idx] = std::move(fn);
return true;
}
}
t->valueHandlers.push_back(std::move(fn));
n->get().handlerValue = static_cast<uint32_t>(t->valueHandlers.size());
return true;
}
bool UIHost::setTextHandler(const std::string &id, std::function<void(const std::string &)> fn) {
auto n = findById(id);
if (!n) return false;
auto t = tree();
if (n->get().handlerText != 0) {
size_t idx = size_t(n->get().handlerText - 1);
if (idx < t->textHandlers.size()) {
t->textHandlers[idx] = std::move(fn);
return true;
}
}
t->textHandlers.push_back(std::move(fn));
n->get().handlerText = static_cast<uint32_t>(t->textHandlers.size());
return true;
}
} // namespace eve::ui