From 6352e8e3196f78388b6c771073f9e03eaa612673 Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Tue, 20 Jun 2023 14:33:09 +1000 Subject: everything everywhere all at once --- source/windowing/StarButtonWidget.cpp | 380 ++++++++++++++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 source/windowing/StarButtonWidget.cpp (limited to 'source/windowing/StarButtonWidget.cpp') diff --git a/source/windowing/StarButtonWidget.cpp b/source/windowing/StarButtonWidget.cpp new file mode 100644 index 0000000..88b3996 --- /dev/null +++ b/source/windowing/StarButtonWidget.cpp @@ -0,0 +1,380 @@ +#include "StarButtonWidget.hpp" +#include "StarRoot.hpp" +#include "StarJsonExtra.hpp" +#include "StarRandom.hpp" +#include "StarAssets.hpp" + +namespace Star { + +ButtonWidget::ButtonWidget() { + m_hovered = false; + m_pressed = false; + m_checkable = false; + m_checked = false; + m_disabled = false; + m_highlighted = false; + m_hasCheckedImages = false; + m_sustain = false; + m_invisible = false; + m_hTextAnchor = HorizontalAnchor::HMidAnchor; + m_fontColor = Color::White; + m_fontColorDisabled = Color::Gray; + + auto assets = Root::singleton().assets(); + + auto interfaceConfig = assets->json("/interface.config"); + m_pressedOffset = jsonToVec2I(interfaceConfig.get("buttonPressedOffset")); + m_fontSize = interfaceConfig.query("font.buttonSize").toInt(); +} + +ButtonWidget::ButtonWidget(WidgetCallbackFunc callback, + String const& baseImage, + String const& hoverImage, + String const& pressedImage, + String const& disabledImage) + : ButtonWidget() { + setCallback(callback); + setImages(baseImage, hoverImage, pressedImage, disabledImage); +} + +ButtonWidget::~ButtonWidget() { + if (m_buttonGroup) + m_buttonGroup->removeButton(this); +} + +void ButtonWidget::renderImpl() { + if (isPressed() && sustainCallbackOnDownHold()) + if (m_callback) + m_callback(this); + + Vec2F position = Vec2F(screenPosition()); + Vec2F textPosition = position + Vec2F(m_textOffset); + if (m_hTextAnchor == HorizontalAnchor::HMidAnchor) + textPosition += Vec2F(size()) / 2; + else if (m_hTextAnchor == HorizontalAnchor::RightAnchor) + textPosition += Vec2F(size()[0], size()[1] / 2); + else if (m_hTextAnchor == HorizontalAnchor::LeftAnchor) + textPosition += Vec2F(0, size()[1] / 2); + // We need to show the down button offset if we're pressing the button or + // don't have checked images and thus need some way to show that the button + // is checked (there's probably some better default behavior in that case) + if (m_pressed || (m_checked && !m_hasCheckedImages)) { + position += Vec2F(m_pressedOffset); + textPosition += Vec2F(m_pressedOffset); + } + + if (m_hasCheckedImages && m_checked) { + if (m_disabled) + drawButtonPart(m_disabledImageChecked, position); + else if ((m_pressed || m_highlighted) && !m_pressedImageChecked.empty()) + drawButtonPart(m_pressedImageChecked, position); + else if ((m_pressed || m_hovered || m_highlighted) && !m_hoverImageChecked.empty()) + drawButtonPart(m_hoverImageChecked, position); + else + drawButtonPart(m_baseImageChecked, position); + } else { + if (m_disabled) + drawButtonPart(m_disabledImage, position); + else if ((m_pressed || m_highlighted) && !m_pressedImage.empty()) + drawButtonPart(m_pressedImage, position); + else if ((m_pressed || m_hovered || m_highlighted) && !m_hoverImage.empty()) + drawButtonPart(m_hoverImage, position); + else if (!m_invisible) + drawButtonPart(m_baseImage, position); + } + + if (!m_overlayImage.empty()) + drawButtonPart(m_overlayImage, position); + + if (!m_text.empty()) { + auto& guiContext = GuiContext::singleton(); + guiContext.setFontSize(m_fontSize); + if (m_disabled) + guiContext.setFontColor(m_fontColorDisabled.toRgba()); + else if (m_fontColorChecked && m_checked) + guiContext.setFontColor(m_fontColorChecked.value().toRgba()); + else + guiContext.setFontColor(m_fontColor.toRgba()); + guiContext.setFontMode(FontMode::Shadow); + guiContext.renderInterfaceText(m_text, {textPosition, m_hTextAnchor, VerticalAnchor::VMidAnchor}); + guiContext.setFontMode(FontMode::Normal); + } +} + +bool ButtonWidget::sendEvent(InputEvent const& event) { + if (m_visible && !m_disabled) { + if (event.is() && event.get().mouseButton == MouseButton::Left) { + if (inMember(*context()->mousePosition(event))) { + if (!isPressed()) { + auto assets = Root::singleton().assets(); + auto sound = Random::randValueFrom(assets->json("/interface.config:buttonClickSound").toArray(), "").toString(); + if (!sound.empty()) + context()->playAudio(sound); + } + setPressed(true); + if (m_callback) { + focus(); + return true; + } + } else { + blur(); + return false; + } + } else if (event.is()) { + setPressed(false); + return false; + } + } + + return false; +} + +void ButtonWidget::mouseOver() { + Widget::mouseOver(); + if (!m_disabled) { + if (!m_hovered) { + auto assets = Root::singleton().assets(); + auto sound = Random::randValueFrom(assets->json("/interface.config:buttonHoverSound").toArray(), "").toString(); + if (!sound.empty()) + context()->playAudio(sound); + } + m_hovered = true; + } +} + +void ButtonWidget::mouseOut() { + Widget::mouseOut(); + m_hovered = false; + m_pressed = false; +} + +void ButtonWidget::mouseReturnStillDown() { + Widget::mouseReturnStillDown(); + m_hovered = true; + m_pressed = true; +} + +void ButtonWidget::hide() { + Widget::hide(); + m_pressed = false; + m_hovered = false; +} + +void ButtonWidget::setCallback(WidgetCallbackFunc callback) { + m_callback = callback; +} + +ButtonGroupPtr ButtonWidget::buttonGroup() const { + return m_buttonGroup; +} + +void ButtonWidget::setButtonGroup(ButtonGroupPtr newGroup, int id) { + if (m_buttonGroup != newGroup) { + if (m_buttonGroup) + m_buttonGroup->removeButton(this); + + m_buttonGroup = move(newGroup); + + if (m_buttonGroup) { + setCheckable(true); + m_buttonGroup->addButton(this, id); + } + } +} + +int ButtonWidget::buttonGroupId() { + if (m_buttonGroup) + return m_buttonGroup->id(this); + else + return ButtonGroup::NoButton; +} + +bool ButtonWidget::isHovered() const { + return m_hovered; +} + +bool ButtonWidget::isPressed() const { + return m_pressed; +} + +void ButtonWidget::setPressed(bool pressed) { + if (m_pressed != pressed) { + // Button action is triggered when the button is released after being pressed + if (m_pressed) { + check(); + if (m_callback) { + m_callback(this); + } + } + m_pressed = pressed; + } +} + +bool ButtonWidget::isCheckable() const { + return m_checkable; +} + +void ButtonWidget::setCheckable(bool checkable) { + m_checkable = checkable; +} + +bool ButtonWidget::isHighlighted() const { + return m_highlighted; +} + +void ButtonWidget::setHighlighted(bool highlighted) { + m_highlighted = highlighted; +} + +bool ButtonWidget::isChecked() const { + return m_checked; +} + +void ButtonWidget::setChecked(bool checked) { + // might cause button groups to have multiple selected against its rules, be careful with direct poking, use check() + // instead. + m_checked = checked; +} + +void ButtonWidget::check() { + if (m_checkable) { + // If we are part of an exclusive button group, then don't uncheck if + // we are already checked and pressed again. + if (m_buttonGroup) { + if (m_buttonGroup->toggle() || !isChecked()) { + setChecked(!m_buttonGroup->toggle() || !isChecked()); + m_buttonGroup->wasChecked(this); + } + } else { + setChecked(!isChecked()); + } + } +} + +bool ButtonWidget::sustainCallbackOnDownHold() { + return m_sustain; +} + +void ButtonWidget::setSustainCallbackOnDownHold(bool sustain) { + m_sustain = sustain; +} + +void ButtonWidget::setImages(String const& baseImage, String const& hoverImage, String const& pressedImage, String const& disabledImage) { + m_baseImage = baseImage; + m_hoverImage = hoverImage; + m_pressedImage = pressedImage; + m_disabledImage = disabledImage; + if (m_disabledImage.empty() && !m_baseImage.empty()) + m_disabledImage = m_baseImage + Root::singleton().assets()->json("/interface.config:disabledButton").toString(); + updateSize(); +} + +void ButtonWidget::setCheckedImages(String const& baseImage, String const& hoverImage, String const& pressedImage, String const& disabledImage) { + m_hasCheckedImages = !baseImage.empty(); + m_baseImageChecked = baseImage; + m_hoverImageChecked = hoverImage; + m_pressedImageChecked = pressedImage; + m_disabledImageChecked = disabledImage; + if (m_hasCheckedImages && m_disabledImageChecked.empty()) + m_disabledImageChecked = m_baseImageChecked + Root::singleton().assets()->json("/interface.config:disabledButton").toString(); + updateSize(); +} + +void ButtonWidget::setOverlayImage(String const& overlayImage) { + m_overlayImage = overlayImage; +} + +Vec2I const& ButtonWidget::pressedOffset() const { + return m_pressedOffset; +} + +void ButtonWidget::setPressedOffset(Vec2I const& offset) { + m_pressedOffset = offset; +} + +void ButtonWidget::setText(String const& text) { + m_text = text; +} + +void ButtonWidget::setFontSize(int size) { + m_fontSize = size; +} + +void ButtonWidget::setTextOffset(Vec2I textOffset) { + m_textOffset = textOffset; +} + +void ButtonWidget::setTextAlign(HorizontalAnchor hAnchor) { + m_hTextAnchor = hAnchor; +} + +void ButtonWidget::setFontColor(Color color) { + m_fontColor = color; +} + +void ButtonWidget::setFontColorDisabled(Color color) { + m_fontColorDisabled = color; +} + +void ButtonWidget::setFontColorChecked(Color color) { + m_fontColorChecked = color; +} + +// Although ButtonWidget wraps other widgets from time to time. These should never be "accessible" +WidgetPtr ButtonWidget::getChildAt(Vec2I const&) { + return {}; +} + +void ButtonWidget::disable() { + m_disabled = true; + m_pressed = false; +} + +void ButtonWidget::enable() { + m_disabled = false; +} + +void ButtonWidget::setEnabled(bool enabled) { + m_disabled = !enabled; + m_pressed &= enabled; // and off the pressed flag if the button is no longer enabled. +} + +void ButtonWidget::setInvisible(bool invisible) { + m_invisible = invisible; +} + +RectI ButtonWidget::getScissorRect() const { + if (m_pressed) { + return RectI::withSize(screenPosition(), size() + m_pressedOffset); + } + return RectI::withSize(screenPosition(), size()); +} + +void ButtonWidget::drawButtonPart(String const& image, Vec2F const& position) { + auto& guiContext = GuiContext::singleton(); + auto imageSize = guiContext.textureSize(image); + guiContext.drawInterfaceQuad(image, position + Vec2F(m_buttonBoundSize - imageSize) / 2); +} + +void ButtonWidget::updateSize() { + if (m_invisible || m_baseImage.empty()) + return; + auto& guiContext = GuiContext::singleton(); + m_buttonBoundSize = guiContext.textureSize(m_baseImage); + if (!m_hoverImage.empty()) + m_buttonBoundSize = m_buttonBoundSize.piecewiseMax(guiContext.textureSize(m_hoverImage)); + if (!m_pressedImage.empty()) + m_buttonBoundSize = m_buttonBoundSize.piecewiseMax(guiContext.textureSize(m_pressedImage)); + if (!m_baseImageChecked.empty()) + m_buttonBoundSize = m_buttonBoundSize.piecewiseMax(guiContext.textureSize(m_baseImageChecked)); + if (!m_hoverImageChecked.empty()) + m_buttonBoundSize = m_buttonBoundSize.piecewiseMax(guiContext.textureSize(m_hoverImageChecked)); + if (!m_pressedImageChecked.empty()) + m_buttonBoundSize = m_buttonBoundSize.piecewiseMax(guiContext.textureSize(m_pressedImageChecked)); + if (!m_disabledImageChecked.empty()) + m_buttonBoundSize = m_buttonBoundSize.piecewiseMax(guiContext.textureSize(m_disabledImageChecked)); + + setSize(Vec2I(m_buttonBoundSize)); +} + +} -- cgit v1.2.3