diff --git a/.gitmodules b/.gitmodules index 3cf1ef6..aaca960 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "deps/SFML"] path = deps/SFML url = https://github.com/SFML/SFML.git + branch = 2.6.1 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 40bcf90..378dbd5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,8 +10,10 @@ set(CONTAINER src/containers) set(SOURCES ${SOURCES} ${ROOT}/window.cpp) set(SOURCES ${SOURCES} ${ROOT}/font.cpp) +set(SOURCES ${SOURCES} ${ROOT}/richText.cpp) set(SOURCES ${SOURCES} ${CONTAINER}/container.cpp) set(SOURCES ${SOURCES} ${CONTAINER}/label.cpp) +set(SOURCES ${SOURCES} ${CONTAINER}/rtlabel.cpp) add_library(sftk SHARED ${SOURCES}) target_link_libraries(sftk PUBLIC sfml-graphics sfml-audio sfml-network) diff --git a/demo/SarasaUiSC-Bold.ttf b/demo/SarasaUiSC-Bold.ttf new file mode 100644 index 0000000..3bb5a52 Binary files /dev/null and b/demo/SarasaUiSC-Bold.ttf differ diff --git a/demo/SarasaUiSC-BoldItalic.ttf b/demo/SarasaUiSC-BoldItalic.ttf new file mode 100644 index 0000000..9b736d4 Binary files /dev/null and b/demo/SarasaUiSC-BoldItalic.ttf differ diff --git a/demo/SarasaUiSC-Italic.ttf b/demo/SarasaUiSC-Italic.ttf new file mode 100644 index 0000000..ab7eb11 Binary files /dev/null and b/demo/SarasaUiSC-Italic.ttf differ diff --git a/demo/demo1.cpp b/demo/demo1.cpp index 21f14cb..98c4d23 100644 --- a/demo/demo1.cpp +++ b/demo/demo1.cpp @@ -1,13 +1,17 @@ -#include - #include int main() { auto &window = sftk::Window::create(800, 600, L"demo1"); window.setBackgroudColor(sf::Color::White); - auto &label = sftk::Label::create(L"hello SFTK.", sftk::Font("../demo/SarasaUiSC-Regular.ttf", "", 18)); + auto &label = sftk::Label::create( + L"SFTK demo.\nBy Random World Studio.", + sftk::Font("../demo/SarasaUiSC-Regular.ttf", 18)); label.setColor(sf::Color::Black); - label.setPosition(vec(100.f, 100.f)); + window.registerEvent( + sftk::EventType::Resized, + std::function( + [&label](sf::Vector2f size) + { label.setPosition(size - label.getSize()); })); window.exec(); } diff --git a/include/containers/container.hpp b/include/containers/container.hpp index 3aae428..74ed9e8 100644 --- a/include/containers/container.hpp +++ b/include/containers/container.hpp @@ -38,7 +38,7 @@ namespace sftk virtual bool isRoot() const; virtual bool hasInternalChange() const; - void update(sf::RenderWindow &window) const; + void update(sf::RenderWindow &window, sf::View parentView) const; template void registerEvent(EventType type, std::function handler); diff --git a/include/containers/label.hpp b/include/containers/label.hpp index 3319f47..f7290c5 100644 --- a/include/containers/label.hpp +++ b/include/containers/label.hpp @@ -1,5 +1,5 @@ -#ifndef LABLE_HPP -#define LABLE_HPP +#ifndef LABEL_HPP +#define LABEL_HPP #include #include @@ -38,6 +38,10 @@ namespace sftk Font font; sf::Color color; + // After setText() called, this vector will be filled. + std::vector wrongGlyphs; + // This vector records every characters' x offset. + std::vector> linesGlyphPrefixSum; std::vector linesWidths; }; }; diff --git a/include/exceptions.hpp b/include/exceptions.hpp index 8ccc141..63437e9 100644 --- a/include/exceptions.hpp +++ b/include/exceptions.hpp @@ -33,7 +33,7 @@ namespace sftk * { * static char msg[] = "sftk::SomeClass 0x********_******** reported an exception."; * SFTKException::formatAddress(msg, sizeof(msg)); - * return msg; + * return const_cast(msg); * } * ``` * diff --git a/include/font.hpp b/include/font.hpp index f2ad56f..0d5ce9b 100644 --- a/include/font.hpp +++ b/include/font.hpp @@ -13,10 +13,9 @@ namespace sftk { public: Font(); - Font(std::string font, std::string fallback, int size); + Font(std::string font, int size); sf::Font font; - sf::Font fallback; int size; public: @@ -35,6 +34,42 @@ namespace sftk }; }; + class FontFamily + { + public: + enum FontType + { + Regular, + Bold, + Italic, + BoldItalic, + }; + + private: + static std::string genFontFileName(FontType ftype, std::string name); + + public: + FontFamily(); + FontFamily(std::string name, uint baseSize, std::string findPath = ""); + + sf::Font &operator[](FontType ftype); + + std::string name; + uint baseSize; + sf::Font regular; + sf::Font bold; + sf::Font italic; + sf::Font boldItalic; + + public: + class UnexceptedFontType : public SFTKException + { + public: + UnexceptedFontType(FontFamily *fntfml); + const char *what() const noexcept override; + }; + }; + }; #endif diff --git a/include/gtypes.hpp b/include/gtypes.hpp index 023e529..f9f80e5 100644 --- a/include/gtypes.hpp +++ b/include/gtypes.hpp @@ -16,7 +16,7 @@ constexpr sf::Vector3 vec(T x, T y, T z) } template -constexpr sf::Rect vec(T x, T y, T width, T height) +constexpr sf::Rect rec(T x, T y, T width, T height) { return sf::Rect(x, y, width, height); } diff --git a/include/window.hpp b/include/window.hpp index 042c8af..1d8bd67 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -11,10 +11,6 @@ namespace sftk { - class Window; - - typedef std::shared_ptr pWindow; - /** * @brief Window type * diff --git a/src/containers/container.cpp b/src/containers/container.cpp index 5bd1cfa..1b5667e 100644 --- a/src/containers/container.cpp +++ b/src/containers/container.cpp @@ -5,6 +5,7 @@ #include #include +#include using namespace sftk; @@ -29,7 +30,7 @@ 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)) + view(rec(0.f, 0.f, 0.f, 0.f)) { id = containerCounter++; if (!parent.get()) @@ -52,7 +53,7 @@ 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)) + view(rec(0.f, 0.f, size.x, size.y)) { id = containerCounter++; if (!parent.get()) @@ -69,7 +70,7 @@ Container::Container(sf::Vector2f pos, sf::Vector2f size, pContainer parent) globalContainers[id] = pthis; this->parent->children.push_back(pthis); auto rootsize = getRoot()->getSize(); - view.setViewport(vec( + view.setViewport(rec( (parent->basepos.x + pos.x) / rootsize.x, (parent->basepos.y + pos.y) / rootsize.y, size.x / rootsize.x, size.y / rootsize.y)); for (auto &p : lastMousePressPos) @@ -80,7 +81,7 @@ 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)), + view(rec(0.f, 0.f, size.x, size.y)), basepos(vec(0.f, 0.f)) { if (!root) @@ -89,7 +90,7 @@ Container::Container(sf::Vector2f pos, sf::Vector2f size, bool root) } id = containerCounter++; globalContainers[id] = pContainer(this); - view.setViewport(vec(0.f, 0.f, 1.f, 1.f)); + view.setViewport(rec(0.f, 0.f, 1.f, 1.f)); for (auto &p : lastMousePressPos) p = vec(-1.f, -1.f); } @@ -116,7 +117,7 @@ void Container::setPosition(sf::Vector2f pos) { this->pos = pos; auto rootsize = getRoot()->getSize(); - view.setViewport(vec( + view.setViewport(rec( (basepos.x + pos.x) / rootsize.x, (basepos.x + pos.y) / rootsize.y, size.x / rootsize.x, size.y / rootsize.y)); for (auto c : children) @@ -129,7 +130,7 @@ void Container::setSize(sf::Vector2f size) { this->size = size; auto rootsize = getRoot()->getSize(); - view.setViewport(vec( + view.setViewport(rec( (basepos.x + pos.x) / rootsize.x, (basepos.y + pos.y) / rootsize.y, size.x / rootsize.x, size.y / rootsize.y)); view.setSize(size); @@ -162,7 +163,7 @@ const sf::View &Container::getView() const sf::FloatRect Container::getGlobalBouds() const { - return vec(basepos.x + pos.x, basepos.y + pos.y, size.x, size.y); + return rec(basepos.x + pos.x, basepos.y + pos.y, size.x, size.y); } bool Container::isRoot() const @@ -175,13 +176,26 @@ bool Container::hasInternalChange() const return false; } -void Container::update(sf::RenderWindow &window) const +static sf::FloatRect calViewportOverlap(sf::FloatRect vpa, sf::FloatRect vpb) { + auto left = std::max(vpa.left, vpb.left); + auto right = std::min(vpa.left + vpa.width, vpb.left + vpb.width); + auto top = std::max(vpa.top, vpb.top); + auto bottom = std::min(vpa.top + vpa.height, vpb.top + vpb.height); + return sf::FloatRect(left, top, right - left, bottom - top); +} + +void Container::update(sf::RenderWindow &window, sf::View parentView) const +{ + auto view = this->view; + auto sz = getSize(); + sf::View currView(rec(0.f, 0.f, sz.x, sz.y)); + currView.setViewport(calViewportOverlap(view.getViewport(), parentView.getViewport())); window.setView(view); draw(window); for (auto c : children) { - c->update(window); + c->update(window, currView); } } @@ -361,6 +375,7 @@ bool Container::dispatchEvent(Event &event) } break; case EventType::Resized: + broadcast = true; rsz = event.data.resized; if (resized) { diff --git a/src/containers/label.cpp b/src/containers/label.cpp index 9b31aeb..74d75d9 100644 --- a/src/containers/label.cpp +++ b/src/containers/label.cpp @@ -44,10 +44,13 @@ Label &Label::create(std::wstring text, Font font, pContainer parent) void Label::setText(std::wstring text) { + sf::Font font = this->font.font; + uint font_size = this->font.size; this->text = text; linesWidths.clear(); float currentLineWidth = 0; float maxWidth = 0; + uint index = 0; for (auto ch : text) { if (ch == L'\n') @@ -57,7 +60,9 @@ void Label::setText(std::wstring text) maxWidth = currentLineWidth; currentLineWidth = 0; } - auto glyph = font.font.getGlyph(ch, font.size, false); + if (!font.hasGlyph(ch)) + wrongGlyphs.push_back(index); + auto glyph = font.getGlyph(ch, font_size, false); currentLineWidth += glyph.advance; } if (currentLineWidth != 0) @@ -66,7 +71,7 @@ void Label::setText(std::wstring text) if (currentLineWidth > maxWidth) maxWidth = currentLineWidth; } - auto lineSpacing = font.font.getLineSpacing(font.size); + auto lineSpacing = font.getLineSpacing(font_size); Container::setSize(vec(maxWidth, linesWidths.size() * lineSpacing)); } diff --git a/src/font.cpp b/src/font.cpp index b7358e5..532f2f4 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -1,19 +1,19 @@ #include +#include +namespace fs = std::filesystem; + using namespace sftk; Font::Font() {} -Font::Font(std::string font, std::string fallback, int size) +Font::Font(std::string font, 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) @@ -35,3 +35,75 @@ const char *Font::FailedToLoadFont::what() const noexcept SFTKException::formatAddress(msg, sizeof(msg)); return const_cast(msg); } + +FontFamily::FontFamily() {} + +FontFamily::FontFamily(std::string name, uint baseSize, std::string findPath) + : name(name), baseSize(baseSize) +{ + fs::path path(findPath); + for (auto &entry : fs::recursive_directory_iterator(path)) + { + if (fs::is_regular_file(entry)) + { + auto fname = entry.path().filename().generic_string(); + if (!fname.find(genFontFileName(FontType::Regular, name))) + regular.loadFromFile(entry.path()); + else if (!fname.find(genFontFileName(FontType::Bold, name))) + bold.loadFromFile(entry.path()); + else if (!fname.find(genFontFileName(FontType::Italic, name))) + italic.loadFromFile(entry.path()); + else if (!fname.find(genFontFileName(FontType::BoldItalic, name))) + boldItalic.loadFromFile(entry.path()); + } + } +} + +sf::Font &FontFamily::operator[](FontType ftype) +{ + switch (ftype) + { + case FontType::Regular: + return regular; + case FontType::Bold: + return bold; + case FontType::Italic: + return italic; + case FontType::BoldItalic: + return boldItalic; + default: + throw new UnexceptedFontType(this); + } +} + +std::string FontFamily::genFontFileName(FontType ftype, std::string name) +{ + name += '-'; + switch (ftype) + { + case FontType::Regular: + name += "Regular"; + break; + case FontType::Bold: + name += "Bold"; + break; + case FontType::Italic: + name += "Italic"; + break; + case FontType::BoldItalic: + name += "BoldItalic"; + break; + } + return name; +} + +FontFamily::UnexceptedFontType::UnexceptedFontType(FontFamily *fntfml) + : SFTKException(fntfml) {} + +const char *FontFamily::UnexceptedFontType::what() const noexcept +{ + static char msg[] = "sftk::FontFamily 0x********_********:" + " Using wrong index calling sftk::FontFamily::operator[](FontType)."; + SFTKException::formatAddress(msg, sizeof(msg)); + return const_cast(msg); +} diff --git a/src/window.cpp b/src/window.cpp index 5ea665d..6446e05 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -48,7 +48,7 @@ void Window::exec() if (event_occured || hasInternalChange()) { sfwindow.clear(backgroud); - update(sfwindow); + update(sfwindow, getView()); sfwindow.display(); } }