osb/source/windowing/StarWidget.cpp
2023-07-29 02:12:03 +10:00

479 lines
8.9 KiB
C++

#include "StarWidget.hpp"
#include "StarPane.hpp"
#include "StarLabelWidget.hpp"
#include "StarFlowLayout.hpp"
namespace Star {
Widget::Widget() {
m_parent = nullptr;
m_context = GuiContext::singletonPtr();
m_visible = true;
m_focus = false;
m_doScissor = true;
m_container = false;
m_mouseTransparent = false;
}
Widget::~Widget() {
removeAllChildren();
}
void Widget::update(float dt) {
for (auto& widget : m_members)
widget->update(dt);
}
GuiContext* Widget::context() const {
return m_context;
}
void Widget::render(RectI const& region) {
if (!m_visible)
return;
if (!setupDrawRegion(region))
return;
renderImpl();
drawChildren();
}
void Widget::renderImpl() {}
void Widget::drawChildren() {
for (auto child : m_members)
child->render(m_drawingArea);
}
Vec2I Widget::position() const {
return m_position + m_drawingOffset;
}
Vec2I Widget::relativePosition() const {
return m_position;
}
bool Widget::setupDrawRegion(RectI const& region) {
RectI scissorRect;
if (m_doScissor) {
scissorRect = getScissorRect();
} else {
scissorRect = noScissor();
}
m_drawingArea = scissorRect.limited(region);
if (m_drawingArea.isEmpty())
return false;
m_context->setInterfaceScissorRect(m_drawingArea);
return true;
}
Vec2I Widget::drawingOffset() const {
return m_drawingOffset;
}
void Widget::setDrawingOffset(Vec2I const& offset) {
m_drawingOffset = offset;
}
RectI Widget::getScissorRect() const {
return screenBoundRect();
}
RectI Widget::noScissor() const {
return RectI::inf();
}
void Widget::disableScissoring() {
m_doScissor = false;
}
void Widget::enableScissoring() {
m_doScissor = true;
}
void Widget::setPosition(Vec2I const& position) {
m_position = position;
}
Vec2I Widget::size() const {
return m_size;
}
void Widget::setSize(Vec2I const& size) {
m_size = size;
}
RectI Widget::relativeBoundRect() const {
return RectI::withSize(relativePosition(), size());
}
RectI Widget::screenBoundRect() const {
return relativeBoundRect().translated(screenPosition() - relativePosition());
}
void Widget::determineSizeFromChildren() {
Vec2I max;
for (auto& child : m_members) {
Vec2I childMax = child->position() + child->size();
max = max.piecewiseMax(childMax);
}
setSize(max);
}
void Widget::markAsContainer() {
m_container = true;
}
KeyboardCaptureMode Widget::keyboardCaptured() const {
if (active()) {
for (auto const& member : m_members) {
auto mode = member->keyboardCaptured();
if (mode != KeyboardCaptureMode::None)
return mode;
}
}
return KeyboardCaptureMode::None;
}
void Widget::setData(Json const& data) {
m_data = data;
}
Json const& Widget::data() {
return m_data;
}
Vec2I Widget::screenPosition() const {
if (m_parent) {
return m_parent->screenPosition() + position();
} else {
return position();
}
}
bool Widget::inMember(Vec2I const& position) const {
if (!m_visible)
return false;
if (m_mouseTransparent)
return false;
if (!m_drawingArea.isNull() && !m_drawingArea.contains(Vec2I::floor(position)))
return false;
if (m_container) {
for (auto child : m_members)
if (child->inMember(position))
return true;
} else {
return screenBoundRect().contains(position);
}
return false;
}
bool Widget::sendEvent(InputEvent const& event) {
if (!m_visible)
return false;
for (auto child : reverseIterate(m_members)) {
if (child->sendEvent(event))
return true;
}
return false;
}
void Widget::mouseOver() {}
void Widget::mouseOut() {}
void Widget::mouseReturnStillDown() {}
void Widget::setMouseTransparent(bool transparent) {
m_mouseTransparent = transparent;
}
bool Widget::mouseTransparent() {
return m_mouseTransparent;
}
Widget* Widget::parent() const {
return m_parent;
}
void Widget::setParent(Widget* parent) {
m_parent = parent;
}
void Widget::show() {
m_visible = true;
}
void Widget::hide() {
m_visible = false;
}
bool Widget::visibility() const {
return m_visible;
}
void Widget::toggleVisibility() {
m_visible = !m_visible;
}
void Widget::setVisibility(bool visibility) {
if (visibility)
show();
else
hide();
}
bool Widget::active() const {
return m_visible;
}
bool Widget::interactive() const {
return true;
}
bool Widget::hasFocus() const {
return m_focus;
}
void Widget::focus() {
m_focus = true;
if (auto w = window())
w->setFocus(this);
}
void Widget::blur() {
m_focus = false;
if (window())
window()->removeFocus(this);
}
unsigned Widget::windowHeight() const {
return context()->windowHeight();
}
unsigned Widget::windowWidth() const {
return context()->windowWidth();
}
Vec2I Widget::windowSize() const {
return Vec2I(context()->windowSize());
}
Pane* Widget::window() {
if (m_parent)
return m_parent->window();
return nullptr;
}
Pane const* Widget::window() const {
if (m_parent)
return m_parent->window();
return nullptr;
}
void Widget::addChild(String const& name, WidgetPtr member) {
member->setName(name);
m_members.push_back(member);
m_memberHash[member->name()] = member;
member->setParent(this);
}
void Widget::addChildAt(String const& name, WidgetPtr member, size_t at) {
if (at > m_members.size())
throw GuiException("Attempted to insert item after the end of the list.");
m_members.insert(m_members.begin() + at, member);
m_memberHash[name] = member;
member->setName(name);
member->setParent(this);
}
bool Widget::removeChild(Widget* member) {
m_memberHash.erase(member->name());
for (auto child = m_members.begin(); child != m_members.end(); ++child) {
if (child->get() == member) {
(*child)->setParent(nullptr);
m_members.erase(child);
return true;
} else {
if ((*child)->removeChild(member))
return true;
}
}
return false;
}
bool Widget::removeChild(String const& name) {
m_memberHash.erase(name);
for (auto child = m_members.begin(); child != m_members.end(); ++child) {
auto ptr = *child;
if (ptr->name() == name) {
m_members.erase(child);
ptr->setParent(nullptr);
return true;
}
}
return false;
}
bool Widget::removeChildAt(size_t at) {
if (at >= m_members.size())
return false;
m_memberHash.erase(m_members.at(at)->name());
m_members.at(at)->setParent(nullptr);
m_members.erase(m_members.begin() + at);
return true;
}
void Widget::removeAllChildren() {
for (auto child : m_members)
child->setParent(nullptr);
m_members.clear();
m_memberHash.clear();
}
bool Widget::containsChild(String const& name) {
if (fetchChild(name))
return true;
return false;
}
WidgetPtr Widget::fetchChild(String const& name) {
WidgetPtr res;
if (name.contains(".")) {
StringList nameList = name.split(".", 1);
if (auto child = m_memberHash.value(nameList[0], {}))
return child->fetchChild(nameList[1]);
} else {
if (auto child = m_memberHash.value(name, {}))
return child;
}
return {};
}
WidgetPtr Widget::findChild(String const& name) {
if (auto found = fetchChild(name))
return found;
for (auto const& child : m_members) {
if (auto found = child->findChild(name))
return found;
}
return {};
}
WidgetPtr Widget::childPtr(Widget const* child) const {
for (auto const& m : m_members) {
if (m.get() == child)
return m;
if (auto c = m->childPtr(child))
return c;
}
return {};
}
WidgetPtr Widget::getChildAt(Vec2I const& pos) {
for (auto child : reverseIterate(m_members)) {
if (child->inMember(pos)) {
auto res = child->getChildAt(pos);
if (res) {
return res;
}
return child;
}
}
return {};
}
size_t Widget::numChildren() const {
return m_members.size();
}
WidgetPtr Widget::getChildNum(size_t num) const {
return m_members.at(num);
}
String const& Widget::name() const {
return m_name;
}
void Widget::setName(String const& name) {
m_name = name;
}
String Widget::fullName() const {
if (m_parent) {
return m_parent->fullName() + "." + name();
}
return name();
}
String Widget::toStringImpl(int indentLevel) const {
auto leader = String(" ") * indentLevel;
String childrenString;
for (auto child : m_members) {
childrenString.append(child->toStringImpl(indentLevel + 4));
}
String output = strf(R"OUTPUT({}{} : {
{} address : %p,
{} visible : {},
{} position : {},
{} size : {},
{} children : {
{}
{} }
{}}
)OUTPUT",
leader,
m_name,
leader,
(void*)this,
leader,
m_visible ? "true" : "false",
leader,
m_position,
leader,
m_size,
leader,
childrenString,
leader,
leader);
return output;
}
bool Widget::setLabel(String const& name, String const& value) {
if (containsChild(name)) {
auto child = fetchChild(name);
if (auto label = as<LabelWidget>(child)) {
label->setText(value);
return true;
}
}
return false;
}
std::ostream& operator<<(std::ostream& os, Widget const& widget) {
os << widget.toStringImpl(0);
return os;
}
}