diff options
Diffstat (limited to 'main')
| -rw-r--r-- | main/data/conversation_content_view/view.ui | 1 | ||||
| -rw-r--r-- | main/data/style.css | 18 | ||||
| -rw-r--r-- | main/src/ui/conversation_content_view/code_block_widget.vala | 1 | ||||
| -rw-r--r-- | main/src/ui/conversation_content_view/conversation_view.vala | 21 | ||||
| -rw-r--r-- | main/src/ui/conversation_content_view/message_widget.vala | 40 |
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)); |
