diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a31b8a..78b1ffe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,9 @@ set(ROOT src/) set(CONTAINER src/containers) set(SOURCES ${SOURCES} ${ROOT}/window.cpp) +set(SOURCES ${SOURCES} ${ROOT}/font.cpp) set(SOURCES ${SOURCES} ${CONTAINER}/container.cpp) +set(SOURCES ${SOURCES} ${CONTAINER}/label.cpp) add_library(sftk SHARED ${SOURCES}) target_link_libraries(sftk PUBLIC sfml-graphics sfml-audio sfml-network) diff --git a/demo/demo1.cpp b/demo/demo1.cpp index e1b37b7..60fdecc 100644 --- a/demo/demo1.cpp +++ b/demo/demo1.cpp @@ -5,15 +5,8 @@ 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); - container->registerEvent( - sftk::EventType::MouseDragged, - std::function( - [](sf::Vector2f start, sf::Vector2f now, sf::Mouse::Button button) - { std::cout << "(" << start.x << "," << start.y << ")" - << "->(" << now.x << "," << now.y << ")" - << "|" << button - << std::endl; })); + auto &label = sftk::Label::create(L"hello SFTK.", sftk::Font("SarasaUiSC-Regular.ttf", "", 18)); + label.setColor(sf::Color::White); + label.setPosition(vec(100.f, 100.f)); window.exec(); } diff --git a/include/containers/container.hpp b/include/containers/container.hpp index 218dd26..3aae428 100644 --- a/include/containers/container.hpp +++ b/include/containers/container.hpp @@ -2,6 +2,7 @@ #define CONTAINER_HPP #include +#include #include #include @@ -25,35 +26,38 @@ namespace sftk void setParent(pContainer parent); void setPosition(sf::Vector2f pos = sf::Vector2f(0, 0)); - void setSize(sf::Vector2f pos); - void setBackgroudColor(sf::Color color); + void setSize(sf::Vector2f size); + pContainer asContainer() const; pContainer getParent() const; const sf::Vector2f &getPosition() const; const sf::Vector2f &getSize() const; + const sf::View &getView() const; sf::FloatRect getGlobalBouds() const; virtual bool isRoot() const; virtual bool hasInternalChange() const; - virtual void update() const; + void update(sf::RenderWindow &window) const; - public: // Event hadler register functions template void registerEvent(EventType type, std::function handler); public: // Static and operators - static pContainer getRoot(Container *p = nullptr); + static pContainer getRoot(pContainer 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); + // Only call by sftk::Window::Window() + Container(sf::Vector2f pos, sf::Vector2f size, bool root = false); virtual sf::RenderWindow &windowSFWindow(); + virtual void draw(sf::RenderWindow &sfwindow) const; + // The events bases on sfml's Event, // so the Window class must use this function to // pass event to the root container @@ -61,23 +65,27 @@ namespace sftk // to process it as Event. void dispatchEvent(sf::Event &event); - private: // For this class to use + private: + uint64_t id; + 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; + std::vector children; sf::Vector2f basepos; sf::Vector2f pos, size; sf::View view; - sf::Color backgroudColor; - static const int64_t maxClickTime = 300; + static thread_local uint64_t containerCounter; + static thread_local std::map globalContainers; + static thread_local pContainer root; + private: // Called on construction static void rootTest(Container *self); inline static int64_t currentTime(); @@ -125,6 +133,13 @@ namespace sftk HandlerNotMatched(Container *container); const char *what() const noexcept override; }; + + class ConstructorCalledIllegally : public SFTKException + { + public: + ConstructorCalledIllegally(Container *container); + const char *what() const noexcept override; + }; }; }; diff --git a/include/containers/label.hpp b/include/containers/label.hpp new file mode 100644 index 0000000..3319f47 --- /dev/null +++ b/include/containers/label.hpp @@ -0,0 +1,45 @@ +#ifndef LABLE_HPP +#define LABLE_HPP + +#include +#include + +#include +#include + +namespace sftk +{ + class Label : public Container + { + public: + static Label &create(pContainer parent = nullptr); + static Label &create(sf::Vector2f pos, pContainer parent = nullptr); + static Label &create(std::wstring text, pContainer parent = nullptr); + static Label &create(Font font, pContainer parent = nullptr); + static Label &create(std::wstring text, Font font, pContainer parent = nullptr); + + void setText(std::wstring text); + void setFont(Font font); + void setColor(sf::Color color); + void setSize(sf::Vector2f size) = delete; + + const std::wstring &getText() const; + const Font &getFont() const; + sf::Color getColor() const; + + protected: + Label(pContainer parent = nullptr); + Label(sf::Vector2f pos, pContainer parent = nullptr); + + void draw(sf::RenderWindow &sfwindow) const; + + private: + std::wstring text; + Font font; + sf::Color color; + + std::vector linesWidths; + }; +}; + +#endif diff --git a/include/exceptions.hpp b/include/exceptions.hpp index 0b11618..8ccc141 100644 --- a/include/exceptions.hpp +++ b/include/exceptions.hpp @@ -40,17 +40,23 @@ namespace sftk * @param msg exception message * @param len message length */ - void formatAddress(char *msg, int len) const + void formatAddress(char *msg, int len) const noexcept { - 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++) + auto ptr = reinterpret_cast(sftkObj); + while (--len >= 0) { - if (msg[i] == '*') + if (msg[len] == '*') { - msg[i] = *it; - ++it; + auto bit = ptr-- & 0xf; + if (bit < 10) + { + bit += '0'; + } + else + { + bit -= 10 - 'a'; + } + msg[len] = bit; } } } diff --git a/include/font.hpp b/include/font.hpp new file mode 100644 index 0000000..f2ad56f --- /dev/null +++ b/include/font.hpp @@ -0,0 +1,40 @@ +#ifndef FONT_HPP +#define FONT_HPP + +#include + +#include + +#include + +namespace sftk +{ + class Font + { + public: + Font(); + Font(std::string font, std::string fallback, int size); + + sf::Font font; + sf::Font fallback; + int size; + + public: + class MainFontIsEmpty : public SFTKException + { + public: + MainFontIsEmpty(Font *font); + const char *what() const noexcept override; + }; + + class FailedToLoadFont : public SFTKException + { + public: + FailedToLoadFont(Font *font); + const char *what() const noexcept override; + }; + }; + +}; + +#endif diff --git a/include/sftk.hpp b/include/sftk.hpp index 9ec0109..36f49a2 100644 --- a/include/sftk.hpp +++ b/include/sftk.hpp @@ -4,7 +4,7 @@ #include #include - #include +#include #endif diff --git a/include/window.hpp b/include/window.hpp index c012fd8..042c8af 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -28,6 +28,9 @@ namespace sftk void exec(); void setSize(sf::Vector2u size); + void setBackgroudColor(sf::Color color); + + const sf::Color &getBackgroundColor() const; bool isRoot() const; @@ -45,6 +48,7 @@ namespace sftk private: std::unique_ptr> v2f_dumpst; + sf::Color backgroud; public: class WindowMultipleConstructed : public SFTKException diff --git a/src/containers/container.cpp b/src/containers/container.cpp index db4149d..5bd1cfa 100644 --- a/src/containers/container.cpp +++ b/src/containers/container.cpp @@ -8,11 +8,14 @@ using namespace sftk; -pContainer Container::getRoot(Container *p) +thread_local uint64_t Container::containerCounter = 0; +thread_local std::map Container::globalContainers; +thread_local pContainer Container::root = nullptr; + +pContainer Container::getRoot(pContainer p) { - static thread_local pContainer root; if (p) - root.reset(p); + root = p; return root; } @@ -22,8 +25,13 @@ void Container::rootTest(Container *self) throw RootContainerNotPresent(self); } -Container::Container(pContainer parent) : parent(nullptr) +Container::Container(pContainer parent) + : parent(nullptr), + pos(vec(0.f, 0.f)), + size(vec(0.f, 0.f)), + view(vec(0.f, 0.f, 0.f, 0.f)) { + id = containerCounter++; if (!parent.get()) { Container::rootTest(this); @@ -33,7 +41,9 @@ Container::Container(pContainer parent) : parent(nullptr) { this->parent = parent; } - this->parent->childs.push_back(pContainer(this)); + pContainer pthis(this); + globalContainers[id] = pthis; + this->parent->children.push_back(pthis); for (auto &p : lastMousePressPos) p = vec(-1.f, -1.f); } @@ -42,10 +52,9 @@ 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) + view(vec(0.f, 0.f, size.x, size.y)) { + id = containerCounter++; if (!parent.get()) { Container::rootTest(this); @@ -55,7 +64,10 @@ Container::Container(sf::Vector2f pos, sf::Vector2f size, pContainer parent) { this->parent = parent; } - this->parent->childs.push_back(pContainer(this)); + basepos = this->parent->basepos + this->parent->pos; + pContainer pthis(this); + globalContainers[id] = pthis; + this->parent->children.push_back(pthis); auto rootsize = getRoot()->getSize(); view.setViewport(vec( (parent->basepos.x + pos.x) / rootsize.x, (parent->basepos.y + pos.y) / rootsize.y, @@ -69,9 +81,14 @@ Container::Container(sf::Vector2f pos, sf::Vector2f size, bool root) pos(pos), size(size), view(vec(0.f, 0.f, size.x, size.y)), - basepos(vec(0.f, 0.f)), - backgroudColor(sf::Color::Black) + basepos(vec(0.f, 0.f)) { + if (!root) + { + throw new ConstructorCalledIllegally(this); + } + id = containerCounter++; + globalContainers[id] = pContainer(this); view.setViewport(vec(0.f, 0.f, 1.f, 1.f)); for (auto &p : lastMousePressPos) p = vec(-1.f, -1.f); @@ -79,20 +96,20 @@ Container::Container(sf::Vector2f pos, sf::Vector2f size, bool root) pContainer Container::create(pContainer parent) { - return pContainer(new Container(parent)); + return (new Container(parent))->asContainer(); } pContainer Container::create(sf::Vector2f pos, sf::Vector2f size, pContainer parent) { - return pContainer(new Container(pos, size, parent)); + return (new Container(pos, size, parent))->asContainer(); } void Container::setParent(pContainer parent) { - auto it = std::find(this->parent->childs.begin(), this->parent->childs.end(), this); - this->parent->childs.erase(it); + auto it = std::find(this->parent->children.begin(), this->parent->children.end(), this); + this->parent->children.erase(it); this->parent = parent; - this->parent->childs.push_back(pContainer(this)); + this->parent->children.push_back(pContainer(this)); } void Container::setPosition(sf::Vector2f pos) @@ -100,8 +117,12 @@ 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, + (basepos.x + pos.x) / rootsize.x, (basepos.x + pos.y) / rootsize.y, size.x / rootsize.x, size.y / rootsize.y)); + for (auto c : children) + { + c->basepos = basepos + pos; + } } void Container::setSize(sf::Vector2f size) @@ -111,11 +132,12 @@ void Container::setSize(sf::Vector2f size) view.setViewport(vec( (basepos.x + pos.x) / rootsize.x, (basepos.y + pos.y) / rootsize.y, size.x / rootsize.x, size.y / rootsize.y)); + view.setSize(size); } -void Container::setBackgroudColor(sf::Color color) +pContainer Container::asContainer() const { - backgroudColor = color; + return globalContainers[id]; } pContainer Container::getParent() const @@ -133,6 +155,11 @@ const sf::Vector2f &Container::getSize() const return size; } +const sf::View &Container::getView() const +{ + return view; +} + sf::FloatRect Container::getGlobalBouds() const { return vec(basepos.x + pos.x, basepos.y + pos.y, size.x, size.y); @@ -148,27 +175,18 @@ bool Container::hasInternalChange() const return false; } -void Container::update() const +void Container::update(sf::RenderWindow &window) const { - auto &window = getRoot()->windowSFWindow(); - if (isRoot()) + window.setView(view); + draw(window); + for (auto c : children) { - window.clear(backgroudColor); - } - else - { - 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(); + c->update(window); } } +void Container::draw(sf::RenderWindow &sfwindow) const {} + template <> void Container::registerEvent(EventType type, std::function handler) { @@ -507,7 +525,7 @@ bool Container::dispatchEvent(Event &event) bool child_handled; bool handler_handled = false; - for (auto container : childs) + for (auto container : children) { if ((child_handled = container->dispatchEvent(event)) && !broadcast) { @@ -548,7 +566,7 @@ const char *Container::RootContainerNotPresent::what() const noexcept { static char msg[] = "sftk::Container 0x********_********: Root container not present."; SFTKException::formatAddress(msg, sizeof(msg)); - return msg; + return const_cast(msg); } Container::NotAWindowObj::NotAWindowObj(Container *container) @@ -558,7 +576,7 @@ const char *Container::NotAWindowObj::what() const noexcept { static char msg[] = "sftk::Container 0x********_********: Not a sftk::Window."; SFTKException::formatAddress(msg, sizeof(msg)); - return msg; + return const_cast(msg); } Container::HandlerNotMatched::HandlerNotMatched(Container *container) @@ -566,7 +584,19 @@ Container::HandlerNotMatched::HandlerNotMatched(Container *container) const char *Container::HandlerNotMatched::what() const noexcept { - static char msg[] = "sftk::Container 0x********_********: When calling Container::registerEvent(), passed a closure with wrong arguments."; + static char msg[] = "sftk::Container 0x********_********:" + " When calling Container::registerEvent(), passed a closure with wrong arguments."; SFTKException::formatAddress(msg, sizeof(msg)); - return msg; + return const_cast(msg); +} + +Container::ConstructorCalledIllegally::ConstructorCalledIllegally(Container *container) + : SFTKException(container) {} + +const char *Container::ConstructorCalledIllegally::what() const noexcept +{ + static char msg[] = "sftk::Container 0x********_********:" + " This constructor can only be called by sftk::Window::Window()."; + SFTKException::formatAddress(msg, sizeof(msg)); + return const_cast(msg); } diff --git a/src/containers/label.cpp b/src/containers/label.cpp new file mode 100644 index 0000000..9b31aeb --- /dev/null +++ b/src/containers/label.cpp @@ -0,0 +1,108 @@ +#include + +#include +#include + +using namespace sftk; + +Label::Label(pContainer parent) : Container(parent) {} + +Label::Label(sf::Vector2f pos, pContainer parent) + : Container(pos, vec(0.f, 0.f), parent) {} + +Label &Label::create(pContainer parent) +{ + return *new Label(parent); +} + +Label &Label::create(sf::Vector2f pos, pContainer parent) +{ + return *new Label(pos, parent); +} + +Label &Label::create(std::wstring text, pContainer parent) +{ + auto *label = new Label(parent); + label->setText(text); + return *label; +} + +Label &Label::create(Font font, pContainer parent) +{ + auto *label = new Label(parent); + label->setFont(font); + return *label; +} + +Label &Label::create(std::wstring text, Font font, pContainer parent) +{ + auto *label = new Label(parent); + label->setFont(font); + label->setText(text); + return *label; +} + +void Label::setText(std::wstring text) +{ + this->text = text; + linesWidths.clear(); + float currentLineWidth = 0; + float maxWidth = 0; + for (auto ch : text) + { + if (ch == L'\n') + { + linesWidths.push_back(currentLineWidth); + if (currentLineWidth > maxWidth) + maxWidth = currentLineWidth; + currentLineWidth = 0; + } + auto glyph = font.font.getGlyph(ch, font.size, false); + currentLineWidth += glyph.advance; + } + if (currentLineWidth != 0) + { + linesWidths.push_back(currentLineWidth); + if (currentLineWidth > maxWidth) + maxWidth = currentLineWidth; + } + auto lineSpacing = font.font.getLineSpacing(font.size); + Container::setSize(vec(maxWidth, linesWidths.size() * lineSpacing)); +} + +void Label::setFont(Font font) +{ + this->font = font; +} + +void Label::setColor(sf::Color color) +{ + this->color = color; +} + +const std::wstring &Label::getText() const +{ + return text; +} + +const Font &Label::getFont() const +{ + return font; +} + +sf::Color Label::getColor() const +{ + return color; +} + +void Label::draw(sf::RenderWindow &sfwindow) const +{ + sf::Text textObj; + auto sz = getSize(); + textObj.setPosition(vec(-sz.x / 2, -sz.y / 2)); + textObj.setString(text); + textObj.setFont(font.font); + textObj.setCharacterSize(font.size); + textObj.setFillColor(color); + sfwindow.draw(textObj); +} diff --git a/src/font.cpp b/src/font.cpp new file mode 100644 index 0000000..b7358e5 --- /dev/null +++ b/src/font.cpp @@ -0,0 +1,37 @@ +#include + +using namespace sftk; + +Font::Font() {} + +Font::Font(std::string font, std::string fallback, int size) + : size(size) +{ + if (font.empty()) + throw new MainFontIsEmpty(this); + if (!this->font.loadFromFile(font)) + throw new FailedToLoadFont(this); + if (!fallback.empty()) + if (!this->fallback.loadFromFile(fallback)) + throw new FailedToLoadFont(this); +} + +Font::MainFontIsEmpty::MainFontIsEmpty(Font *font) + : SFTKException(font) {} + +const char *Font::MainFontIsEmpty::what() const noexcept +{ + static char msg[] = "sftk::Font 0x********_********: The main font must be set."; + SFTKException::formatAddress(msg, sizeof(msg)); + return const_cast(msg); +} + +Font::FailedToLoadFont::FailedToLoadFont(Font *font) + : SFTKException(font) {} + +const char *Font::FailedToLoadFont::what() const noexcept +{ + static char msg[] = "sftk::Font 0x********_********: Failed to load font file."; + SFTKException::formatAddress(msg, sizeof(msg)); + return const_cast(msg); +} diff --git a/src/window.cpp b/src/window.cpp index 690e51b..5ea665d 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -16,11 +17,12 @@ Window &Window::create(int width, int height, std::wstring 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)) + running(new bool(true)), + backgroud(sf::Color::Black) { if (Container::getRoot().get()) throw WindowMultipleConstructed(this); - Container::getRoot(this); + Container::getRoot(this->asContainer()); } void Window::exec() @@ -45,7 +47,8 @@ void Window::exec() } if (event_occured || hasInternalChange()) { - update(); + sfwindow.clear(backgroud); + update(sfwindow); sfwindow.display(); } } @@ -57,6 +60,16 @@ void Window::setSize(sf::Vector2u size) Container::setSize(vec((float)size.x, (float)size.y)); } +void Window::setBackgroudColor(sf::Color color) +{ + backgroud = color; +} + +const sf::Color &Window::getBackgroundColor() const +{ + return backgroud; +} + bool Window::isRoot() const { return true; @@ -82,5 +95,5 @@ 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; + return const_cast(msg); }