aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main/data/conversation_content_view/view.ui1
-rw-r--r--main/data/style.css18
-rw-r--r--main/src/ui/conversation_content_view/code_block_widget.vala1
-rw-r--r--main/src/ui/conversation_content_view/conversation_view.vala21
-rw-r--r--main/src/ui/conversation_content_view/message_widget.vala40
5 files changed, 77 insertions, 4 deletions
diff --git a/main/data/conversation_content_view/view.ui b/main/data/conversation_content_view/view.ui
index c5ee7a92..24ce5476 100644
--- a/main/data/conversation_content_view/view.ui
+++ b/main/data/conversation_content_view/view.ui
@@ -31,6 +31,7 @@
<class name="card"/>
<class name="toolbar"/>
<class name="compact-card-toolbar"/>
+ <class name="message-actions"/>
</style>
</object>
</child>
diff --git a/main/data/style.css b/main/data/style.css
index 9e6af19b..6377d2c2 100644
--- a/main/data/style.css
+++ b/main/data/style.css
@@ -237,6 +237,24 @@ picture.avatar {
margin-bottom: 0;
}
+window.dino-main .message-actions {
+ padding: 1px;
+ border-radius: 999em;
+}
+
+window.dino-main .message-actions > button,
+window.dino-main .message-actions > menubutton > button {
+ min-width: 28px;
+ min-height: 28px;
+ margin: 0;
+ padding: 0;
+ border-radius: 999em;
+}
+
+window.dino-main .message-text {
+ caret-color: transparent;
+}
+
/* File Widget */
window.dino-main .file-box-outer,
diff --git a/main/src/ui/conversation_content_view/code_block_widget.vala b/main/src/ui/conversation_content_view/code_block_widget.vala
index dc1ecc1f..3b24c944 100644
--- a/main/src/ui/conversation_content_view/code_block_widget.vala
+++ b/main/src/ui/conversation_content_view/code_block_widget.vala
@@ -129,6 +129,7 @@ public class CodeBlockWidget : Box {
yalign=0,
wrap=false
};
+ code_label.add_css_class("message-text");
code_label.add_css_class("code-block-content");
append(code_label);
diff --git a/main/src/ui/conversation_content_view/conversation_view.vala b/main/src/ui/conversation_content_view/conversation_view.vala
index 6a686f1b..83e86827 100644
--- a/main/src/ui/conversation_content_view/conversation_view.vala
+++ b/main/src/ui/conversation_content_view/conversation_view.vala
@@ -55,6 +55,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
Widget currently_highlighted = null;
ContentMetaItem? current_meta_item = null;
double last_y = -1;
+ private bool message_text_selectable = true;
private Button create_action_button(string action_name) {
var button = new Button() { name=action_name };
@@ -82,6 +83,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
create_action_button("reply");
create_action_button("delete");
+ create_action_button("copy");
var menu_button = new MenuButton() { name="menu", tooltip_text=_("More actions") };
action_buttons["menu"] = menu_button;
@@ -127,7 +129,10 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
// reaches the overlapping part of a button.
EventControllerMotion main_motion_events = new EventControllerMotion();
main.add_controller(main_motion_events);
- main_motion_events.motion.connect(update_highlight);
+ main_motion_events.motion.connect((x, y) => {
+ set_message_text_selectable(true);
+ update_highlight(x, y);
+ });
// Process touch events and capture phase to allow highlighting a message without cursor
GestureClick click_controller = new GestureClick();
@@ -135,12 +140,23 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
click_controller.propagation_phase = Gtk.PropagationPhase.CAPTURE;
main_wrap_box.add_controller(click_controller);
click_controller.pressed.connect_after((n, x, y) => {
+ set_message_text_selectable(false);
update_highlight(x, y);
});
return this;
}
+ private void set_message_text_selectable(bool selectable) {
+ if (message_text_selectable == selectable) return;
+
+ message_text_selectable = selectable;
+ foreach (Plugins.MetaConversationItem item in meta_items) {
+ MessageMetaItem? message_item = item as MessageMetaItem;
+ if (message_item != null) message_item.set_text_selectable(selectable);
+ }
+ }
+
public void activate_last_message_correction() {
Gee.BidirIterator<Plugins.MetaConversationItem> iter = content_items.bidir_iterator();
iter.last();
@@ -578,6 +594,9 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
private Widget insert_new(Plugins.MetaConversationItem item) {
Plugins.MetaConversationItem? lower_item = meta_items.lower(item);
+ MessageMetaItem? message_item = item as MessageMetaItem;
+ if (message_item != null) message_item.set_text_selectable(message_text_selectable);
+
// Fill datastructure
ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item);
item_item_skeletons[item] = item_skeleton;
diff --git a/main/src/ui/conversation_content_view/message_widget.vala b/main/src/ui/conversation_content_view/message_widget.vala
index f8db1a45..c5a9c145 100644
--- a/main/src/ui/conversation_content_view/message_widget.vala
+++ b/main/src/ui/conversation_content_view/message_widget.vala
@@ -31,12 +31,15 @@ public class MessageMetaItem : ContentMetaItem {
public Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, vexpand=true };
private Widget? content_widget;
+ private bool text_selectable = true;
public MessageMetaItem(ContentItem content_item, StreamInteractor stream_interactor) {
base(content_item);
message_item = content_item as MessageItem;
this.stream_interactor = stream_interactor;
+ label.add_css_class("message-text");
+
stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.connect(on_updated_item);
stream_interactor.get_module(MessageDeletion.IDENTITY).item_deleted.connect(on_updated_item);
@@ -111,12 +114,12 @@ public class MessageMetaItem : ContentMetaItem {
return markups;
}
- private string prepare_message(ArrayList<Xep.MessageMarkup.Span> markups, out int action_name_length) {
+ private string prepare_message(ArrayList<Xep.MessageMarkup.Span> markups, out int action_name_length, bool truncate = true) {
Message message = message_item.message;
string text = message.body ?? "";
action_name_length = 0;
- if (text.char_count() > 10000) {
+ if (truncate && text.char_count() > 10000) {
text = text.substring(0, text.index_of_nth_char(10000)) + " [" + _("Message too long") + "]";
}
@@ -141,15 +144,32 @@ public class MessageMetaItem : ContentMetaItem {
var text_label = new Label("") {
use_markup=true,
xalign=0,
- selectable=true,
+ selectable=text_selectable,
wrap=true,
wrap_mode=Pango.WrapMode.WORD_CHAR,
hexpand=true
};
+ text_label.add_css_class("message-text");
text_label.activate_link.connect(on_label_activate_link);
return text_label;
}
+ public void set_text_selectable(bool selectable) {
+ text_selectable = selectable;
+ if (content_widget != null) set_text_selectable_recursive(content_widget, selectable);
+ }
+
+ private void set_text_selectable_recursive(Widget widget, bool selectable) {
+ Label? text_label = widget as Label;
+ if (text_label != null && text_label.has_css_class("message-text")) {
+ text_label.selectable = selectable;
+ }
+
+ for (Widget? child = widget.get_first_child(); child != null; child = child.get_next_sibling()) {
+ set_text_selectable_recursive(child, selectable);
+ }
+ }
+
private void add_text_attribute(AttrList attributes, Attribute attribute, string text, int source_start, int span_start, int span_end) {
int source_end = source_start + text.char_count();
int start = int.max(source_start, span_start);
@@ -286,6 +306,7 @@ public class MessageMetaItem : ContentMetaItem {
generate_markup_text(label, text, markups, action_name_length);
content_widget = label;
}
+ set_text_selectable_recursive(content_widget, text_selectable);
if (outer != null && !in_edit_mode) {
outer.set_widget(content_widget, Plugins.WidgetType.GTK4, 2);
@@ -331,6 +352,19 @@ public class MessageMetaItem : ContentMetaItem {
actions.add(action1);
}
+ Plugins.MessageAction copy_action = new Plugins.MessageAction();
+ copy_action.name = "copy";
+ copy_action.icon_name = "dino-edit-copy-symbolic";
+ copy_action.tooltip = _("Copy message");
+ copy_action.callback = () => {
+ var markups = copy_markups(message_item.message);
+ int action_name_length;
+ string text = prepare_message(markups, out action_name_length, false);
+ Gdk.Display? display = Gdk.Display.get_default();
+ if (display != null) display.get_clipboard().set_text(text);
+ };
+ actions.add(copy_action);
+
actions.add(get_reply_action(content_item, message_item.conversation, stream_interactor));
actions.add(get_reaction_action(content_item, message_item.conversation, stream_interactor));