Added richtext clipboard to Emscripten and fixed emscripten compilation warning

This commit is contained in:
Yousef Khadadeh 2025-11-12 09:00:03 +03:00
parent 8830386dc0
commit 9eeee0dfff
5 changed files with 66 additions and 26 deletions

View file

@ -26,7 +26,7 @@ template <typename T> class ColorPicker : public Element {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }
},
.aspectRatio = 1.0f,
.aspectRatio = {1.0f},
.custom = { .customData = this }
}) {
selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld);

View file

@ -141,6 +141,7 @@ std::string InputManager::get_clipboard_data_for_mimetype(const std::string& mim
void InputManager::set_clipboard_str(std::string_view s) {
#ifdef __EMSCRIPTEN__
emscripten_browser_clipboard::copy(std::string(s));
lastCopiedRichText = std::nullopt;
#else
SDL_SetClipboardText(s.data());
#endif
@ -150,13 +151,14 @@ std::string InputManager::get_infpnt_richtext_mimetype() {
return "web text/infpnt";
}
void InputManager::set_clipboard_plain_and_richtext_pair(const std::pair<std::string, std::string>& plainAndRichtextPair) {
void InputManager::set_clipboard_plain_and_richtext_pair(const std::pair<std::string, RichText::TextData>& plainAndRichtextPair) {
#ifdef __EMSCRIPTEN__
set_clipboard_str(plainAndRichtextPair.first);
lastCopiedRichText = plainAndRichtextPair.second;
#else
std::unordered_map<std::string, std::string> clipboardData;
clipboardData["text/plain"] = plainAndRichtextPair.first;
clipboardData[get_infpnt_richtext_mimetype()] = plainAndRichtextPair.second;
clipboardData[get_infpnt_richtext_mimetype()] = plainAndRichtextPair.second.get_serialized();
set_clipboard_data(clipboardData);
#endif
}
@ -385,11 +387,24 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) {
void InputManager::process_text_paste() {
if(text.textBox) {
std::string richTextClipboard = get_clipboard_data_for_mimetype(get_infpnt_richtext_mimetype());
if(!richTextClipboard.empty())
text.textBox->process_rich_text_input(*text.cursor, richTextClipboard);
else
text.textBox->process_text_input(*text.cursor, get_clipboard_data_for_mimetype("text/plain"), text.modMap);
#ifdef __EMSCRIPTEN__
// Workaround for not being able to copy richtext to system clipboard, this should at least work within the application itself
std::string plainClipboardStr = get_clipboard_str();
if(lastCopiedRichText.has_value()) {
if(lastCopiedRichText.value().get_plain_text() == plainClipboardStr)
text.textBox->process_rich_text_input(*text.cursor, lastCopiedRichText.value());
else
text.textBox->process_text_input(*text.cursor, plainClipboardStr, text.modMap);
}
else
text.textBox->process_text_input(*text.cursor, plainClipboardStr, text.modMap);
#else
std::string richTextClipboard = get_clipboard_data_for_mimetype(get_infpnt_richtext_mimetype());
if(!richTextClipboard.empty())
text.textBox->process_rich_text_input(*text.cursor, RichText::TextData::deserialize_string(richTextClipboard));
else
text.textBox->process_text_input(*text.cursor, get_clipboard_data_for_mimetype("text/plain"), text.modMap);
#endif
}
}

View file

@ -199,7 +199,7 @@ struct InputManager {
std::string get_clipboard_data_for_mimetype(const std::string& mimeType);
void set_clipboard_str(std::string_view s);
void set_clipboard_plain_and_richtext_pair(const std::pair<std::string, std::string>& plainAndRichtextPair);
void set_clipboard_plain_and_richtext_pair(const std::pair<std::string, RichText::TextData>& plainAndRichtextPair);
void set_clipboard_data(const std::unordered_map<std::string, std::string>& newClipboardData);
void process_text_paste();
@ -210,6 +210,7 @@ struct InputManager {
#ifdef __EMSCRIPTEN__
bool clipboardPasteEventHappened = false;
std::unordered_map<std::string, std::string> clipboardPasteEventData;
std::optional<RichText::TextData> lastCopiedRichText;
#endif
std::string key_assignment_to_str(const Vector2ui32& k);

View file

@ -1,4 +1,5 @@
#include "TextBox.hpp"
#include "Helpers/Networking/ByteStream.hpp"
#include "TextStyleModifier.hpp"
#include "cereal/archives/portable_binary.hpp"
#include <limits>
@ -16,7 +17,7 @@
namespace RichText {
std::string TextData::get_serialized() {
std::string TextData::get_serialized() const {
std::stringstream s;
{
cereal::PortableBinaryOutputArchive a(s);
@ -54,6 +55,33 @@ void TextData::load(cereal::PortableBinaryInputArchive& a) {
}
}
TextData TextData::deserialize_string(std::string& s) {
if(s.empty()) {
RichText::TextData textData;
textData.paragraphs.emplace_back();
return textData;
}
RichText::TextData textData;
ByteMemStream ss(s.data(), s.length());
{
cereal::PortableBinaryInputArchive a(ss);
a(textData);
}
return textData;
}
std::string TextData::get_plain_text() const {
if(paragraphs.empty())
return "";
std::string toRet;
for(auto& p : paragraphs) {
toRet += p.text;
toRet += '\n';
}
toRet.pop_back();
return toRet;
}
bool TextPosition::operator<(const TextPosition& o) const {
return (this->fParagraphIndex < o.fParagraphIndex) || (this->fParagraphIndex == o.fParagraphIndex && this->fTextByteIndex < o.fTextByteIndex);
}
@ -151,11 +179,11 @@ void TextBox::process_mouse_left_button(Cursor& cur, const Vector2f& pos, bool c
}
}
std::pair<std::string, std::string> TextBox::process_copy(Cursor& cur) {
return {get_text_between(cur.selectionBeginPos, cur.selectionEndPos), get_rich_text_data_between(cur.selectionBeginPos, cur.selectionEndPos).get_serialized()};
std::pair<std::string, TextData> TextBox::process_copy(Cursor& cur) {
return {get_text_between(cur.selectionBeginPos, cur.selectionEndPos), get_rich_text_data_between(cur.selectionBeginPos, cur.selectionEndPos)};
}
std::pair<std::string, std::string> TextBox::process_cut(Cursor& cur) {
std::pair<std::string, TextData> TextBox::process_cut(Cursor& cur) {
auto toRet = process_copy(cur);
if(cur.selectionBeginPos != cur.selectionEndPos) {
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.selectionBeginPos, cur.selectionEndPos);
@ -175,17 +203,11 @@ void TextBox::process_text_input(Cursor& cur, const std::string& in, const std::
}
}
void TextBox::process_rich_text_input(Cursor& cur, const std::string& serializedRichText) {
if(!serializedRichText.empty()) {
void TextBox::process_rich_text_input(Cursor& cur, const TextData& richText) {
if(!richText.paragraphs.empty() && !(richText.paragraphs.size() == 1 && richText.paragraphs[0].text.empty())) {
if(cur.selectionBeginPos != cur.selectionEndPos)
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.selectionBeginPos, cur.selectionEndPos);
RichText::TextData textData;
std::stringstream ss(serializedRichText);
{
cereal::PortableBinaryInputArchive a(ss);
a(textData);
}
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = insert_rich_text(cur.pos, textData);
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = insert_rich_text(cur.pos, richText);
cur.previousX = std::nullopt;
inputChangedTextBox = true;
}

View file

@ -48,7 +48,9 @@ class TextData {
a(text, pStyleData);
}
};
std::string get_serialized();
std::string get_serialized() const;
static TextData deserialize_string(std::string& s);
std::string get_plain_text() const;
void save(cereal::PortableBinaryOutputArchive& a) const;
void load(cereal::PortableBinaryInputArchive& a);
TextStyleModContainer tStyleMods;
@ -133,13 +135,13 @@ class TextBox {
void process_mouse_left_button(Cursor& cur, const Vector2f& pos, bool clicked, bool held, bool shift);
void process_key_input(Cursor& cur, InputKey in, bool ctrl, bool shift, const std::optional<TextStyleModifier::ModifierMap>& inputModMap = std::nullopt);
std::pair<std::string, std::string> process_copy(Cursor& cur);
std::pair<std::string, std::string> process_cut(Cursor& cur);
std::pair<std::string, TextData> process_copy(Cursor& cur);
std::pair<std::string, TextData> process_cut(Cursor& cur);
std::string get_text_between(TextPosition p1, TextPosition p2);
std::string get_string();
void set_tab_space_width(unsigned newTabWidth);
void process_text_input(Cursor& cur, const std::string& in, const std::optional<TextStyleModifier::ModifierMap>& inputModMap = std::nullopt);
void process_rich_text_input(Cursor& cur, const std::string& serializedRichText);
void process_rich_text_input(Cursor& cur, const TextData& richText);
private:
std::pair<TextPosition, TextPosition> get_start_end_text_pos(TextPosition p1, TextPosition p2);