aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-01-27 16:23:26 +0100
committerdec05eba <dec05eba@protonmail.com>2020-08-19 00:14:44 +0200
commit61c3ec1e6e8f26f6d1efc271794e791ecde8f1e2 (patch)
treeaaa7d7c88f20b5972cf85514ea5c5990ea1a6a9c
parenta089da2e943ee4e0237a370b9372f232970c22b3 (diff)
Use rich text for messages
-rw-r--r--css/style.css4
-rw-r--r--include/ChatMessage.hpp8
-rw-r--r--src/ChatMessage.cpp63
-rw-r--r--src/ChatWindow.cpp16
-rw-r--r--tests/main.cpp36
5 files changed, 82 insertions, 45 deletions
diff --git a/css/style.css b/css/style.css
index a75dce4..e94571b 100644
--- a/css/style.css
+++ b/css/style.css
@@ -237,6 +237,10 @@ treeview {
font-size: 13px;
}
+.message-message-text text {
+ background-color: #36393e;
+}
+
textview {
caret-color: #f7f7f7;
}
diff --git a/include/ChatMessage.hpp b/include/ChatMessage.hpp
index a50a799..9f628c0 100644
--- a/include/ChatMessage.hpp
+++ b/include/ChatMessage.hpp
@@ -4,17 +4,23 @@
#include <dchat/types.hpp>
#include <gtkmm/grid.h>
#include <gtkmm/label.h>
+#include <gtkmm/textview.h>
namespace dchat
{
+ void applyRichText(Gtk::TextView *textView, const Glib::ustring &text);
+
class ChatMessage : public Gtk::Grid
{
public:
ChatMessage(const Glib::ustring &username, const Glib::ustring &text, uint32_t timestampSeconds);
+ void appendText(const Glib::ustring &text);
+
DynamicImage avatar;
Gtk::Label username;
- Gtk::Label text;
uint32_t timestampSeconds;
+ private:
+ Gtk::TextView text;
};
}
diff --git a/src/ChatMessage.cpp b/src/ChatMessage.cpp
index e1fe927..e440409 100644
--- a/src/ChatMessage.cpp
+++ b/src/ChatMessage.cpp
@@ -1,10 +1,46 @@
#include "../include/ChatMessage.hpp"
+#include <dchat/IncomingMessage.hpp>
+#include <assert.h>
namespace dchat
{
+ static void appendRichText(Gtk::TextView *textView, Glib::RefPtr<Gtk::TextBuffer> buffer, Gtk::TextIter iter, const Glib::ustring &text)
+ {
+ parseIncomingMessage(text.data(), text.bytes(), [textView, &text, &iter, &buffer](IncomingMessagePart messagePart)
+ {
+ switch(messagePart.type)
+ {
+ case IncomingMessagePart::Type::TEXT:
+ {
+ iter = buffer->insert(iter, text.data() + messagePart.textRange.start, text.data() + messagePart.textRange.end);
+ break;
+ }
+ case IncomingMessagePart::Type::EMOJI:
+ {
+ auto anchor = Gtk::TextChildAnchor::create();
+ iter = buffer->insert_child_anchor(iter, anchor);
+ auto image = Gtk::manage(new DynamicImage());
+ image->url = text.substr(messagePart.textRange.start, messagePart.textRange.length());
+ image->set_size_request(35, 35);
+ textView->add_child_at_anchor(*image, anchor);
+ break;
+ }
+ default:
+ assert(false);
+ break;
+ }
+ });
+ }
+
+ void applyRichText(Gtk::TextView *textView, const Glib::ustring &text)
+ {
+ auto buffer = textView->get_buffer();
+ buffer->set_text("");
+ appendRichText(textView, buffer, buffer->begin(), text);
+ }
+
ChatMessage::ChatMessage(const Glib::ustring &_username, const Glib::ustring &_text, uint32_t _timestampSeconds) :
username(_username),
- text(_text),
timestampSeconds(_timestampSeconds)
{
avatar.set_halign(Gtk::ALIGN_START);
@@ -15,11 +51,12 @@ namespace dchat
username.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START);
username.get_style_context()->add_class("message-message-username");
- text.set_selectable(true);
- text.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START);
- text.set_line_wrap(true);
- text.set_line_wrap_mode(Pango::WRAP_WORD_CHAR);
- text.set_vexpand(true);
+ text.set_wrap_mode(Gtk::WRAP_WORD_CHAR);
+ //text.set_halign(Gtk::ALIGN_START);
+ //text.set_valign(Gtk::ALIGN_START);
+ text.set_hexpand(true);
+ text.set_editable(false);
+ applyRichText(&text, _text);
text.get_style_context()->add_class("message-message-text");
attach(avatar, 0, 0, 1, 2);
@@ -31,4 +68,18 @@ namespace dchat
set_row_spacing(0);
set_vexpand(false);
}
+
+ void ChatMessage::appendText(const Glib::ustring &_text)
+ {
+ auto buffer = text.get_buffer();
+ // Optimized for single ascii characters, such as "\n"
+ if(_text.bytes() == 1)
+ {
+ buffer->insert(buffer->end(), _text);
+ }
+ else
+ {
+ appendRichText(&text, buffer, buffer->end(), _text);
+ }
+ }
} \ No newline at end of file
diff --git a/src/ChatWindow.cpp b/src/ChatWindow.cpp
index 9fc810b..d33c824 100644
--- a/src/ChatWindow.cpp
+++ b/src/ChatWindow.cpp
@@ -303,8 +303,11 @@ namespace dchat
{
if(chatInput.get_editable())
{
- currentRoom->publishMessage(chatInput.get_buffer()->get_text());
+ Glib::ustring str = chatInput.get_buffer()->get_text();
chatInput.get_buffer()->set_text("");
+ while(gtk_events_pending())
+ gtk_main_iteration_do(FALSE);
+ currentRoom->publishMessage(str);
}
return true;
}
@@ -441,14 +444,19 @@ namespace dchat
if(msgTimeDiff <= MERGE_MESSAGE_TIMESTAMP_DIFF_SEC)
{
auto message = messageById[request.prevMessage->id];
- message->text.set_text(message->text.get_text() + "\n" + request.message->text);
+ message->appendText("\n");
+ message->appendText(request.message->text);
// Since messages that are sent withing a timeframe are combined, several message ids can refer to the same message
messageById[request.message->id] = message;
if(!request.loadedFromCache && *request.room->id == *currentRoom->id)
{
- currentRoomData->messageAreaLayout->queue_draw();
+ currentRoomData->messageAreaLayout->signal_draw();
scrollToBottom();
}
+ else
+ {
+ currentRoomData->messageAreaLayout->signal_draw();
+ }
return;
}
}
@@ -471,7 +479,7 @@ namespace dchat
// TODO: When we get a message in the current room we scroll to the bottom, but this should only be done if we are not manually scrolling to view old messages
if(!request.loadedFromCache && *request.room->id == *currentRoom->id)
{
- roomData->messageAreaLayout->queue_draw();
+ roomData->messageAreaLayout->signal_draw();
scrollToBottom();
}
}
diff --git a/tests/main.cpp b/tests/main.cpp
index 0fd291c..24cc74f 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -1,5 +1,5 @@
#include "../include/DynamicImage.hpp"
-#include <dchat/IncomingMessage.hpp>
+#include "../include/ChatMessage.hpp"
#include <stdio.h>
#include <vector>
#include <string.h>
@@ -20,38 +20,6 @@ static void requireEqualValues(int a, int b, const char *file, int line)
}
#define REQUIRE_EQUAL(a, b) do { requireEqualValues((a), (b), __FILE__, __LINE__); } while(0)
-static void applyRichText(Gtk::TextView *textView, const Glib::ustring &text)
-{
- auto buffer = textView->get_buffer();
- buffer->set_text("");
- Gtk::TextIter iter = buffer->get_iter_at_offset(0);
-
- dchat::parseIncomingMessage(text.data(), text.bytes(), [textView, &text, &iter, &buffer](dchat::IncomingMessagePart messagePart)
- {
- switch(messagePart.type)
- {
- case dchat::IncomingMessagePart::Type::TEXT:
- {
- iter = buffer->insert(iter, text.data() + messagePart.textRange.start, text.data() + messagePart.textRange.end);
- break;
- }
- case dchat::IncomingMessagePart::Type::EMOJI:
- {
- auto anchor = Gtk::TextChildAnchor::create();
- iter = buffer->insert_child_anchor(iter, anchor);
- auto image = Gtk::manage(new dchat::DynamicImage());
- image->url = text.substr(messagePart.textRange.start, messagePart.textRange.length());
- image->set_size_request(35, 35);
- textView->add_child_at_anchor(*image, anchor);
- break;
- }
- default:
- assert(false);
- break;
- }
- });
-}
-
static int testVisual(int argc, char **argv)
{
auto app = Gtk::Application::create(argc, argv, "dec05eba.dchat", Gio::APPLICATION_NON_UNIQUE);
@@ -67,7 +35,7 @@ static int testVisual(int argc, char **argv)
auto textView = Gtk::manage(new Gtk::TextView());
textView->set_size_request(640, 480);
textView->set_wrap_mode(Gtk::WRAP_WORD_CHAR);
- applyRichText(textView, "Hello world [emoji](https://discordemoji.com/assets/emoji/PeepoHide.png)[emoji](https://discordemoji.com/assets/emoji/PeepoHide.png)");
+ dchat::applyRichText(textView, "Hello world [emoji](https://discordemoji.com/assets/emoji/PeepoHide.png)[emoji](https://discordemoji.com/assets/emoji/PeepoHide.png)");
grid->attach(*textView, 0, 0, 1, 1);
window.show_all();