diff options
author | ouwou <26526779+ouwou@users.noreply.github.com> | 2020-10-24 19:42:06 -0400 |
---|---|---|
committer | ouwou <26526779+ouwou@users.noreply.github.com> | 2020-10-24 19:42:06 -0400 |
commit | 7c31190adcf2dd7c45114f7c0e7e87825f106744 (patch) | |
tree | 5312d9ded299d0ba80cbfe5058733a8d626c7d5c /components | |
parent | cb73bba135933357878dc47f6e7a6bec3168d001 (diff) | |
download | abaddon-portaudio-7c31190adcf2dd7c45114f7c0e7e87825f106744.tar.gz abaddon-portaudio-7c31190adcf2dd7c45114f7c0e7e87825f106744.zip |
render emojis
Diffstat (limited to 'components')
-rw-r--r-- | components/chatmessage.cpp | 109 | ||||
-rw-r--r-- | components/chatmessage.hpp | 5 |
2 files changed, 114 insertions, 0 deletions
diff --git a/components/chatmessage.cpp b/components/chatmessage.cpp index d15eef8..4e7dcb3 100644 --- a/components/chatmessage.cpp +++ b/components/chatmessage.cpp @@ -1,6 +1,7 @@ #include "chatmessage.hpp" #include "../abaddon.hpp" #include "../util.hpp" +#include <unordered_map> ChatMessageItemContainer::ChatMessageItemContainer() { m_main = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); @@ -178,6 +179,7 @@ void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) { HandleUserMentions(tv); HandleLinks(tv); HandleChannelMentions(tv); + HandleEmojis(tv); break; case MessageType::GUILD_MEMBER_JOIN: b->insert_markup(s, "<span color='#999999'><i>[user joined]</i></span>"); @@ -367,6 +369,22 @@ void ChatMessageItemContainer::HandleImage(const AttachmentData &data, Gtk::Imag Glib::signal_idle().connect(sigc::bind(sigc::mem_fun(*this, &ChatMessageItemContainer::EmitImageLoad), url)); } +Glib::ustring ChatMessageItemContainer::GetTextFiltered(const Glib::RefPtr<Gtk::TextBuffer> &buf) { + Gtk::TextBuffer::iterator a, b; + buf->get_bounds(a, b); + auto slice = buf->get_slice(a, b, true); + // make all the 0xFFFC chars fuck off because it breaks regex + while (true) { + int r = slice.find(u8"\uFFFC"); + if (r == Glib::ustring::npos) break; + auto iter = buf->get_iter_at_offset(r); + auto len = Glib::ustring(u8"\uFFFC").size(); + slice.erase(r, len); + slice.insert(r, "\x80"); + } + return slice; +} + void ChatMessageItemContainer::HandleUserMentions(Gtk::TextView *tv) { constexpr static const auto mentions_regex = R"(<@!?(\d+)>)"; @@ -412,6 +430,97 @@ void ChatMessageItemContainer::HandleUserMentions(Gtk::TextView *tv) { } } +void ChatMessageItemContainer::HandleStockEmojis(Gtk::TextView *tv) { + auto get_text = [&]() -> Glib::ustring { + auto x = tv->get_buffer(); + Gtk::TextBuffer::iterator a, b; + x->get_bounds(a, b); + return x->get_slice(a, b, true); + }; + + auto buf = tv->get_buffer(); + auto text = get_text(); + + auto &emojis = Abaddon::Get().GetEmojis(); + int searchpos; + Glib::MatchInfo match; + for (const auto &pattern : emojis.GetPatterns()) { + searchpos = 0; + Glib::RefPtr<Gdk::Pixbuf> pixbuf; + while (true) { + size_t r = text.find(pattern, searchpos); + if (r == Glib::ustring::npos) break; + if (!pixbuf) pixbuf = emojis.GetPixBuf(pattern); + if (!pixbuf) break; + searchpos = r + pattern.size(); + auto start_it = buf->get_iter_at_offset(r); + auto end_it = buf->get_iter_at_offset(r + pattern.size()); + auto it = buf->erase(start_it, end_it); + buf->insert_pixbuf(it, pixbuf->scale_simple(24, 24, Gdk::INTERP_BILINEAR)); + int alen = text.size(); + text = get_text(); + int blen = text.size(); + searchpos -= (alen - blen); + } + } +} + +void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView *tv) { + static auto rgx = Glib::Regex::create(R"(<a?:([\w\d_]+):(\d+)>)"); + + auto &img = Abaddon::Get().GetImageManager(); + + auto buf = tv->get_buffer(); + auto text = GetTextFiltered(buf); + + Glib::MatchInfo match; + int startpos = 0; + while (rgx->match(text, startpos, match)) { + int mstart, mend; + if (!match.fetch_pos(0, mstart, mend)) break; + auto start_it = buf->get_iter_at_offset(mstart); + auto end_it = buf->get_iter_at_offset(mend); + startpos = mend; + auto pixbuf = img.GetFromURLIfCached(Emoji::URLFromID(match.fetch(2))); + if (pixbuf) { + auto it = buf->erase(start_it, end_it); + int alen = text.size(); + text = GetTextFiltered(buf); + int blen = text.size(); + startpos -= (alen - blen); + buf->insert_pixbuf(it, pixbuf->scale_simple(24, 24, Gdk::INTERP_BILINEAR)); + } else { + // clang-format off + // can't erase before pixbuf is ready or else marks that are in the same pos get mixed up + auto mark_start = buf->create_mark(start_it, false); + end_it.backward_char(); + auto mark_end = buf->create_mark(end_it, false); + img.LoadFromURL(Emoji::URLFromID(match.fetch(2)), sigc::track_obj([this, buf, mark_start, mark_end](Glib::RefPtr<Gdk::Pixbuf> pixbuf) { + auto start_it = mark_start->get_iter(); + auto end_it = mark_end->get_iter(); + end_it.forward_char(); + buf->delete_mark(mark_start); + buf->delete_mark(mark_end); + auto it = buf->erase(start_it, end_it); + buf->insert_pixbuf(it, pixbuf->scale_simple(24, 24, Gdk::INTERP_BILINEAR)); + }, tv)); + // clang-format on + } + + int alen = text.size(); + text = GetTextFiltered(buf); + int blen = text.size(); + } +} + +void ChatMessageItemContainer::HandleEmojis(Gtk::TextView *tv) { + static bool emojis = Abaddon::Get().GetSettings().GetSettingString("gui", "emojis", "true") != "false"; + if (emojis) { + HandleStockEmojis(tv); + HandleCustomEmojis(tv); + } +} + void ChatMessageItemContainer::HandleChannelMentions(Gtk::TextView *tv) { constexpr static const auto chan_regex = R"(<#(\d+)>)"; diff --git a/components/chatmessage.hpp b/components/chatmessage.hpp index 959a8d6..cd07fce 100644 --- a/components/chatmessage.hpp +++ b/components/chatmessage.hpp @@ -26,7 +26,12 @@ protected: Gtk::Box *CreateAttachmentComponent(const AttachmentData &data); // non-image attachments void HandleImage(const AttachmentData &data, Gtk::Image *img, std::string url); + static Glib::ustring GetTextFiltered(const Glib::RefPtr<Gtk::TextBuffer> &buf); + void HandleUserMentions(Gtk::TextView *tv); + void HandleStockEmojis(Gtk::TextView *tv); + void HandleCustomEmojis(Gtk::TextView *tv); + void HandleEmojis(Gtk::TextView *tv); void HandleChannelMentions(Gtk::TextView *tv); bool OnClickChannel(GdkEventButton *ev); |