Enables selection of bubbles using an invisible text view
This commit is contained in:
@@ -17,10 +17,7 @@ public class TranscriptView : Adw.Bin
|
||||
_model = value;
|
||||
|
||||
if (value != null) {
|
||||
// Reset scroll position by updating the existing adjustment
|
||||
scrolled_window.vadjustment.value = 0;
|
||||
scrolled_window.vadjustment.upper = 0;
|
||||
scrolled_window.vadjustment.page_size = 0;
|
||||
reset_for_conversation_change();
|
||||
|
||||
weak TranscriptView self = this;
|
||||
messages_changed_handler_id = value.messages_changed.connect(() => {
|
||||
@@ -44,11 +41,18 @@ public class TranscriptView : Adw.Bin
|
||||
private Adw.ToolbarView container;
|
||||
private Label title_label = new Label("Messages");
|
||||
|
||||
private Overlay overlay = new Overlay();
|
||||
private TranscriptDrawingArea transcript_drawing_area = new TranscriptDrawingArea();
|
||||
private ScrolledWindow scrolled_window = new ScrolledWindow();
|
||||
private ulong messages_changed_handler_id = 0;
|
||||
private bool needs_reload = false;
|
||||
|
||||
private Graphene.Point _hovering_text_view_origin = Graphene.Point() { x = 0, y = 0 };
|
||||
private TextView _hovering_text_view = new TextView();
|
||||
|
||||
private VisibleTextLayout? hovered_text_bubble = null;
|
||||
private VisibleTextLayout? locked_text_bubble = null;
|
||||
|
||||
public TranscriptView() {
|
||||
container = new Adw.ToolbarView();
|
||||
set_child(container);
|
||||
@@ -56,8 +60,26 @@ public class TranscriptView : Adw.Bin
|
||||
// Set minimum width for the transcript view
|
||||
set_size_request(330, -1);
|
||||
|
||||
scrolled_window.set_child(transcript_drawing_area);
|
||||
scrolled_window.add_css_class("message-list-scroller");
|
||||
overlay.set_child(transcript_drawing_area);
|
||||
|
||||
weak TranscriptView self = this;
|
||||
overlay.get_child_position.connect((child, out allocation) => {
|
||||
allocation = Gtk.Allocation() { x = 0, y = 0, width = 0, height = 0 };
|
||||
if (self.hovered_text_bubble != null) {
|
||||
var rect = self.hovered_text_bubble.rect;
|
||||
allocation.x = (int)(rect.origin.x + self._hovering_text_view_origin.x);
|
||||
allocation.y = (int)(rect.origin.y - self._hovering_text_view_origin.y);
|
||||
allocation.width = (int)rect.size.width;
|
||||
allocation.height = (int)rect.size.height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
scrolled_window.set_child(overlay);
|
||||
scrolled_window.add_css_class("flipped-y-axis");
|
||||
transcript_drawing_area.viewport = scrolled_window.vadjustment;
|
||||
container.set_content(scrolled_window);
|
||||
|
||||
@@ -72,6 +94,33 @@ public class TranscriptView : Adw.Bin
|
||||
header_bar.set_title_widget(title_label);
|
||||
container.add_top_bar(header_bar);
|
||||
|
||||
// This is an invisible text view that's used to handle selection.
|
||||
_hovering_text_view.add_css_class("hovering-text-view");
|
||||
_hovering_text_view.add_css_class("flipped-y-axis");
|
||||
_hovering_text_view.set_editable(false);
|
||||
_hovering_text_view.set_wrap_mode(Gtk.WrapMode.WORD_CHAR);
|
||||
overlay.add_overlay(_hovering_text_view);
|
||||
|
||||
// When the selection changes, lock the text bubble so that if the cursor moves to another bubble we don't clear it.
|
||||
_hovering_text_view.buffer.mark_set.connect((location, end) => {
|
||||
self.lock_text_bubble(self.hovered_text_bubble);
|
||||
});
|
||||
|
||||
// When the mouse hovers over a text bubble, configure the hovering text view to show the text of the bubble.
|
||||
transcript_drawing_area.on_text_bubble_hover.connect((visible_text_layout) => {
|
||||
if (visible_text_layout != null) {
|
||||
configure_hovering_text_view(visible_text_layout);
|
||||
}
|
||||
});
|
||||
|
||||
// This is triggered when another bubble is currently locked, and the user clicks on a new bubble.
|
||||
transcript_drawing_area.on_text_bubble_click.connect((visible_text_layout) => {
|
||||
if (visible_text_layout != null) {
|
||||
locked_text_bubble = null;
|
||||
configure_hovering_text_view(visible_text_layout);
|
||||
}
|
||||
});
|
||||
|
||||
Repository.get_instance().attachment_downloaded.connect((attachment_guid) => {
|
||||
debug("Attachment downloaded: %s", attachment_guid);
|
||||
|
||||
@@ -103,6 +152,35 @@ public class TranscriptView : Adw.Bin
|
||||
});
|
||||
}
|
||||
|
||||
private void reset_for_conversation_change() {
|
||||
locked_text_bubble = null;
|
||||
hovered_text_bubble = null;
|
||||
_hovering_text_view.buffer.text = "";
|
||||
overlay.queue_allocate();
|
||||
|
||||
// Reset scroll position by updating the existing adjustment
|
||||
scrolled_window.vadjustment.value = 0;
|
||||
scrolled_window.vadjustment.upper = 0;
|
||||
scrolled_window.vadjustment.page_size = 0;
|
||||
}
|
||||
|
||||
private void lock_text_bubble(VisibleTextLayout? visible_text_layout) {
|
||||
if (visible_text_layout != null) {
|
||||
locked_text_bubble = visible_text_layout;
|
||||
configure_hovering_text_view(visible_text_layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void configure_hovering_text_view(VisibleTextLayout? visible_text_layout) {
|
||||
hovered_text_bubble = visible_text_layout;
|
||||
|
||||
if (locked_text_bubble == null) {
|
||||
_hovering_text_view_origin = visible_text_layout.text_bubble.get_text_origin();
|
||||
_hovering_text_view.buffer.text = visible_text_layout.text_bubble.message.text;
|
||||
overlay.queue_allocate();
|
||||
}
|
||||
}
|
||||
|
||||
private void reload_messages() {
|
||||
transcript_drawing_area.show_sender = _model.is_group_chat;
|
||||
transcript_drawing_area.set_messages(_model.messages);
|
||||
|
||||
Reference in New Issue
Block a user