实现Label组件

This commit is contained in:
pointer-to-bios 2024-08-20 10:39:38 +08:00
parent c7cd5bad91
commit 77d2d45b20
12 changed files with 366 additions and 73 deletions

View File

@ -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)

View File

@ -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();
}

View File

@ -2,6 +2,7 @@
#define CONTAINER_HPP
#include <vector>
#include <map>
#include <memory>
#include <functional>
@ -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 <class T>
void registerEvent(EventType type, std::function<T> 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<pContainer> childs;
std::vector<pContainer> 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<uint64_t, pContainer> 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<Container>
{
public:
ConstructorCalledIllegally(Container *container);
const char *what() const noexcept override;
};
};
};

View File

@ -0,0 +1,45 @@
#ifndef LABLE_HPP
#define LABLE_HPP
#include <string>
#include <vector>
#include <font.hpp>
#include <containers/container.hpp>
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<float> linesWidths;
};
};
#endif

View File

@ -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<uintptr_t>(sftkObj);
auto it = ss.str().begin();
for (int i = 0; i < len; i++)
auto ptr = reinterpret_cast<uintptr_t>(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;
}
}
}

40
include/font.hpp Normal file
View File

@ -0,0 +1,40 @@
#ifndef FONT_HPP
#define FONT_HPP
#include <string>
#include <SFML/Graphics.hpp>
#include <exceptions.hpp>
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<Font>
{
public:
MainFontIsEmpty(Font *font);
const char *what() const noexcept override;
};
class FailedToLoadFont : public SFTKException<Font>
{
public:
FailedToLoadFont(Font *font);
const char *what() const noexcept override;
};
};
};
#endif

View File

@ -4,7 +4,7 @@
#include <gtypes.hpp>
#include <window.hpp>
#include <containers/container.hpp>
#include <containers/label.hpp>
#endif

View File

@ -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<std::vector<sf::Vector2f>> v2f_dumpst;
sf::Color backgroud;
public:
class WindowMultipleConstructed : public SFTKException<Window>

View File

@ -8,11 +8,14 @@
using namespace sftk;
pContainer Container::getRoot(Container *p)
thread_local uint64_t Container::containerCounter = 0;
thread_local std::map<uint64_t, pContainer> 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<void()> 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<const char *>(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<const char *>(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<const char *>(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<const char *>(msg);
}

108
src/containers/label.cpp Normal file
View File

@ -0,0 +1,108 @@
#include <containers/label.hpp>
#include <iostream>
#include <gtypes.hpp>
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);
}

37
src/font.cpp Normal file
View File

@ -0,0 +1,37 @@
#include <font.hpp>
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<const char *>(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<const char *>(msg);
}

View File

@ -2,6 +2,7 @@
#include <sstream>
#include <iomanip>
#include <iostream>
#include <gtypes.hpp>
#include <containers/container.hpp>
@ -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<const char *>(msg);
}