370 lines
9.4 KiB
C++
370 lines
9.4 KiB
C++
|
#include "StarPane.hpp"
|
||
|
#include "StarRoot.hpp"
|
||
|
#include "StarJsonExtra.hpp"
|
||
|
#include "StarAssets.hpp"
|
||
|
|
||
|
namespace Star {
|
||
|
|
||
|
EnumMap<PaneAnchor> const PaneAnchorNames{
|
||
|
{PaneAnchor::None, "none"},
|
||
|
{PaneAnchor::BottomLeft, "bottomLeft"},
|
||
|
{PaneAnchor::BottomRight, "bottomRight"},
|
||
|
{PaneAnchor::TopLeft, "topLeft"},
|
||
|
{PaneAnchor::TopRight, "topRight"},
|
||
|
{PaneAnchor::CenterBottom, "centerBottom"},
|
||
|
{PaneAnchor::CenterTop, "centerTop"},
|
||
|
{PaneAnchor::CenterLeft, "centerLeft"},
|
||
|
{PaneAnchor::CenterRight, "centerRight"},
|
||
|
{PaneAnchor::Center, "center"},
|
||
|
};
|
||
|
|
||
|
Pane::Pane() {
|
||
|
m_dragActive = m_lockPosition = false;
|
||
|
m_dismissed = true;
|
||
|
m_centerOffset = Vec2I();
|
||
|
m_anchor = PaneAnchor::None;
|
||
|
m_anchorOffset = Vec2I();
|
||
|
m_visible = false;
|
||
|
m_hasDisplayed = false;
|
||
|
|
||
|
auto assets = Root::singleton().assets();
|
||
|
m_fontSize = assets->json("/interface.config:font.baseSize").toInt();
|
||
|
m_iconOffset = jsonToVec2I(assets->json("/interface.config:paneIconOffset"));
|
||
|
m_titleOffset = jsonToVec2I(assets->json("/interface.config:paneTitleOffset"));
|
||
|
m_subTitleOffset = jsonToVec2I(assets->json("/interface.config:paneSubTitleOffset"));
|
||
|
m_titleColor = jsonToColor(assets->json("/interface.config:paneTitleColor"));
|
||
|
m_subTitleColor = jsonToColor(assets->json("/interface.config:paneSubTitleColor"));
|
||
|
}
|
||
|
|
||
|
void Pane::displayed() {
|
||
|
m_dismissed = false;
|
||
|
m_hasDisplayed = true;
|
||
|
show();
|
||
|
}
|
||
|
|
||
|
void Pane::dismissed() {
|
||
|
if (m_clickDown)
|
||
|
m_clickDown->mouseOut();
|
||
|
m_clickDown.reset();
|
||
|
if (m_mouseOver)
|
||
|
m_mouseOver->mouseOut();
|
||
|
m_mouseOver.reset();
|
||
|
hide();
|
||
|
m_dismissed = true;
|
||
|
}
|
||
|
|
||
|
void Pane::dismiss() {
|
||
|
m_dismissed = true;
|
||
|
}
|
||
|
|
||
|
bool Pane::isDismissed() const {
|
||
|
return m_dismissed;
|
||
|
}
|
||
|
|
||
|
bool Pane::isDisplayed() const {
|
||
|
return !m_dismissed;
|
||
|
}
|
||
|
|
||
|
bool Pane::sendEvent(InputEvent const& event) {
|
||
|
if (m_visible) {
|
||
|
if (event.is<MouseButtonDownEvent>() || event.is<MouseButtonUpEvent>() || event.is<MouseMoveEvent>()
|
||
|
|| event.is<MouseWheelEvent>()) {
|
||
|
Vec2I mousePos = *m_context->mousePosition(event);
|
||
|
// First, handle preliminary mouse out / click up events
|
||
|
if (m_mouseOver) {
|
||
|
if (!m_mouseOver->inMember(mousePos) || !m_mouseOver->active()) {
|
||
|
m_mouseOver->mouseOut();
|
||
|
m_mouseOver.reset();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (event.is<MouseButtonUpEvent>())
|
||
|
m_clickDown.reset();
|
||
|
|
||
|
WidgetPtr newClickDown;
|
||
|
WidgetPtr newMouseOver;
|
||
|
WidgetPtr newFocusWidget;
|
||
|
|
||
|
// Then, go through widgets in highest to lowest z-order and handle mouse
|
||
|
// over, focus, and capture events.
|
||
|
for (auto const& widget : reverseIterate(m_members)) {
|
||
|
if (widget->inMember(mousePos) && widget->active() && widget->interactive()) {
|
||
|
WidgetPtr topWidget = widget;
|
||
|
WidgetPtr child = getChildAt(mousePos);
|
||
|
if (child->active() && child->interactive()) {
|
||
|
if (event.is<MouseButtonDownEvent>()
|
||
|
&& (event.get<MouseButtonDownEvent>().mouseButton == MouseButton::Left
|
||
|
|| event.get<MouseButtonDownEvent>().mouseButton == MouseButton::Right)) {
|
||
|
if (!newClickDown)
|
||
|
newClickDown = child;
|
||
|
|
||
|
if (!newFocusWidget)
|
||
|
newFocusWidget = child;
|
||
|
}
|
||
|
|
||
|
if (!newMouseOver)
|
||
|
newMouseOver = child;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (m_clickDown != newClickDown)
|
||
|
m_clickDown = newClickDown;
|
||
|
|
||
|
if (m_mouseOver != newMouseOver) {
|
||
|
if (m_mouseOver)
|
||
|
m_mouseOver->mouseOut();
|
||
|
m_mouseOver = newMouseOver;
|
||
|
if (m_mouseOver) {
|
||
|
if (m_clickDown == m_mouseOver)
|
||
|
m_mouseOver->mouseReturnStillDown();
|
||
|
else
|
||
|
m_mouseOver->mouseOver();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (newFocusWidget && m_focusWidget != newFocusWidget) {
|
||
|
if (auto focusWidget = m_focusWidget)
|
||
|
focusWidget->blur();
|
||
|
m_focusWidget = newFocusWidget;
|
||
|
m_focusWidget->focus();
|
||
|
}
|
||
|
|
||
|
// Finally go through widgets in highest to lowest z-order and send the
|
||
|
// raw event, stopping further processing if the widget consumes it.
|
||
|
for (auto const& widget : reverseIterate(m_members)) {
|
||
|
if (widget->inMember(mousePos) && widget->active() && widget->interactive()) {
|
||
|
if (widget->sendEvent(event))
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (event.is<MouseButtonDownEvent>()) {
|
||
|
Vec2I mousePos = *m_context->mousePosition(event);
|
||
|
if (inDragArea(mousePos) && !m_lockPosition) {
|
||
|
setDragActive(true, mousePos);
|
||
|
return true;
|
||
|
}
|
||
|
if (inWindow(mousePos))
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (m_focusWidget) {
|
||
|
if (m_focusWidget->sendEvent(event))
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void Pane::setFocus(Widget const* focus) {
|
||
|
if (m_focusWidget.get() == focus)
|
||
|
return;
|
||
|
if (m_focusWidget)
|
||
|
m_focusWidget->blur();
|
||
|
if (auto c = childPtr(focus))
|
||
|
m_focusWidget = move(c);
|
||
|
else
|
||
|
throw GuiException("Cannot set focus on a widget which is not a child of this pane");
|
||
|
}
|
||
|
|
||
|
void Pane::removeFocus(Widget const* focus) {
|
||
|
if (m_focusWidget.get() == focus)
|
||
|
m_focusWidget.reset();
|
||
|
}
|
||
|
|
||
|
void Pane::removeFocus() {
|
||
|
m_focusWidget.reset();
|
||
|
}
|
||
|
|
||
|
Pane const* Pane::window() const {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
Pane* Pane::window() {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
void Pane::renderImpl() {
|
||
|
if (m_bgFooter != "")
|
||
|
m_context->drawInterfaceQuad(m_bgFooter, Vec2F(position()));
|
||
|
|
||
|
if (m_bgBody != "")
|
||
|
m_context->drawInterfaceQuad(m_bgBody, Vec2F(position()) + Vec2F(0, m_footerSize[1]));
|
||
|
|
||
|
if (m_bgHeader != "") {
|
||
|
auto headerPos = Vec2F(position()) + Vec2F(0, m_footerSize[1] + m_bodySize[1]);
|
||
|
m_context->drawInterfaceQuad(m_bgHeader, headerPos);
|
||
|
|
||
|
if (m_icon) {
|
||
|
m_icon->setPosition(Vec2I(0, m_footerSize[1] + m_bodySize[1]) + m_iconOffset);
|
||
|
m_icon->render(m_drawingArea);
|
||
|
m_context->resetInterfaceScissorRect();
|
||
|
}
|
||
|
|
||
|
m_context->setFontSize(m_fontSize);
|
||
|
m_context->setFontColor(m_titleColor.toRgba());
|
||
|
m_context->setFontMode(FontMode::Shadow);
|
||
|
m_context->renderInterfaceText(m_title, {headerPos + Vec2F(m_titleOffset)});
|
||
|
m_context->setFontColor(m_subTitleColor.toRgba());
|
||
|
m_context->renderInterfaceText(m_subTitle, {headerPos + Vec2F(m_subTitleOffset)});
|
||
|
m_context->setFontMode(FontMode::Normal);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Pane::update() {
|
||
|
if (m_visible) {
|
||
|
for (auto const& widget : m_members) {
|
||
|
widget->update();
|
||
|
if ((m_focusWidget == widget) != widget->hasFocus()) {
|
||
|
m_focusWidget.reset();
|
||
|
widget->blur();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Pane::tick() {}
|
||
|
|
||
|
bool Pane::dragActive() const {
|
||
|
return m_dragActive;
|
||
|
}
|
||
|
|
||
|
Vec2I Pane::dragMouseOrigin() const {
|
||
|
return m_dragMouseOrigin;
|
||
|
}
|
||
|
|
||
|
void Pane::setDragActive(bool dragActive, Vec2I dragMouseOrigin) {
|
||
|
m_dragActive = dragActive;
|
||
|
m_dragMouseOrigin = dragMouseOrigin;
|
||
|
}
|
||
|
|
||
|
void Pane::drag(Vec2I mousePosition) {
|
||
|
Vec2I delta = mousePosition - m_dragMouseOrigin;
|
||
|
setPosition(position() + delta);
|
||
|
m_dragMouseOrigin += delta;
|
||
|
}
|
||
|
|
||
|
bool Pane::inWindow(Vec2I const& position) const {
|
||
|
return screenBoundRect().contains(position);
|
||
|
}
|
||
|
|
||
|
bool Pane::inDragArea(Vec2I const& position) const {
|
||
|
return inWindow(position) && (position[1] < (this->position()[1] + m_footerSize[1])
|
||
|
|| position[1] > (this->position()[1] + (m_footerSize[1] + m_bodySize[1])));
|
||
|
}
|
||
|
|
||
|
Vec2I Pane::cursorRelativeToPane(Vec2I const& position) const {
|
||
|
return position - this->position();
|
||
|
}
|
||
|
|
||
|
Vec2I Pane::centerOffset() const {
|
||
|
return m_centerOffset;
|
||
|
}
|
||
|
|
||
|
void Pane::setBG(BGResult const& res) {
|
||
|
setBG(res.header, res.body, res.footer);
|
||
|
}
|
||
|
|
||
|
void Pane::setBG(String const& header, String const& body, String const& footer) {
|
||
|
m_bgHeader = header;
|
||
|
m_bgBody = body;
|
||
|
m_bgFooter = footer;
|
||
|
if (m_bgHeader != "") {
|
||
|
m_headerSize = Vec2I(m_context->textureSize(m_bgHeader));
|
||
|
} else {
|
||
|
m_headerSize = {};
|
||
|
}
|
||
|
if (m_bgBody != "") {
|
||
|
m_bodySize = Vec2I(m_context->textureSize(m_bgBody));
|
||
|
} else {
|
||
|
m_bodySize = {};
|
||
|
}
|
||
|
if (m_bgFooter != "") {
|
||
|
m_footerSize = Vec2I(m_context->textureSize(m_bgFooter));
|
||
|
} else {
|
||
|
m_footerSize = {};
|
||
|
}
|
||
|
|
||
|
setSize(Vec2I(std::max(std::max(m_headerSize[0], m_bodySize[0]), m_footerSize[0]),
|
||
|
m_headerSize[1] + m_bodySize[1] + m_footerSize[1]));
|
||
|
}
|
||
|
|
||
|
Pane::BGResult Pane::getBG() const {
|
||
|
return {m_bgHeader, m_bgBody, m_bgFooter};
|
||
|
}
|
||
|
|
||
|
void Pane::lockPosition() {
|
||
|
m_lockPosition = true;
|
||
|
}
|
||
|
|
||
|
void Pane::unlockPosition() {
|
||
|
m_lockPosition = false;
|
||
|
}
|
||
|
|
||
|
void Pane::setTitle(WidgetPtr icon, String const& title, String const& subTitle) {
|
||
|
m_icon = icon;
|
||
|
m_title = title;
|
||
|
m_subTitle = subTitle;
|
||
|
if (m_icon) {
|
||
|
m_icon->setParent(this);
|
||
|
m_icon->show();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Pane::setTitleString(String const& title, String const& subTitle) {
|
||
|
m_title = title;
|
||
|
m_subTitle = subTitle;
|
||
|
}
|
||
|
|
||
|
void Pane::setTitleIcon(WidgetPtr icon) {
|
||
|
m_icon = icon;
|
||
|
if (m_icon) {
|
||
|
m_icon->setParent(this);
|
||
|
m_icon->show();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
String Pane::title() const {
|
||
|
return m_title;
|
||
|
}
|
||
|
|
||
|
String Pane::subTitle() const {
|
||
|
return m_subTitle;
|
||
|
}
|
||
|
|
||
|
WidgetPtr Pane::titleIcon() const {
|
||
|
return m_icon;
|
||
|
}
|
||
|
|
||
|
PaneAnchor Pane::anchor() {
|
||
|
return m_anchor;
|
||
|
}
|
||
|
|
||
|
void Pane::setAnchor(PaneAnchor anchor) {
|
||
|
m_anchor = anchor;
|
||
|
}
|
||
|
|
||
|
Vec2I Pane::anchorOffset() const {
|
||
|
return m_anchorOffset;
|
||
|
}
|
||
|
|
||
|
void Pane::setAnchorOffset(Vec2I anchorOffset) {
|
||
|
m_anchorOffset = anchorOffset;
|
||
|
}
|
||
|
|
||
|
bool Pane::hasDisplayed() const {
|
||
|
return m_hasDisplayed;
|
||
|
}
|
||
|
|
||
|
PanePtr Pane::createTooltip(Vec2I const&) {
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
Maybe<String> Pane::cursorOverride(Vec2I const&) {
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
}
|