Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/windowing/StarGuiContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/windowing/StarGuiContext.cpp')
-rw-r--r--source/windowing/StarGuiContext.cpp427
1 files changed, 427 insertions, 0 deletions
diff --git a/source/windowing/StarGuiContext.cpp b/source/windowing/StarGuiContext.cpp
new file mode 100644
index 0000000..45d0806
--- /dev/null
+++ b/source/windowing/StarGuiContext.cpp
@@ -0,0 +1,427 @@
+#include "StarGuiContext.hpp"
+#include "StarRoot.hpp"
+#include "StarConfiguration.hpp"
+#include "StarMixer.hpp"
+#include "StarAssets.hpp"
+#include "StarImageMetadataDatabase.hpp"
+
+namespace Star {
+
+GuiContext* GuiContext::s_singleton;
+
+GuiContext* GuiContext::singletonPtr() {
+ return s_singleton;
+}
+
+GuiContext& GuiContext::singleton() {
+ if (!s_singleton)
+ throw GuiContextException("GuiContext::singleton() called with no GuiContext instance available");
+ else
+ return *s_singleton;
+}
+
+GuiContext::GuiContext(MixerPtr mixer, ApplicationControllerPtr appController) {
+ if (s_singleton)
+ throw GuiContextException("Singleton GuiContext has been constructed twice");
+
+ s_singleton = this;
+
+ m_mixer = move(mixer);
+ m_applicationController = move(appController);
+
+ m_interfaceScale = 1;
+
+ m_shiftHeld = false;
+
+ refreshKeybindings();
+}
+
+GuiContext::~GuiContext() {
+ s_singleton = nullptr;
+}
+
+void GuiContext::renderInit(RendererPtr renderer) {
+ m_renderer = move(renderer);
+ auto textureGroup = m_renderer->createTextureGroup();
+ m_textureCollection = make_shared<AssetTextureGroup>(textureGroup);
+ m_drawablePainter = make_shared<DrawablePainter>(m_renderer, m_textureCollection);
+ m_textPainter = make_shared<TextPainter>(Root::singleton().assets()->font("/hobo.ttf")->clone(), m_renderer, textureGroup);
+}
+
+MixerPtr const& GuiContext::mixer() const {
+ return m_mixer;
+}
+
+ApplicationControllerPtr const& GuiContext::applicationController() const {
+ return m_applicationController;
+}
+
+RendererPtr const& GuiContext::renderer() const {
+ if (!m_renderer)
+ throw GuiContextException("GuiContext::renderer() called before renderInit");
+ return m_renderer;
+}
+
+AssetTextureGroupPtr const& GuiContext::assetTextureGroup() const {
+ if (!m_textureCollection)
+ throw GuiContextException("GuiContext::assetTextureGroup() called before renderInit");
+ return m_textureCollection;
+}
+
+TextPainterPtr const& GuiContext::textPainter() const {
+ if (!m_textPainter)
+ throw GuiContextException("GuiContext::textPainter() called before renderInit");
+ return m_textPainter;
+}
+
+unsigned GuiContext::windowWidth() const {
+ return renderer()->screenSize()[0];
+}
+
+unsigned GuiContext::windowHeight() const {
+ return renderer()->screenSize()[1];
+}
+
+Vec2U GuiContext::windowSize() const {
+ return renderer()->screenSize();
+}
+
+Vec2U GuiContext::windowInterfaceSize() const {
+ return Vec2U::ceil(Vec2F(windowSize()) / interfaceScale());
+}
+
+int GuiContext::interfaceScale() const {
+ return m_interfaceScale;
+}
+
+void GuiContext::setInterfaceScale(int interfaceScale) {
+ m_interfaceScale = interfaceScale;
+}
+
+Maybe<Vec2I> GuiContext::mousePosition(InputEvent const& event) const {
+ auto getInterfacePosition = [this](Vec2I pos) {
+ return Vec2I(pos) / interfaceScale();
+ };
+
+ if (auto mouseMoveEvent = event.ptr<MouseMoveEvent>())
+ return getInterfacePosition(mouseMoveEvent->mousePosition);
+ else if (auto mouseDownEvent = event.ptr<MouseButtonDownEvent>())
+ return getInterfacePosition(mouseDownEvent->mousePosition);
+ else if (auto mouseUpEvent = event.ptr<MouseButtonUpEvent>())
+ return getInterfacePosition(mouseUpEvent->mousePosition);
+ else if (auto mouseWheelEvent = event.ptr<MouseWheelEvent>())
+ return getInterfacePosition(mouseWheelEvent->mousePosition);
+ else
+ return {};
+}
+
+Set<InterfaceAction> GuiContext::actions(InputEvent const& event) const {
+ return m_keyBindings.actions(event);
+}
+
+Set<InterfaceAction> GuiContext::actionsForKey(Key key) const {
+ return m_keyBindings.actionsForKey(key);
+}
+
+void GuiContext::refreshKeybindings() {
+ m_keyBindings = KeyBindings(Root::singleton().configuration()->get("bindings"));
+}
+
+void GuiContext::setInterfaceScissorRect(RectI const& scissor) {
+ renderer()->setScissorRect(RectI(scissor).scaled(interfaceScale()));
+}
+
+void GuiContext::resetInterfaceScissorRect() {
+ renderer()->setScissorRect({});
+}
+
+Vec2U GuiContext::textureSize(String const& texName) {
+ return assetTextureGroup()->loadTexture(texName)->size();
+}
+
+void GuiContext::drawQuad(RectF const& screenCoords, Vec4B const& color) {
+ renderer()->render(renderFlatRect(screenCoords, color, 0.0f));
+}
+
+void GuiContext::drawQuad(String const& texName, RectF const& screenCoords, Vec4B const& color) {
+ renderer()->render(renderTexturedRect(assetTextureGroup()->loadTexture(texName), screenCoords, color, 0.0f));
+}
+
+void GuiContext::drawQuad(String const& texName, Vec2F const& screenPos, int pixelRatio, Vec4B const& color) {
+ renderer()->render(renderTexturedRect(assetTextureGroup()->loadTexture(texName), screenPos, pixelRatio, color, 0.0f));
+}
+
+void GuiContext::drawQuad(String const& texName, RectF const& texCoords, RectF const& screenCoords, Vec4B const& color) {
+ renderer()->render(RenderQuad{assetTextureGroup()->loadTexture(texName),
+ RenderVertex{Vec2F(screenCoords.xMin(), screenCoords.yMin()), Vec2F(texCoords.xMin(), texCoords.yMin()), color, 0.0f},
+ RenderVertex{Vec2F(screenCoords.xMax(), screenCoords.yMin()), Vec2F(texCoords.xMax(), texCoords.yMin()), color, 0.0f},
+ RenderVertex{Vec2F(screenCoords.xMax(), screenCoords.yMax()), Vec2F(texCoords.xMax(), texCoords.yMax()), color, 0.0f},
+ RenderVertex{Vec2F(screenCoords.xMin(), screenCoords.yMax()), Vec2F(texCoords.xMin(), texCoords.yMax()), color, 0.0f}});
+}
+
+void GuiContext::drawDrawable(Drawable drawable, Vec2F const& screenPos, int pixelRatio, Vec4B const& color) {
+ if (drawable.isLine())
+ drawable.linePart().width *= pixelRatio;
+
+ drawable.scale(pixelRatio);
+ drawable.translate(screenPos);
+ drawable.color *= Color::rgba(color);
+ m_drawablePainter->drawDrawable(drawable);
+}
+
+void GuiContext::drawLine(Vec2F const& begin, Vec2F const end, Vec4B const& color, float lineWidth) {
+ Vec2F left = vnorm(Vec2F(end) - Vec2F(begin)).rot90() * lineWidth / 2.0f;
+ renderer()->render(RenderQuad{{},
+ RenderVertex{Vec2F(begin) + left, Vec2F(), color, 0.0f},
+ RenderVertex{Vec2F(begin) - left, Vec2F(), color, 0.0f},
+ RenderVertex{Vec2F(end) - left, Vec2F(), color, 0.0f},
+ RenderVertex{Vec2F(end) + left, Vec2F(), color, 0.0f}});
+}
+
+void GuiContext::drawPolyLines(PolyF const& poly, Vec4B const& color, float lineWidth) {
+ for (size_t i = 0; i < poly.sides(); ++i)
+ drawLine(poly.vertex(i), poly.vertex(i + 1), color, lineWidth);
+}
+
+void GuiContext::drawTriangles(List<tuple<Vec2F, Vec2F, Vec2F>> const& triangles, Vec4B const& color) {
+ for (auto poly : triangles) {
+ renderer()->render(RenderTriangle{{},
+ RenderVertex{get<0>(poly), Vec2F(), color, 0.0f},
+ RenderVertex{get<1>(poly), Vec2F(), color, 0.0f},
+ RenderVertex{get<2>(poly), Vec2F(), color, 0.0f}});
+ }
+}
+
+void GuiContext::drawInterfaceDrawable(Drawable drawable, Vec2F const& screenPos, Vec4B const& color) {
+ drawDrawable(move(drawable), screenPos * interfaceScale(), (float)interfaceScale(), color);
+}
+
+void GuiContext::drawInterfaceLine(Vec2F const& begin, Vec2F const end, Vec4B const& color, float lineWidth) {
+ drawLine(begin * interfaceScale(), end * interfaceScale(), color, lineWidth * interfaceScale());
+}
+
+void GuiContext::drawInterfacePolyLines(PolyF poly, Vec4B const& color, float lineWidth) {
+ poly.scale(interfaceScale());
+ drawPolyLines(poly, color, lineWidth * interfaceScale());
+}
+
+void GuiContext::drawInterfaceQuad(RectF const& screenCoords, Vec4B const& color) {
+ drawQuad(screenCoords.scaled(interfaceScale()), color);
+}
+
+void GuiContext::drawInterfaceQuad(String const& texName, Vec2F const& screenCoords, Vec4B const& color) {
+ drawQuad(texName, screenCoords * interfaceScale(), interfaceScale(), color);
+}
+
+void GuiContext::drawInterfaceQuad(String const& texName, Vec2F const& screenCoords, float scale, Vec4B const& color) {
+ drawQuad(texName, screenCoords * interfaceScale(), interfaceScale() * scale, color);
+}
+
+void GuiContext::drawInterfaceQuad(String const& texName, RectF const& texCoords, RectF const& screenCoords, Vec4B const& color) {
+ drawQuad(texName, texCoords, screenCoords.scaled(interfaceScale()), color);
+}
+
+void GuiContext::drawInterfaceTriangles(List<tuple<Vec2F, Vec2F, Vec2F>> const& triangles, Vec4B const& color) {
+ drawTriangles(triangles.transformed([this](tuple<Vec2F, Vec2F, Vec2F> const& poly) {
+ return tuple<Vec2F, Vec2F, Vec2F>(get<0>(poly) * interfaceScale(), get<1>(poly) * interfaceScale(), get<2>(poly) * interfaceScale());
+ }), color);
+}
+
+void GuiContext::drawImageStretchSet(ImageStretchSet const& imageSet, RectF const& screenPos, GuiDirection direction, Vec4B const& color) {
+ int innerSize;
+ int innerOffset = 0;
+ RectF begin, end, inner;
+ if (direction == GuiDirection::Horizontal) {
+ innerSize = screenPos.width();
+ if (imageSet.begin.size())
+ innerOffset = textureSize(imageSet.begin)[0];
+
+ if (imageSet.end.size())
+ innerSize = std::max(0, innerSize - innerOffset - Vec2I(textureSize(imageSet.end))[0]);
+ else
+ innerSize = std::max(0, innerSize - innerOffset);
+
+ if (imageSet.begin.size())
+ begin = RectF::withSize(screenPos.min(), Vec2F(textureSize(imageSet.begin)[0], screenPos.height()));
+ else
+ begin = RectF::withSize(screenPos.min(), Vec2F(0, screenPos.height()));
+
+ inner = RectF::withSize(screenPos.min() + Vec2F(innerOffset, 0), Vec2F(innerSize, screenPos.height()));
+ if (imageSet.end.size())
+ end = RectF::withSize(screenPos.min() + Vec2F(innerOffset + innerSize, 0), Vec2F(textureSize(imageSet.end)[0], screenPos.height()));
+ else
+ end = RectF::withSize(screenPos.min(), Vec2F(0, screenPos.height()));
+
+ } else {
+ innerSize = screenPos.height();
+ if (imageSet.begin.size())
+ innerOffset = textureSize(imageSet.begin)[1];
+
+ if (imageSet.end.size())
+ innerSize = std::max(0, innerSize - innerOffset - Vec2I(textureSize(imageSet.end))[1]);
+ else
+ innerSize = std::max(0, innerSize - innerOffset);
+
+ if (imageSet.begin.size())
+ begin = RectF::withSize(screenPos.min(), Vec2F(screenPos.width(), textureSize(imageSet.begin)[1]));
+ else
+ begin = RectF::withSize(screenPos.min(), Vec2F(screenPos.width(), 0));
+
+ inner = RectF::withSize(screenPos.min() + Vec2F(0, innerOffset), Vec2F(screenPos.width(), innerSize));
+ if (imageSet.end.size()) {
+ end = RectF::withSize(screenPos.min() + Vec2F(0, innerOffset + innerSize), Vec2F(screenPos.width(), textureSize(imageSet.end)[1]));
+ } else {
+ end = RectF::withSize(screenPos.min(), Vec2F(screenPos.width(), 0));
+ }
+ }
+
+ if (imageSet.begin.size())
+ drawInterfaceQuad(imageSet.begin, RectF(Vec2F(), Vec2F(textureSize(imageSet.begin))), begin, color);
+
+ if (imageSet.type == ImageStretchSet::ImageStretchType::Stretch) {
+ drawInterfaceQuad(imageSet.inner, RectF(Vec2F(), Vec2F(textureSize(imageSet.inner))), inner, color);
+
+ } else {
+ int position = 0;
+ auto texSize = Vec2F(textureSize(imageSet.inner));
+ if (direction == GuiDirection::Horizontal) {
+ starAssert(texSize[0] > 0);
+ while (position < inner.width()) {
+ RectF partialImage = RectF::withSize(Vec2F(), Vec2F(std::min(inner.width() - position, texSize[0]), texSize[1]));
+ drawInterfaceQuad(imageSet.inner, partialImage, RectF::withSize(inner.min() + Vec2F(position, 0), partialImage.size()), color);
+ position += partialImage.size()[0];
+ }
+ } else {
+ starAssert(texSize[1] > 0);
+ while (position < inner.height()) {
+ RectF partialImage = RectF::withSize(
+ Vec2F(0, max(0.0f, texSize[1] - (inner.height() - position))),
+ Vec2F(texSize[0], std::min(inner.height() - position, texSize[1])));
+ drawInterfaceQuad(imageSet.inner, partialImage, RectF::withSize(inner.min() + Vec2F(0, position), partialImage.size()), color);
+ position += partialImage.size()[1];
+ }
+ }
+ }
+
+ if (imageSet.end.size())
+ drawInterfaceQuad(imageSet.end, RectF(Vec2F(), Vec2F(textureSize(imageSet.end))), end, color);
+}
+
+RectF GuiContext::renderText(String const& s, TextPositioning const& position) {
+ return textPainter()->renderText(s, position);
+}
+
+RectF GuiContext::renderInterfaceText(String const& s, TextPositioning const& position) {
+ auto res = renderText(s, {
+ position.pos * interfaceScale(),
+ position.hAnchor,
+ position.vAnchor,
+ position.wrapWidth.apply(bind(std::multiplies<int>(), _1, interfaceScale())),
+ position.charLimit
+ });
+ return RectF(res).scaled(1.0f / interfaceScale());
+}
+
+RectF GuiContext::determineTextSize(String const& s, TextPositioning const& positioning) {
+ return textPainter()->determineTextSize(s, positioning);
+}
+
+RectF GuiContext::determineInterfaceTextSize(String const& s, TextPositioning const& positioning) {
+ auto res = determineTextSize(s, {
+ positioning.pos * interfaceScale(),
+ positioning.hAnchor,
+ positioning.vAnchor,
+ positioning.wrapWidth.apply(bind(std::multiplies<int>(), _1, interfaceScale()))
+ });
+ return RectF(res).scaled(1.0f / interfaceScale());
+}
+
+void GuiContext::setFontSize(unsigned size) {
+ setFontSize(size, interfaceScale());
+}
+
+void GuiContext::setFontSize(unsigned size, int pixelRatio) {
+ textPainter()->setFontSize(size * pixelRatio);
+}
+
+void GuiContext::setFontColor(Vec4B const& color) {
+ textPainter()->setFontColor(color);
+}
+
+void GuiContext::setFontMode(FontMode mode) {
+ textPainter()->setMode(mode);
+}
+
+void GuiContext::setFontProcessingDirectives(String const& directives) {
+ textPainter()->setProcessingDirectives(directives);
+}
+
+void GuiContext::setLineSpacing(float lineSpacing) {
+ textPainter()->setLineSpacing(lineSpacing);
+}
+
+void GuiContext::setDefaultLineSpacing() {
+ textPainter()->setLineSpacing(DefaultLineSpacing);
+}
+
+int GuiContext::stringWidth(String const& s) {
+ return textPainter()->stringWidth(s);
+}
+
+int GuiContext::stringInterfaceWidth(String const& s) {
+ if (interfaceScale()) {
+ // font size is already adjusted UP by interfaceScale, so we have to adjust
+ // it back down
+ return stringWidth(s) / interfaceScale();
+ }
+ return 0;
+}
+
+StringList GuiContext::wrapText(String const& s, Maybe<unsigned> wrapWidth) {
+ return textPainter()->wrapText(s, wrapWidth);
+}
+
+StringList GuiContext::wrapInterfaceText(String const& s, Maybe<unsigned> wrapWidth) {
+ if (wrapWidth)
+ *wrapWidth *= interfaceScale();
+ return wrapText(s, wrapWidth);
+}
+
+bool GuiContext::shiftHeld() const {
+ return m_shiftHeld;
+}
+
+void GuiContext::setShiftHeld(bool held) {
+ m_shiftHeld = held;
+}
+
+void GuiContext::playAudio(AudioInstancePtr audioInstance) {
+ m_mixer->play(audioInstance);
+}
+
+void GuiContext::playAudio(String const& audioAsset, int loops, float volume) {
+ auto assets = Root::singleton().assets();
+ auto config = Root::singleton().configuration();
+ auto audioInstance = make_shared<AudioInstance>(*assets->audio(audioAsset));
+ audioInstance->setVolume(volume);
+ audioInstance->setLoops(loops);
+ m_mixer->play(move(audioInstance));
+}
+
+String GuiContext::getClipboard() const {
+ return m_applicationController->getClipboard().value();
+}
+
+void GuiContext::setClipboard(String text) {
+ m_applicationController->setClipboard(move(text));
+}
+
+void GuiContext::cleanup() {
+ int64_t textureTimeout = Root::singleton().assets()->json("/rendering.config:textureTimeout").toInt();
+ if (m_textureCollection)
+ m_textureCollection->cleanup(textureTimeout);
+ if (m_textPainter)
+ m_textPainter->cleanup(textureTimeout);
+}
+
+}