diff --git a/CMakeLists.txt b/CMakeLists.txt index 26e0bd9..3fd646e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ set(sources "src/main.cpp" "src/GUIStuff/Elements/MovableTabList.cpp" "src/GUIStuff/Elements/PaintCircleMenu.cpp" "src/GUIStuff/Elements/RotateWheel.cpp" + "src/GUIStuff/Elements/TextParagraph.cpp" "include/sago/platform_folders.cpp" "include/Helpers/Networking/NetLibrary.cpp" "include/Helpers/Networking/NetServer.cpp" diff --git a/src/FontData.cpp b/src/FontData.cpp index 788e05b..5a0fc5c 100644 --- a/src/FontData.cpp +++ b/src/FontData.cpp @@ -29,6 +29,8 @@ FontData::FontData() mgr = SkFontMgr_New_Custom_Empty(); #endif map["Roboto"] = mgr->makeFromFile("data/fonts/Roboto-variable.ttf"); + collection = sk_make_sp(); + collection->setDefaultFontManager(mgr); } FontData::~FontData() { diff --git a/src/FontData.hpp b/src/FontData.hpp index 51cc7cd..1248b54 100644 --- a/src/FontData.hpp +++ b/src/FontData.hpp @@ -4,12 +4,14 @@ #include #include #include +#include using namespace Eigen; struct FontData { FontData(); sk_sp mgr; + sk_sp collection; std::unordered_map> map; ~FontData(); }; diff --git a/src/GUIStuff/Elements/Element.hpp b/src/GUIStuff/Elements/Element.hpp index c82396a..331510c 100644 --- a/src/GUIStuff/Elements/Element.hpp +++ b/src/GUIStuff/Elements/Element.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,7 @@ struct UpdateInputData { bool copy = false; bool paste = false; bool cut = false; + bool escape = false; } key; struct { @@ -107,6 +109,8 @@ struct UpdateInputData { uint16_t fontSize = 20; sk_sp textTypeface; + sk_sp fontCollection; + std::string textInput; bool hoverObstructed = false; bool acceptingTextInput = false; diff --git a/src/GUIStuff/Elements/TextParagraph.cpp b/src/GUIStuff/Elements/TextParagraph.cpp new file mode 100644 index 0000000..a07ec51 --- /dev/null +++ b/src/GUIStuff/Elements/TextParagraph.cpp @@ -0,0 +1,28 @@ +#include "TextParagraph.hpp" +#include +#include + +namespace GUIStuff { + +void TextParagraph::update(UpdateInputData& io, std::unique_ptr paragraph, float width, const std::function& elemUpdate) { + data = std::move(paragraph); + data->layout(width); + CLAY({ + .layout = { + .sizing = {.width = CLAY_SIZING_FIXED(width), .height = CLAY_SIZING_FIXED(data->getHeight())} + }, + .custom = { .customData = this } + }) { + if(elemUpdate) + elemUpdate(); + } +} + +void TextParagraph::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) { + bb = get_bb(command); + + if(data) + data->paint(canvas, bb.min.x(), bb.min.y()); +} + +} diff --git a/src/GUIStuff/Elements/TextParagraph.hpp b/src/GUIStuff/Elements/TextParagraph.hpp new file mode 100644 index 0000000..f968dcc --- /dev/null +++ b/src/GUIStuff/Elements/TextParagraph.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "Element.hpp" +#include + +namespace GUIStuff { + +class TextParagraph : public Element { + public: + void update(UpdateInputData& io, std::unique_ptr paragraph, float width, const std::function& elemUpdate); + virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) override; + private: + SCollision::AABB bb = SCollision::AABB({0.0f, 0.0f}, {0.0f, 0.0f}); + std::unique_ptr data; +}; + +} diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp index d7e0135..b280db5 100644 --- a/src/GUIStuff/GUIManager.cpp +++ b/src/GUIStuff/GUIManager.cpp @@ -7,13 +7,21 @@ #include "Elements/SVGIcon.hpp" #include "Elements/SelectableButton.hpp" #include "Elements/RotateWheel.hpp" +#include "Elements/TextParagraph.hpp" #include "Elements/MovableTabList.hpp" #include "Elements/Element.hpp" +#include "Elements/TextParagraph.hpp" #include #include #include #include #include +#include +#include +#include +#include +#include +#include namespace GUIStuff { @@ -28,11 +36,20 @@ GUIManager::GUIManager() Clay_Dimensions GUIManager::clay_skia_measure_text(Clay_StringSlice str, Clay_TextElementConfig* config, void* userData) { GUIManager* window = static_cast(userData); - SkFont font = window->io->get_font(config->fontSize); - SkFontMetrics metrics; - font.getMetrics(&metrics); - float nextText = font.measureText(str.chars, str.length, SkTextEncoding::kUTF8, nullptr); - return Clay_Dimensions(nextText, - metrics.fAscent + metrics.fDescent); + + skia::textlayout::ParagraphStyle pStyle; + pStyle.setTextAlign(skia::textlayout::TextAlign::kLeft); + skia::textlayout::TextStyle tStyle; + tStyle.setFontSize(config->fontSize); + tStyle.setFontFamilies({SkString{"Roboto"}}); + pStyle.setTextStyle(tStyle); + + skia::textlayout::ParagraphBuilderImpl a(pStyle, window->io->fontCollection, SkUnicodes::ICU::Make()); + a.addText(str.chars, str.length); + std::unique_ptr p = a.Build(); + p->layout(std::numeric_limits::max()); + + return Clay_Dimensions(p->getMaxIntrinsicWidth(), p->getHeight()); } void GUIManager::begin() { @@ -92,12 +109,20 @@ void GUIManager::draw(SkCanvas* canvas) { } case CLAY_RENDER_COMMAND_TYPE_TEXT: { Clay_TextRenderData* config = &command->renderData.text; - SkPaint paint; - paint.setColor4f(convert_vec4(config->textColor)); - SkFont f = io->get_font(config->fontSize); - SkFontMetrics metrics; - f.getMetrics(&metrics); - canvas->drawSimpleText(config->stringContents.chars, config->stringContents.length, SkTextEncoding::kUTF8, bb.x, (bb.y + bb.height - metrics.fDescent), io->get_font(config->fontSize), paint); + + skia::textlayout::ParagraphStyle pStyle; + pStyle.setTextAlign(skia::textlayout::TextAlign::kLeft); + skia::textlayout::TextStyle tStyle; + tStyle.setFontSize(config->fontSize); + tStyle.setFontFamilies({SkString{"Roboto"}}); + pStyle.setTextStyle(tStyle); + + skia::textlayout::ParagraphBuilderImpl a(pStyle, io->fontCollection, SkUnicodes::ICU::Make()); + a.addText(config->stringContents.chars, config->stringContents.length); + std::unique_ptr p = a.Build(); + p->layout(std::numeric_limits::max()); + p->paint(canvas, bb.x, bb.y); + break; } case CLAY_RENDER_COMMAND_TYPE_BORDER: { @@ -679,6 +704,13 @@ bool GUIManager::rotate_wheel(const std::string& id, double* angle, float size, return toRet; } +void GUIManager::text_paragraph(const std::string& id, std::unique_ptr paragraph, float width, const std::function& elemUpdate) { + push_id(id); + TextParagraph* e = insert_element(); + e->update(*io, std::move(paragraph), width, elemUpdate); + pop_id(); +} + void GUIManager::paint_circle_popup_menu(const std::string& id, const Vector2f& centerPos, const PaintCircleMenu::Data& val, const std::function& elemUpdate) { float size = 300.0f; push_id(id); diff --git a/src/GUIStuff/GUIManager.hpp b/src/GUIStuff/GUIManager.hpp index b73b676..34af422 100644 --- a/src/GUIStuff/GUIManager.hpp +++ b/src/GUIStuff/GUIManager.hpp @@ -14,6 +14,8 @@ #include #include +#include + using namespace Eigen; namespace GUIStuff { @@ -43,6 +45,7 @@ class GUIManager { void input_text_field(const std::string& id, const std::string& name, std::string* val, const std::function& elemUpdate = nullptr); + void text_paragraph(const std::string& id, std::unique_ptr paragraph, float width, const std::function& elemUpdate = nullptr); void text_label_color(const std::string& val, const SkColor4f& color); void text_label_size(const std::string& val, float modifier); void text_label(const std::string& val); diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 1fd9cff..2c658e6 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -18,6 +18,12 @@ #include #include +#include +#include +#include +#include +#include + #define UPDATE_DOWNLOAD_URL "https://infinipaint.com/download.html" #define UPDATE_NOTIFICATION_URL "https://infinipaint.com/updateNotificationVersion.txt" @@ -33,6 +39,7 @@ Toolbar::Toolbar(MainProgram& initMain): { io->textTypeface = main.fonts.map["Roboto"]; io->textFontMgr = main.fonts.mgr; + io->fontCollection = main.fonts.collection; io->fontSize = 20; load_default_palette(); @@ -819,6 +826,7 @@ void Toolbar::bookmark_menu(bool justOpened) { } void Toolbar::chat_box() { + constexpr float CHATBOX_WIDTH = 700; if(main.world->network_being_used()) { CLAY({ .layout = { @@ -839,7 +847,7 @@ void Toolbar::chat_box() { } CLAY({ .layout = { - .sizing = {.width = CLAY_SIZING_FIXED(700), .height = CLAY_SIZING_FIT(0) }, + .sizing = {.width = CLAY_SIZING_FIXED(CHATBOX_WIDTH), .height = CLAY_SIZING_FIT(0) }, .childGap = 0, .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_BOTTOM}, .layoutDirection = CLAY_TOP_TO_BOTTOM @@ -849,6 +857,7 @@ void Toolbar::chat_box() { gui.obstructing_window(); gui.push_id("chat box"); if(chatBoxState == CHATBOXSTATE_JUSTOPEN || chatBoxState == CHATBOXSTATE_OPEN) { + gui.push_id("messages"); CLAY({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0) }, @@ -859,7 +868,9 @@ void Toolbar::chat_box() { }, .backgroundColor = convert_vec4(io->theme->backColor1) }) { + int id = 0; for(auto& chatMessage : main.world->chatMessages | std::views::reverse) { + id++; SkColor4f c; switch(chatMessage.color) { case ChatMessage::COLOR_NORMAL: @@ -869,9 +880,23 @@ void Toolbar::chat_box() { c = io->theme->fillColor4; break; } - gui.text_label_color(chatMessage.text, c); + gui.push_id(id); + skia::textlayout::ParagraphStyle pStyle; + pStyle.setTextAlign(skia::textlayout::TextAlign::kLeft); + skia::textlayout::TextStyle tStyle; + tStyle.setFontSize(io->fontSize); + tStyle.setFontFamilies({SkString{"Roboto"}}); + tStyle.setForegroundColor(SkPaint{c}); + pStyle.setTextStyle(tStyle); + + skia::textlayout::ParagraphBuilderImpl a(pStyle, io->fontCollection, SkUnicodes::ICU::Make()); + a.addText(chatMessage.text.c_str(), chatMessage.text.length()); + + gui.text_paragraph("text", a.Build(), CHATBOX_WIDTH); + gui.pop_id(); } } + gui.pop_id(); gui.left_to_right_line_layout([&]() { gui.input_text("message input", &chatMessageInput); @@ -888,9 +913,9 @@ void Toolbar::chat_box() { t.selection.selected = true; chatBoxState = CHATBOXSTATE_OPEN; } - if(main.input.key(InputManager::KEY_GENERIC_ESCAPE).pressed) + if(io->key.escape) t.selection.selected = false; - if(main.input.key(InputManager::KEY_TEXT_ENTER).pressed) { + if(io->key.enter) { main.world->send_chat_message(chatMessageInput); t.selection.selected = false; } @@ -902,7 +927,10 @@ void Toolbar::chat_box() { else { constexpr float DISPLAY_TIME = 8.0f; constexpr float FADE_START_TIME = 7.0f; + gui.push_id("messages popup"); + int id = 0; for(auto& chatMessage : main.world->chatMessages | std::views::reverse) { + id++; chatMessage.time.update_time_since(); if(chatMessage.time < DISPLAY_TIME) { float a = 1.0f - lerp_time(chatMessage.time, DISPLAY_TIME, FADE_START_TIME); @@ -925,11 +953,25 @@ void Toolbar::chat_box() { }, .backgroundColor = convert_vec4(color_mul_alpha(io->theme->backColor1, a)), }) { + gui.push_id(id); gui.obstructing_window(); - gui.text_label_color(chatMessage.text, color_mul_alpha(c, a)); + skia::textlayout::ParagraphStyle pStyle; + pStyle.setTextAlign(skia::textlayout::TextAlign::kLeft); + skia::textlayout::TextStyle tStyle; + tStyle.setFontSize(io->fontSize); + tStyle.setFontFamilies({SkString{"Roboto"}}); + tStyle.setForegroundColor(SkPaint{color_mul_alpha(c, a)}); + pStyle.setTextStyle(tStyle); + + skia::textlayout::ParagraphBuilderImpl a(pStyle, io->fontCollection, SkUnicodes::ICU::Make()); + a.addText(chatMessage.text.c_str(), chatMessage.text.length()); + + gui.text_paragraph("text", a.Build(), CHATBOX_WIDTH); + gui.pop_id(); } } } + gui.pop_id(); } gui.pop_id(); } @@ -1910,6 +1952,7 @@ void Toolbar::initialize_io_before_update() { io->key.copy = main.input.key(InputManager::KEY_TEXT_COPY).repeat; io->key.paste = main.input.get_clipboard_paste_happened(); io->key.cut = main.input.key(InputManager::KEY_TEXT_CUT).repeat; + io->key.escape = main.input.key(InputManager::KEY_GENERIC_ESCAPE).repeat; io->textInput = main.input.text.newInput; io->clipboard.textInFunc = [&]() {