Move ConversationContentView+ChatInput into ConversationView, handle drag'n'drop on ConversationView level

This commit is contained in:
fiaxh 2020-02-21 02:11:23 +01:00
parent a7e92960a3
commit 28c44380ba
10 changed files with 194 additions and 128 deletions

View file

@ -43,6 +43,7 @@ set(RESOURCE_LIST
contact_details_dialog.ui
conversation_list_titlebar.ui
conversation_list_titlebar_csd.ui
conversation_view.ui
emojichooser.ui
global_search.ui
conversation_selector/chat_row_tooltip.ui
@ -97,6 +98,8 @@ SOURCES
src/ui/chat_input_controller.vala
src/ui/conversation_list_titlebar.vala
src/ui/conversation_list_titlebar_csd.vala
src/ui/conversation_view.vala
src/ui/conversation_view_controller.vala
src/ui/global_search.vala
src/ui/notifications.vala
src/ui/settings_dialog.vala

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="DinoUiConversationView" parent="GtkBox">
<property name="orientation">vertical</property>
<property name="visible">True</property>
<style>
<class name="dino-conversation"/>
</style>
<child>
<object class="GtkOverlay">
<property name="visible">True</property>
<child>
<object class="DinoUiConversationSummaryConversationView" id="conversation_frame">
<property name="visible">True</property>
</object>
</child>
<child type="overlay">
<object class="GtkRevealer" id="goto_end_revealer">
<property name="halign">end</property>
<property name="valign">end</property>
<property name="transition-type">crossfade</property>
<property name="visible">True</property>
<property name="margin-end">30</property>
<property name="margin-bottom">30</property>
<child>
<object class="GtkButton" id="goto_end_button">
<property name="vexpand">False</property>
<property name="halign">end</property>
<property name="valign">end</property>
<property name="visible">True</property>
<style>
<class name="circular"/>
</style>
<child>
<object class="GtkImage">
<property name="icon-name">go-down-symbolic</property>
<property name="icon-size">1</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="DinoUiChatInputView" id="chat_input">
<property name="visible">True</property>
</object>
</child>
</template>
</interface>

View file

@ -69,55 +69,8 @@
<object class="GtkStack" id="right_stack">
<property name="visible">True</property>
<child>
<object class="GtkOverlay">
<object class="DinoUiConversationView" id="conversation_view">
<property name="visible">True</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="visible">True</property>
<style>
<class name="dino-conversation"/>
</style>
<child>
<object class="DinoUiConversationSummaryConversationView" id="conversation_frame">
<property name="visible">True</property>
</object>
</child>
<child>
<object class="DinoUiChatInputView" id="chat_input">
<property name="visible">True</property>
</object>
</child>
</object>
</child>
<child type="overlay">
<object class="GtkRevealer" id="goto_end_revealer">
<property name="halign">end</property>
<property name="valign">end</property>
<property name="transition-type">crossfade</property>
<property name="visible">True</property>
<property name="margin-end">30</property>
<property name="margin-bottom">70</property>
<child>
<object class="GtkButton" id="goto_end_button">
<property name="vexpand">False</property>
<property name="halign">end</property>
<property name="valign">end</property>
<property name="visible">True</property>
<style>
<class name="circular"/>
</style>
<child>
<object class="GtkImage">
<property name="icon-name">go-down-symbolic</property>
<property name="icon-size">1</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="name">content</property>

View file

@ -23,6 +23,8 @@ public class ChatInputController : Object {
this.status_description_label = chat_input.chat_input_status;
this.stream_interactor = stream_interactor;
chat_input.init(stream_interactor);
reset_input_field_status();
chat_input.text_input.buffer.changed.connect(on_text_input_changed);
@ -40,6 +42,7 @@ public class ChatInputController : Object {
reset_input_field_status();
chat_input.initialize_for_conversation(conversation);
chat_input.occupants_tab_completor.initialize_for_conversation(conversation);
chat_input.edit_history.initialize_for_conversation(conversation);
chat_input.encryption_widget.set_conversation(conversation);

View file

@ -56,7 +56,6 @@ public class ConversationSelector : ListBox {
if (!rows.has_key(conversation)) {
add_conversation(conversation);
}
rows[conversation].grab_focus();
this.select_row(rows[conversation]);
}
@ -86,8 +85,8 @@ public class ConversationSelector : ListBox {
if (this.drag_timeout != null)
return false;
this.drag_timeout = Timeout.add(200, () => {
if (widget.get_type().is_a(typeof(ConversationRow))) {
ConversationRow row = widget as ConversationRow;
if (widget.get_type().is_a(typeof(ConversationSelectorRow))) {
ConversationSelectorRow row = widget as ConversationSelectorRow;
conversation_selected(row.conversation);
}
this.drag_timeout = null;

View file

@ -4,15 +4,6 @@ using Pango;
using Dino.Entities;
enum Target {
URI_LIST,
STRING
}
const TargetEntry[] target_list = {
{ "text/uri-list", 0, Target.URI_LIST },
};
namespace Dino.Ui.ConversationSummary {
[GtkTemplate (ui = "/im/dino/Dino/conversation_summary/view.ui")]
@ -67,9 +58,12 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
return true;
});
drag_dest_unset(main);
drag_dest_set(scrolled, DestDefaults.ALL, target_list, Gdk.DragAction.COPY);
scrolled.drag_data_received.connect(this.on_drag_data_received);
const TargetEntry[] target_list = {
{ "text/uri-list", 0, Target.URI_LIST }
};
// drag_dest_unset(main);
// drag_dest_set(scrolled, DestDefaults.ALL, target_list, Gdk.DragAction.COPY);
// scrolled.drag_data_received.connect(() => print("a\n"));
return this;
}
@ -185,28 +179,6 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
do_insert_item(item);
}
public void on_drag_data_received(Widget widget, Gdk.DragContext context,
int x, int y,
SelectionData selection_data,
uint target_type, uint time) {
if ((selection_data != null) && (selection_data.get_length() >= 0)) {
switch (target_type) {
case Target.URI_LIST:
string[] uris = selection_data.get_uris();
for (int i = 0; i < uris.length; i++) {
try {
string filename = Filename.from_uri(uris[i]);
stream_interactor.get_module(FileManager.IDENTITY).send_file(filename, conversation);
} catch (Error err) {
}
}
break;
default:
break;
}
}
}
public void do_insert_item(Plugins.MetaConversationItem item) {
lock (meta_items) {
insert_new(item);

View file

@ -0,0 +1,19 @@
using Gee;
using Gdk;
using Gtk;
using Dino.Entities;
namespace Dino.Ui {
[GtkTemplate (ui = "/im/dino/Dino/conversation_view.ui")]
public class ConversationView : Gtk.Box {
[GtkChild] public Revealer goto_end_revealer;
[GtkChild] public Button goto_end_button;
[GtkChild] public ChatInput.View chat_input;
[GtkChild] public ConversationSummary.ConversationView conversation_frame;
}
}

View file

@ -0,0 +1,96 @@
using Gee;
using Gdk;
using Gtk;
using Dino.Entities;
namespace Dino.Ui {
enum Target {
URI_LIST,
STRING
}
const TargetEntry[] target_list = {
{ "text/uri-list", 0, Target.URI_LIST }
};
public class ConversationViewController {
private ConversationView widget;
private ChatInputController chat_input_controller;
private StreamInteractor stream_interactor;
private Conversation? conversation;
public ConversationViewController(ConversationView widget, StreamInteractor stream_interactor) {
this.widget = widget;
this.stream_interactor = stream_interactor;
this.chat_input_controller = new ChatInputController(widget.chat_input, stream_interactor);
widget.conversation_frame.init(stream_interactor);
// drag 'n drop file upload
Gtk.drag_dest_unset(widget.chat_input.text_input);
Gtk.drag_dest_set(widget, DestDefaults.ALL, target_list, Gdk.DragAction.COPY);
widget.drag_data_received.connect(this.on_drag_data_received);
// forward key presses
widget.chat_input.key_press_event.connect(forward_key_press_to_chat_input);
widget.conversation_frame.key_press_event.connect(forward_key_press_to_chat_input);
// goto-end floating button
var vadjustment = widget.conversation_frame.scrolled.vadjustment;
vadjustment.notify["value"].connect(() => {
widget.goto_end_revealer.reveal_child = vadjustment.value < vadjustment.upper - vadjustment.page_size;
});
widget.goto_end_button.clicked.connect(() => {
widget.conversation_frame.initialize_for_conversation(conversation);
});
}
public void select_conversation(Conversation? conversation, bool default_initialize_conversation) {
this.conversation = conversation;
chat_input_controller.set_conversation(conversation);
if (default_initialize_conversation) {
widget.conversation_frame.initialize_for_conversation(conversation);
}
}
public void on_drag_data_received(Widget widget, Gdk.DragContext context, int x, int y, SelectionData selection_data, uint target_type, uint time) {
if ((selection_data != null) && (selection_data.get_length() >= 0)) {
switch (target_type) {
case Target.URI_LIST:
string[] uris = selection_data.get_uris();
for (int i = 0; i < uris.length; i++) {
try {
string filename = Filename.from_uri(uris[i]);
stream_interactor.get_module(FileManager.IDENTITY).send_file.begin(filename, conversation);
} catch (Error err) {}
}
break;
default:
break;
}
}
}
public bool forward_key_press_to_chat_input(EventKey event) {
// Don't forward / change focus on Control / Alt
if (event.keyval == Gdk.Key.Control_L || event.keyval == Gdk.Key.Control_R ||
event.keyval == Gdk.Key.Alt_L || event.keyval == Gdk.Key.Alt_R) {
return false;
}
// Don't forward / change focus on Control + ...
if ((event.state & ModifierType.CONTROL_MASK) > 0) {
return false;
}
widget.chat_input.text_input.key_press_event(event);
widget.chat_input.text_input.grab_focus();
return true;
}
}
}

View file

@ -16,9 +16,8 @@ public class UnifiedWindow : Gtk.Window {
public WelcomePlceholder welcome_placeholder = new WelcomePlceholder() { visible=true };
public NoAccountsPlaceholder accounts_placeholder = new NoAccountsPlaceholder() { visible=true };
public NoConversationsPlaceholder conversations_placeholder = new NoConversationsPlaceholder() { visible=true };
public ChatInput.View chat_input;
public ConversationView conversation_view;
public ConversationSelector conversation_selector;
public ConversationSummary.ConversationView conversation_frame;
public ConversationTitlebar conversation_titlebar;
public ConversationTitlebarCsd conversation_titlebar_csd;
public ConversationListTitlebarCsd conversation_list_titlebar_csd;
@ -26,8 +25,6 @@ public class UnifiedWindow : Gtk.Window {
public Box box = new Box(Orientation.VERTICAL, 0) { orientation=Orientation.VERTICAL, visible=true };
public Paned headerbar_paned = new Paned(Orientation.HORIZONTAL) { visible=true };
public Paned paned;
public Revealer goto_end_revealer;
public Button goto_end_button;
public Revealer search_revealer;
public SearchEntry search_entry;
public GlobalSearch search_box;
@ -72,13 +69,8 @@ public class UnifiedWindow : Gtk.Window {
box.add(paned);
left_stack = (Stack) builder.get_object("left_stack");
right_stack = (Stack) builder.get_object("right_stack");
chat_input = ((ChatInput.View) builder.get_object("chat_input")).init(stream_interactor);
chat_input.key_press_event.connect(forward_key_press_to_chat_input);
conversation_frame = ((ConversationSummary.ConversationView) builder.get_object("conversation_frame")).init(stream_interactor);
conversation_frame.key_press_event.connect(forward_key_press_to_chat_input);
conversation_view = (ConversationView) builder.get_object("conversation_view");
conversation_selector = ((ConversationSelector) builder.get_object("conversation_list")).init(stream_interactor);
goto_end_revealer = (Revealer) builder.get_object("goto_end_revealer");
goto_end_button = (Button) builder.get_object("goto_end_button");
search_box = ((GlobalSearch) builder.get_object("search_box")).init(stream_interactor);
search_revealer = (Revealer) builder.get_object("search_revealer");
search_entry = (SearchEntry) builder.get_object("search_entry");
@ -103,7 +95,7 @@ public class UnifiedWindow : Gtk.Window {
box.add(headerbar_paned);
}
headerbar_paned.key_press_event.connect(forward_key_press_to_chat_input);
// headerbar_paned.key_press_event.connect(forward_key_press_to_chat_input); TODO
}
private void set_window_buttons() {
@ -153,21 +145,6 @@ public class UnifiedWindow : Gtk.Window {
}
}
private bool forward_key_press_to_chat_input(EventKey event) {
// Don't forward / change focus on Control / Alt
if (event.keyval == Gdk.Key.Control_L || event.keyval == Gdk.Key.Control_R ||
event.keyval == Gdk.Key.Alt_L || event.keyval == Gdk.Key.Alt_R) {
return false;
}
// Don't forward / change focus on Control + ...
if ((event.state & ModifierType.CONTROL_MASK) > 0) {
return false;
}
chat_input.text_input.key_press_event(event);
chat_input.text_input.grab_focus();
return true;
}
public void loop_conversations(bool backwards) {
conversation_selector.loop_conversations(backwards);
}

View file

@ -19,7 +19,7 @@ public class UnifiedWindowController : Object {
private SearchMenuEntry search_menu_entry = new SearchMenuEntry();
private ChatInputController chat_input_controller;
private ConversationViewController conversation_view_controller;
public UnifiedWindowController(Application application, StreamInteractor stream_interactor, Database db) {
this.app = application;
@ -54,18 +54,15 @@ public class UnifiedWindowController : Object {
public void set_window(UnifiedWindow window) {
this.window = window;
this.chat_input_controller = new ChatInputController(window.chat_input, stream_interactor);
this.conversation_view_controller = new ConversationViewController(window.conversation_view, stream_interactor);
this.bind_property("conversation-display-name", window, "title");
this.bind_property("conversation-topic", window, "subtitle");
search_menu_entry.search_button.bind_property("active", window.search_revealer, "reveal_child");
window.goto_end_button.clicked.connect(() => {
window.conversation_frame.initialize_for_conversation(conversation);
});
window.search_revealer.notify["child-revealed"].connect(() => {
if (window.search_revealer.child_revealed) {
if (window.conversation_frame.conversation != null && window.search_box.search_entry.text == "") {
if (window.conversation_view.conversation_frame.conversation != null && window.search_box.search_entry.text == "") {
reset_search_entry();
}
window.search_box.search_entry.grab_focus_without_selecting();
@ -74,7 +71,7 @@ public class UnifiedWindowController : Object {
});
window.search_box.selected_item.connect((item) => {
select_conversation(item.conversation, false, false);
window.conversation_frame.initialize_around_message(item.conversation, item);
window.conversation_view.conversation_frame.initialize_around_message(item.conversation, item);
close_search();
});
@ -88,10 +85,6 @@ public class UnifiedWindowController : Object {
window.conversations_placeholder.secondary_button.clicked.connect(() => { app.activate_action("add_conference", null); });
window.conversation_selector.conversation_selected.connect((conversation) => select_conversation(conversation));
var vadjustment = window.conversation_frame.scrolled.vadjustment;
vadjustment.notify["value"].connect(() => {
window.goto_end_revealer.reveal_child = vadjustment.value < vadjustment.upper - vadjustment.page_size;
});
window.event.connect((event) => {
if (event.type == EventType.BUTTON_PRESS) {
int dest_x, dest_y;
@ -124,6 +117,8 @@ public class UnifiedWindowController : Object {
public void select_conversation(Conversation? conversation, bool do_reset_search = true, bool default_initialize_conversation = true) {
this.conversation = conversation;
conversation_view_controller.select_conversation(conversation, default_initialize_conversation);
update_conversation_display_name();
update_conversation_topic();
@ -141,11 +136,6 @@ public class UnifiedWindowController : Object {
if (do_reset_search) {
reset_search_entry();
}
chat_input_controller.set_conversation(conversation);
window.chat_input.initialize_for_conversation(conversation);
if (default_initialize_conversation) {
window.conversation_frame.initialize_for_conversation(conversation);
}
}
private void check_unset_conversation() {
@ -188,7 +178,7 @@ public class UnifiedWindowController : Object {
}
private void reset_search_entry() {
if (window.conversation_frame.conversation != null) {
if (window.conversation_view.conversation_frame.conversation != null) {
switch (conversation.type_) {
case Conversation.Type.CHAT:
case Conversation.Type.GROUPCHAT_PM: