From afd7dc29ad5b5422a873170c2323fe953fd89fab Mon Sep 17 00:00:00 2001 From: pointer-to-bios Date: Sat, 3 Aug 2024 04:11:49 +0800 Subject: [PATCH] Created Container and Window. --- .gitignore | 3 + CMakeLists.txt | 24 ++ README.md | 3 + demo/demo1.cpp | 9 + include/containers/container.hpp | 123 ++++++++ include/event.hpp | 81 +++++ include/exceptions.hpp | 63 ++++ include/gtypes.hpp | 24 ++ include/sftk.hpp | 10 + include/window.hpp | 59 ++++ src/containers/container.cpp | 524 +++++++++++++++++++++++++++++++ src/window.cpp | 86 +++++ 12 files changed, 1009 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 demo/demo1.cpp create mode 100644 include/containers/container.hpp create mode 100644 include/event.hpp create mode 100644 include/exceptions.hpp create mode 100644 include/gtypes.hpp create mode 100644 include/sftk.hpp create mode 100644 include/window.hpp create mode 100644 src/containers/container.cpp create mode 100644 src/window.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b304cd --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/build + +/.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a86ae65 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.16) +project(SFTK LANGUAGES CXX) + +set(CXX_STANDARD_REQUIRED 23) + +include(FetchContent) +FetchContent_Declare(SFML + GIT_REPOSITORY https://github.com/SFML/SFML.git + GIT_TAG 2.6.x) +FetchContent_MakeAvailable(SFML) + +set(ROOT src/) +set(CONTAINER src/containers) + +set(SOURCES ${SOURCES} ${ROOT}/window.cpp) +set(SOURCES ${SOURCES} ${CONTAINER}/container.cpp) + +add_library(sftk SHARED ${SOURCES}) +target_link_libraries(sftk PRIVATE sfml-graphics sfml-audio sfml-network) +target_include_directories(sftk PUBLIC include/) + +add_executable(demo1 demo/demo1.cpp) +target_link_libraries(demo1 PRIVATE sftk sfml-graphics sfml-audio sfml-network) +target_include_directories(demo1 PRIVATE include/) diff --git a/README.md b/README.md new file mode 100644 index 0000000..cbb6d52 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# SFTK - SFml ToolKits + +基于**sfml**的UI工具库。 diff --git a/demo/demo1.cpp b/demo/demo1.cpp new file mode 100644 index 0000000..e3822e9 --- /dev/null +++ b/demo/demo1.cpp @@ -0,0 +1,9 @@ +#include + +int main() +{ + auto &window = sftk::Window::create(800, 600, L"demo1"); + auto container = sftk::Container::create(vec(100.f, 100.f), vec(400.f, 400.f), window.getRoot()); + container->setBackgroudColor(sf::Color::White); + window.exec(); +} diff --git a/include/containers/container.hpp b/include/containers/container.hpp new file mode 100644 index 0000000..b9bf23e --- /dev/null +++ b/include/containers/container.hpp @@ -0,0 +1,123 @@ +#ifndef CONTAINER_HPP +#define CONTAINER_HPP + +#include +#include +#include + +#include +#include + +#include + +namespace sftk +{ + class Container; + + typedef std::shared_ptr pContainer; + bool operator==(pContainer &shp, Container *rawp); + + class Container + { + public: // Public base functions + static pContainer create(pContainer parent = nullptr); + static pContainer create(sf::Vector2f pos, sf::Vector2f size, pContainer parent = nullptr); + + void setParent(pContainer parent); + void setPosition(sf::Vector2f pos = sf::Vector2f(0, 0)); + void setSize(sf::Vector2f pos); + void setBackgroudColor(sf::Color color); + + pContainer getParent() const; + const sf::Vector2f &getPosition() const; + const sf::Vector2f &getSize() const; + sf::FloatRect getGlobalBouds() const; + + virtual bool isRoot() const; + virtual bool hasInternalChange() const; + + virtual void update() const; + + public: // Event hadler register functions + template + void registerEvent(EventType type, std::function handler); + + public: // Static and operators + static pContainer getRoot(Container *p = nullptr); + + friend bool sftk::operator==(pContainer &shp, Container *rawp); + + protected: // Must be overrided by child classes + Container(pContainer parent = nullptr); + Container(sf::Vector2f pos, sf::Vector2f size, pContainer parent = nullptr); + Container(sf::Vector2f pos, sf::Vector2f size, bool root); + + virtual sf::RenderWindow &windowSFWindow(); + + // The events bases on sfml's Event, + // so the Window class must use this function to + // pass event to the root container + // (itself's parent class)'s dispatchEvent(Event&) + // to process it as Event. + void dispatchEvent(sf::Event &event); + + private: // For this class to use + sf::RenderWindow &getSfWindow(); + // When this container did deal with the passed event + // it returns true. On the oppsite case it returns false. + bool dispatchEvent(Event &event); + + pContainer parent; + std::vector childs; + sf::Vector2f basepos; + sf::Vector2f pos, size; + + sf::View view; + + sf::Color backgroudColor; + + static const int64_t maxClickTime = 300; + + private: // Called on construction + static void rootTest(Container *self); + inline static int64_t currentTime(); + + private: // Event handlers + std::function closed; + std::function resized; + std::function mousePressed; + std::function mouseReleased; + std::function mouseClicked; + std::function mouseEntered; + std::function mouseLeft; + std::function mouseMoved; + std::function mouseDragged; + std::function mouseScrolled; + std::function textInputed; + + private: // Second events asistant vars + int64_t lastMousePressTime[sf::Mouse::Button::ButtonCount]; + sf::Vector2f lastMousePressPos[sf::Mouse::Button::ButtonCount]; + + bool focused; + bool mouseLocated; + + public: // Exceptions + class RootContainerNotPresent : public SFTKException + { + public: + RootContainerNotPresent(Container *container); + const char *what() const noexcept override; + }; + + class NotAWindowObj : public SFTKException + { + public: + NotAWindowObj(Container *container); + const char *what() const noexcept override; + }; + }; + +}; + +#endif diff --git a/include/event.hpp b/include/event.hpp new file mode 100644 index 0000000..342d459 --- /dev/null +++ b/include/event.hpp @@ -0,0 +1,81 @@ +#ifndef EVENT_HPP +#define EVENT_HPP + +#include + +namespace sftk +{ + enum EventType + { + Closed, + Resized, + MousePressed, + MouseReleased, + MouseClicked, + MouseEntered, + MouseLeft, + MouseMoved, + MouseDragged, + MouseScrolled, + TextInputed, + + EventTypeLength, + }; + + struct _Resized + { + sf::Vector2f size; // The parent Container's new size. + }; + + struct _MouseEnteredLeftMoved + { + sf::Vector2f pos; + }; + + typedef _MouseEnteredLeftMoved MousePosition; + + struct _MousePressedReleasedClicked + { + sf::Vector2f pos; + sf::Mouse::Button button; + }; + + struct _MouseDragged + { + sf::Vector2f start; + sf::Vector2f now; + sf::Mouse::Button button; + }; + + struct _MouseScrolled + { + float delta; + }; + + struct _TextInputed + { + wchar_t uchar; + }; + + union EventData + { + _Resized resized; + _MousePressedReleasedClicked mousePressed; + _MousePressedReleasedClicked mouseReleased; + _MousePressedReleasedClicked mouseClicked; + _MouseEnteredLeftMoved mouseEntered; + _MouseEnteredLeftMoved mouseLeft; + _MouseEnteredLeftMoved mouseMoved; + _MouseDragged mouseDragged; + _MouseScrolled mouseScrolled; + _TextInputed textInputed; + }; + + struct Event + { + EventType type; + EventData data; + }; +}; + +#endif diff --git a/include/exceptions.hpp b/include/exceptions.hpp new file mode 100644 index 0000000..0b11618 --- /dev/null +++ b/include/exceptions.hpp @@ -0,0 +1,63 @@ +#ifndef EXCEPTIONS_HPP +#define EXCEPTIONS_HPP + +#include +#include +#include + +namespace sftk +{ + /** + * @brief # SFTK exceptions super class + * + * @tparam T - The SFTK object types + */ + template + class SFTKException : public std::exception + { + public: + SFTKException(T *obj) : sftkObj(obj) {} + /** + * @brief # Address formatter + * + * This formatter function use 16 `*`s as address placeholder. + * + * When throwing an exception, Exceptions can use placeholder with this function + * to fill the `what()` message. So the reported exception can tell where the crashed + * SFTK object at. + * + * # Example + * + * ```c + * const char *SomeException::what() const noexcept + * { + * static char msg[] = "sftk::SomeClass 0x********_******** reported an exception."; + * SFTKException::formatAddress(msg, sizeof(msg)); + * return msg; + * } + * ``` + * + * @param msg exception message + * @param len message length + */ + void formatAddress(char *msg, int len) const + { + std::stringstream ss; + ss << std::hex << std::setw(16) << std::setfill('0') << reinterpret_cast(sftkObj); + auto it = ss.str().begin(); + for (int i = 0; i < len; i++) + { + if (msg[i] == '*') + { + msg[i] = *it; + ++it; + } + } + } + + private: + T *sftkObj; + }; +}; + +#endif diff --git a/include/gtypes.hpp b/include/gtypes.hpp new file mode 100644 index 0000000..023e529 --- /dev/null +++ b/include/gtypes.hpp @@ -0,0 +1,24 @@ +#ifndef GTYPES_HPP +#define GTYPES_HPP + +#include + +template +constexpr sf::Vector2 vec(T x, T y) +{ + return sf::Vector2(x, y); +} + +template +constexpr sf::Vector3 vec(T x, T y, T z) +{ + return sf::Vector3(x, y, z); +} + +template +constexpr sf::Rect vec(T x, T y, T width, T height) +{ + return sf::Rect(x, y, width, height); +} + +#endif diff --git a/include/sftk.hpp b/include/sftk.hpp new file mode 100644 index 0000000..9ec0109 --- /dev/null +++ b/include/sftk.hpp @@ -0,0 +1,10 @@ +#ifndef SFTK_HPP +#define SFTK_HPP + +#include + +#include + +#include + +#endif diff --git a/include/window.hpp b/include/window.hpp new file mode 100644 index 0000000..c012fd8 --- /dev/null +++ b/include/window.hpp @@ -0,0 +1,59 @@ +#ifndef WINDOW_HPP +#define WINDOW_HPP + +#include +#include +#include + +#include +#include +#include + +namespace sftk +{ + class Window; + + typedef std::shared_ptr pWindow; + + /** + * @brief Window type + * + * To create a unique SFML window in the current thread. + */ + class Window : public Container + { + public: + static Window &create(int width, int height, std::wstring name = L""); + + void exec(); + + void setSize(sf::Vector2u size); + + bool isRoot() const; + + sf::Vector2f &getPosition() const; + + protected: + Window(int width, int height, std::wstring name = L""); + + private: + sf::RenderWindow &windowSFWindow(); + + sf::RenderWindow sfwindow; + + std::unique_ptr running; + + private: + std::unique_ptr> v2f_dumpst; + + public: + class WindowMultipleConstructed : public SFTKException + { + public: + WindowMultipleConstructed(Window *); + const char *what() const noexcept override; + }; + }; + +}; +#endif diff --git a/src/containers/container.cpp b/src/containers/container.cpp new file mode 100644 index 0000000..31ddfe4 --- /dev/null +++ b/src/containers/container.cpp @@ -0,0 +1,524 @@ +#include + +#include +#include + +#include + +using namespace sftk; + +pContainer Container::getRoot(Container *p) +{ + static thread_local pContainer root; + if (p) + root.reset(p); + return root; +} + +void Container::rootTest(Container *self) +{ + if (!getRoot().get()) + throw RootContainerNotPresent(self); +} + +Container::Container(pContainer parent) : parent(nullptr) +{ + if (!parent.get()) + { + Container::rootTest(this); + this->parent = getRoot(); + } + else + { + this->parent = parent; + } + this->parent->childs.push_back(pContainer(this)); +} + +Container::Container(sf::Vector2f pos, sf::Vector2f size, pContainer parent) + : parent(nullptr), + pos(pos), + size(size), + view(vec(0.f, 0.f, size.x, size.y)), + basepos(parent->basepos + parent->pos), + backgroudColor(sf::Color::Black) +{ + if (!parent.get()) + { + Container::rootTest(this); + this->parent = getRoot(); + } + else + { + this->parent = parent; + } + this->parent->childs.push_back(pContainer(this)); + auto rootsize = getRoot()->getSize(); + view.setViewport(vec( + (parent->basepos.x + pos.x) / rootsize.x, (parent->basepos.y + pos.y) / rootsize.y, + size.x / rootsize.x, size.y / rootsize.y)); +} + +Container::Container(sf::Vector2f pos, sf::Vector2f size, bool root) + : parent(nullptr), + pos(pos), + size(size), + view(vec(0.f, 0.f, size.x, size.y)), + basepos(vec(0.f, 0.f)), + backgroudColor(sf::Color::Black) +{ + view.setViewport(vec(0.f, 0.f, 1.f, 1.f)); +} + +pContainer Container::create(pContainer parent) +{ + return pContainer(new Container(parent)); +} + +pContainer Container::create(sf::Vector2f pos, sf::Vector2f size, pContainer parent) +{ + return pContainer(new Container(pos, size, parent)); +} + +void Container::setParent(pContainer parent) +{ + auto it = std::find(this->parent->childs.begin(), this->parent->childs.end(), this); + this->parent->childs.erase(it); + this->parent = parent; + this->parent->childs.push_back(pContainer(this)); +} + +void Container::setPosition(sf::Vector2f pos) +{ + this->pos = pos; + auto rootsize = getRoot()->getSize(); + view.setViewport(vec( + (parent->pos.x + pos.x) / rootsize.x, (parent->pos.y + pos.y) / rootsize.y, + size.x / rootsize.x, size.y / rootsize.y)); +} + +void Container::setSize(sf::Vector2f size) +{ + this->size = size; + auto rootsize = getRoot()->getSize(); + view.setViewport(vec( + (basepos.x + pos.x) / rootsize.x, (basepos.y + pos.y) / rootsize.y, + size.x / rootsize.x, size.y / rootsize.y)); +} + +void Container::setBackgroudColor(sf::Color color) +{ + backgroudColor = color; +} + +pContainer Container::getParent() const +{ + return parent; +} + +const sf::Vector2f &Container::getPosition() const +{ + return pos; +} + +const sf::Vector2f &Container::getSize() const +{ + return size; +} + +sf::FloatRect Container::getGlobalBouds() const +{ + return vec(basepos.x + pos.x, basepos.y + pos.y, size.x, size.y); +} + +bool Container::isRoot() const +{ + return false; +} + +bool Container::hasInternalChange() const +{ + return false; +} + +void Container::update() const +{ + auto &window = getRoot()->windowSFWindow(); + window.setView(view); + sf::RectangleShape bg(vec(size.x, size.y)); + bg.setPosition(vec(0.f, 0.f)); + bg.setFillColor(backgroudColor); + window.draw(bg); + for (auto c : childs) + { + c->update(); + } +} + +template <> +void Container::registerEvent(EventType type, std::function handler) +{ + if (type != EventType::Closed) + return; + closed = handler; +} + +template <> +void Container::registerEvent(EventType type, std::function handler) +{ + switch (type) + { + case EventType::Resized: + resized = handler; + break; + case EventType::MouseEntered: + mouseEntered = handler; + break; + case EventType::MouseLeft: + mouseLeft = handler; + break; + case EventType::MouseMoved: + mouseMoved = handler; + break; + } +} + +template <> +void Container::registerEvent(EventType type, std::function handler) +{ + switch (type) + { + case EventType::MousePressed: + mousePressed = handler; + break; + case EventType::MouseReleased: + mouseReleased = handler; + break; + case EventType::MouseClicked: + mouseClicked = handler; + break; + } +} + +template <> +void Container::registerEvent(EventType type, std::function handler) +{ + if (type != EventType::MouseDragged) + return; + mouseDragged = handler; +} + +template <> +void Container::registerEvent(EventType type, std::function handler) +{ + if (type != EventType::MouseScrolled) + return; + mouseScrolled = handler; +} + +template <> +void Container::registerEvent(EventType type, std::function handler) +{ + if (type != EventType::TextInputed) + return; + textInputed = handler; +} + +void Container::dispatchEvent(sf::Event &event) +{ + Event eve{}; + switch (event.type) + { + case sf::Event::Closed: + eve = Event{.type = EventType::Closed}; + break; + case sf::Event::Resized: + eve = Event{ + .type = EventType::Resized, + .data = EventData{ + .resized = _Resized{ + .size = vec((float)event.size.width, (float)event.size.height)}}}; + break; + case sf::Event::MouseEntered: + eve = Event{ + .type = EventType::MouseEntered, + .data = EventData{ + .mouseEntered = _MouseEnteredLeftMoved{ + .pos = vec((float)event.mouseMove.x, (float)event.mouseMove.y)}}}; + break; + case sf::Event::MouseLeft: + eve = Event{ + .type = EventType::MouseLeft, + .data = EventData{ + .mouseLeft = _MouseEnteredLeftMoved{ + .pos = vec((float)event.mouseMove.x, (float)event.mouseMove.y)}}}; + break; + case sf::Event::MouseButtonPressed: + eve = Event{ + .type = EventType::MousePressed, + .data = EventData{ + .mousePressed = _MousePressedReleasedClicked{ + .pos = vec((float)event.mouseButton.x, (float)event.mouseButton.y), + .button = event.mouseButton.button}}}; + break; + case sf::Event::MouseButtonReleased: + eve = Event{ + .type = EventType::MouseReleased, + .data = EventData{ + .mouseReleased = _MousePressedReleasedClicked{ + .pos = vec((float)event.mouseButton.x, (float)event.mouseButton.y), + .button = event.mouseButton.button}}}; + break; + case sf::Event::MouseMoved: + eve = Event{ + .type = EventType::MouseMoved, + .data = EventData{ + .mouseMoved = MousePosition{ + .pos = vec((float)event.mouseMove.x, (float)event.mouseMove.y)}}}; + break; + case sf::Event::MouseWheelScrolled: + if (event.mouseWheelScroll.wheel != sf::Mouse::VerticalWheel) + return; + eve = Event{ + .type = EventType::MouseScrolled, + .data = EventData{ + .mouseScrolled = _MouseScrolled{ + .delta = event.mouseWheelScroll.delta, + }}}; + break; + case sf::Event::TextEntered: + eve = Event{ + .type = EventType::TextInputed, + .data = EventData{ + .textInputed = _TextInputed{ + .uchar = static_cast(event.text.unicode)}}}; + break; + default: + return; + } + dispatchEvent(eve); +} + +bool Container::dispatchEvent(Event &event) +{ + bool spread = true; // If this event will pass to children containers. + bool broadcast = false; // If this event will send to all children containers. + bool handler_effective = false; // The return value, if this container processed this event. + + _Resized rsz; + _MousePressedReleasedClicked mpr; + _MousePressedReleasedClicked mrel; + _MousePressedReleasedClicked mcli; + _MouseEnteredLeftMoved mmov; + _MouseDragged mdra; + + switch (event.type) + { + case EventType::Closed: + broadcast = true; + if (closed) + { + handler_effective = true; + closed(); + } + break; + case EventType::Resized: + rsz = event.data.resized; + if (resized) + { + handler_effective = true; + resized(rsz.size); + } + if (isRoot()) + { + setSize(rsz.size); + } + else + { + setPosition(pos); + setSize(size); + } + break; + case EventType::MousePressed: + mpr = event.data.mousePressed; + if (mousePressed && getGlobalBouds().contains(mpr.pos)) + { + handler_effective = true; + mousePressed(mpr.pos - basepos - pos, mpr.button); + } + lastMousePressTime[mpr.button] = currentTime(); + break; + case EventType::MouseReleased: + mrel = event.data.mouseReleased; + lastMousePressTime[mrel.button] = -1; + lastMousePressPos[mrel.button] = vec(-1.f, -1.f); + if (mouseReleased && getGlobalBouds().contains(mrel.pos)) + { + handler_effective = true; + mouseReleased(mrel.pos - basepos - pos, mrel.button); + } + if (isRoot() && + lastMousePressTime[mrel.button] >= 0 && + lastMousePressTime[mrel.button] < maxClickTime) + { + auto tmp = Event{ + .type = EventType::MouseClicked, + .data = EventData{ + .mouseClicked = mrel}}; + dispatchEvent(tmp); + } + break; + case EventType::MouseClicked: + mcli = event.data.mouseClicked; + if (mouseClicked && mouseLocated) + { + handler_effective = true; + mouseClicked(mcli.pos - basepos - pos, mcli.button); + } + if (mouseLocated) + { + focused = true; + } + else + { + focused = false; + } + break; + case EventType::MouseEntered: + mouseLocated = true; + if (mouseEntered) + { + handler_effective = true; + mouseEntered(event.data.mouseEntered.pos - basepos - pos); + } + break; + case EventType::MouseLeft: + mouseLocated = false; + if (mouseLeft) + { + handler_effective = true; + mouseLeft(event.data.mouseLeft.pos - basepos - pos); + } + break; + case EventType::MouseMoved: + mmov = event.data.mouseMoved; + if (getGlobalBouds().contains(mmov.pos)) + { + if (mouseMoved) + { + handler_effective = true; + mouseMoved(mmov.pos - basepos - pos); + } + if (isRoot() && !mouseLocated) + { + auto tmp = Event{ + .type = EventType::MouseEntered, + .data = event.data}; + dispatchEvent(tmp); + } + } + else if (isRoot() && mouseLocated) + { + auto tmp = Event{ + .type = EventType::MouseLeft, + .data = event.data}; + dispatchEvent(tmp); + } + if (!isRoot()) + break; + for (auto i = 0; i < sf::Mouse::Button::ButtonCount; i++) + { + if (lastMousePressPos[i] == vec(-1.f, -1.f)) + continue; + auto tmp = Event{ + .type = EventType::MouseDragged, + .data = { + .mouseDragged = _MouseDragged{ + .start = lastMousePressPos[i], + .now = mmov.pos, + .button = (sf::Mouse::Button)i}}}; + dispatchEvent(tmp); + } + break; + case EventType::MouseDragged: + mdra = event.data.mouseDragged; + if (mouseDragged) + { + handler_effective = true; + mouseDragged(mdra.start, mdra.now, mdra.button); + } + break; + case EventType::MouseScrolled: + if (mouseScrolled) + { + handler_effective = true; + mouseScrolled(event.data.mouseScrolled.delta); + } + break; + case EventType::TextInputed: + if (textInputed) + { + handler_effective = true; + textInputed(event.data.textInputed.uchar); + } + break; + } + + if (!spread) + return handler_effective; + + bool child_handled; + bool handler_handled = false; + for (auto container : childs) + { + if ((child_handled = container->dispatchEvent(event)) && !broadcast) + { + if (child_handled) + handler_handled = true; + break; + } + } + return handler_handled; +} + +sf::RenderWindow &Container::windowSFWindow() +{ + throw NotAWindowObj(const_cast(this)); +} + +sf::RenderWindow &Container::getSfWindow() +{ + return getRoot()->windowSFWindow(); +} + +bool sftk::operator==(pContainer &shp, Container *rawp) +{ + return shp.get() == rawp; +} + +int64_t Container::currentTime() +{ + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); +} + +Container::RootContainerNotPresent::RootContainerNotPresent(Container *container) + : SFTKException(container) {} + +const char *Container::RootContainerNotPresent::what() const noexcept +{ + static char msg[] = "sftk::Container 0x********_********: Root container not present."; + SFTKException::formatAddress(msg, sizeof(msg)); + return msg; +} + +Container::NotAWindowObj::NotAWindowObj(Container *container) + : SFTKException(container) {} + +const char *Container::NotAWindowObj::what() const noexcept +{ + static char msg[] = "sftk::Container 0x********_********: Not a sftk::Window."; + SFTKException::formatAddress(msg, sizeof(msg)); + return msg; +} diff --git a/src/window.cpp b/src/window.cpp new file mode 100644 index 0000000..690e51b --- /dev/null +++ b/src/window.cpp @@ -0,0 +1,86 @@ +#include + +#include +#include + +#include +#include + +using namespace sftk; + +Window &Window::create(int width, int height, std::wstring name) +{ + return *new Window(width, height, name); +} + +Window::Window(int width, int height, std::wstring name) + : Container(vec(0.f, 0.f), vec((float)width, (float)height), true), + sfwindow(sf::VideoMode(width, height), name), + running(new bool(true)) +{ + if (Container::getRoot().get()) + throw WindowMultipleConstructed(this); + Container::getRoot(this); +} + +void Window::exec() +{ + while (*running) + { + sf::Event event; + bool event_occured = false; + while (sfwindow.pollEvent(event)) + { + event_occured = true; + dispatchEvent(event); + switch (event.type) + { + case sf::Event::Closed: + *running = false; + sfwindow.close(); + break; + default: + break; + } + } + if (event_occured || hasInternalChange()) + { + update(); + sfwindow.display(); + } + } +} + +void Window::setSize(sf::Vector2u size) +{ + sfwindow.setSize(size); + Container::setSize(vec((float)size.x, (float)size.y)); +} + +bool Window::isRoot() const +{ + return true; +} + +sf::Vector2f &Window::getPosition() const +{ + auto _pos = sfwindow.getPosition(); + auto pos = vec((float)_pos.x, (float)_pos.y); + v2f_dumpst->push_back(pos); + return v2f_dumpst->back(); +} + +sf::RenderWindow &Window::windowSFWindow() +{ + return sfwindow; +} + +Window::WindowMultipleConstructed::WindowMultipleConstructed(Window *window) + : SFTKException(window) {} + +const char *Window::WindowMultipleConstructed::what() const noexcept +{ + static char msg[] = "sftk::Window 0x********_********: Multiply constructed in one thread."; + SFTKException::formatAddress(msg, sizeof(msg)); + return msg; +}