diff --git a/briar-gtk/briar_gtk/controllers/__init__.py b/briar-gtk/briar_gtk/controllers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fb84ada4bc313ecfdf8c809be769ebdc9d2ba4f5 --- /dev/null +++ b/briar-gtk/briar_gtk/controllers/__init__.py @@ -0,0 +1,6 @@ +# Copyright (c) 2020 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md +""" +Controllers reacting to user input in Briar GTK +""" diff --git a/briar-gtk/briar_gtk/controllers/chat_menu.py b/briar-gtk/briar_gtk/controllers/chat_menu.py new file mode 100644 index 0000000000000000000000000000000000000000..738fcd489854e760d2b6e0a0495ae459a889cc83 --- /dev/null +++ b/briar-gtk/briar_gtk/controllers/chat_menu.py @@ -0,0 +1,9 @@ +# Copyright (c) 2020 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md + + +class ChatMenuController(): + + def __init__(self, chat_menu_view, api): + pass diff --git a/briar-gtk/briar_gtk/controllers/main_menu.py b/briar-gtk/briar_gtk/controllers/main_menu.py new file mode 100644 index 0000000000000000000000000000000000000000..3b910004db853247edd238768337f59cdab1cf54 --- /dev/null +++ b/briar-gtk/briar_gtk/controllers/main_menu.py @@ -0,0 +1,9 @@ +# Copyright (c) 2020 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md + + +class MainMenuController(): + + def __init__(self, main_menu_view, api): + pass diff --git a/briar-gtk/briar_gtk/controllers/main_window.py b/briar-gtk/briar_gtk/controllers/main_window.py new file mode 100644 index 0000000000000000000000000000000000000000..4a42ebe1d06714a6dc96e7655defb3180210cc3f --- /dev/null +++ b/briar-gtk/briar_gtk/controllers/main_window.py @@ -0,0 +1,9 @@ +# Copyright (c) 2020 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md + + +class MainWindowController(): + + def __init__(self): + pass diff --git a/briar-gtk/briar_gtk/controllers/private_chat.py b/briar-gtk/briar_gtk/controllers/private_chat.py new file mode 100644 index 0000000000000000000000000000000000000000..0799e90ebed9fe1dc80dcbc0d26e1db1f3bd44f9 --- /dev/null +++ b/briar-gtk/briar_gtk/controllers/private_chat.py @@ -0,0 +1,22 @@ +# Copyright (c) 2020 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md + +from briar_gtk.controllers.chat_menu import ChatMenuController +from briar_gtk.views.chat_menu import ChatMenuView + + +class PrivateChatController(): + + def __init__(self, private_chat_view, api): + self._private_chat_view = private_chat_view + self._api = api + + self._chat_menu_view = ChatMenuView() + self._chat_menu_controller = ChatMenuController( + self._chat_menu_view, api) + + self._load_content() + + def _load_content(self): + pass diff --git a/briar-gtk/briar_gtk/controllers/sidebar.py b/briar-gtk/briar_gtk/controllers/sidebar.py new file mode 100644 index 0000000000000000000000000000000000000000..042e3f74f5a38ddf698ab82b797ba42780ef9345 --- /dev/null +++ b/briar-gtk/briar_gtk/controllers/sidebar.py @@ -0,0 +1,65 @@ +# Copyright (c) 2020 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md + +from gi.repository import Gio, GLib + +from briar_wrapper.models.contacts import Contacts + +from briar_gtk.define import APP, NOTIFICATION_CONTACT_ADDED +from briar_gtk.define import NOTIFICATION_PRIVATE_MESSAGE + + +class SidebarController(): + + def __init__(self, sidebar_view, api): + self._sidebar_view = sidebar_view + self._api = api + self._signals = list() + + self._load_content() + + def refresh_contacts(self): + selected_row = self._sidebar_view.get_selected_row() + self._load_contacts(selected_row) + + def disconnect_signals(self): + for signal in self._signals: + self._api.socket_listener.disconnect(signal) + + # pylint: disable=no-member + def _load_content(self): + self._contacts = Contacts(self._api) + self._load_contacts() + socket_listener = self._api.socket_listener + self._setup_contact_added_listeners(socket_listener) + self._setup_message_received_listeners(socket_listener) + self._setup_contact_connection_listener(socket_listener) + + def _load_contacts(self, selected_row=-1): + self.contacts_list = self._contacts.get() + self._sidebar_view.show_contacts( + self.contacts_list, selected_row) + + def _setup_contact_added_listeners(self, socket_listener): + signal_id = socket_listener.connect("ContactAddedEvent", + self._refresh_contacts_async) + self._signals.append(signal_id) + + def _setup_message_received_listeners(self, socket_listener): + signal_id = socket_listener.connect("ConversationMessageReceivedEvent", + self._refresh_contacts_async) + self._signals.append(signal_id) + + def _setup_contact_connection_listener(self, socket_listener): + callback = self._refresh_contact_connection_state + signal_ids = self._contacts.watch_connections(callback) + self._signals.extend(signal_ids) + + # pylint: disable=unused-argument + def _refresh_contacts_async(self, message): + GLib.idle_add(self.refresh_contacts) + + # pylint: disable=unused-argument + def _refresh_contact_connection_state(self, contact_id, connected): + self._refresh_contacts_async(None) diff --git a/briar-gtk/briar_gtk/views/chat_menu.py b/briar-gtk/briar_gtk/views/chat_menu.py new file mode 100644 index 0000000000000000000000000000000000000000..d2c9f427804a043f158a0481df1817c74083522c --- /dev/null +++ b/briar-gtk/briar_gtk/views/chat_menu.py @@ -0,0 +1,27 @@ +# Copyright (c) 2020 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md + +import os + +from gi.repository import Gtk + +from briar_gtk.define import RESOURCES_DIR + + +class ChatMenuView(): + + CHAT_MENU_UI = "chat_menu.ui" + + def __init__(self): + self._builder = Gtk.Builder() + + def _add_from_resource(self, ui_filename): + self._builder.add_from_resource( + os.path.join(RESOURCES_DIR, ui_filename) + ) + + def _setup_view(self): + self._add_from_resource(self.CHAT_MENU_UI) + self._builder.connect_signals(self) + self._builder.get_object("chat_menu_button").hide() diff --git a/briar-gtk/briar_gtk/views/main_menu.py b/briar-gtk/briar_gtk/views/main_menu.py new file mode 100644 index 0000000000000000000000000000000000000000..020451bd0682a7074444874c7e97559fb6acf573 --- /dev/null +++ b/briar-gtk/briar_gtk/views/main_menu.py @@ -0,0 +1,26 @@ +# Copyright (c) 2020 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md + +import os + +from gi.repository import Gtk + +from briar_gtk.define import RESOURCES_DIR + + +class MainMenuView(): + + MAIN_MENU_UI = "main_menu.ui" + + def __init__(self): + self._builder = Gtk.Builder() + + def _add_from_resource(self, ui_filename): + self._builder.add_from_resource( + os.path.join(RESOURCES_DIR, ui_filename) + ) + + def _setup_view(self): + self._add_from_resource(self.MAIN_MENU_UI) + self._builder.connect_signals(self) diff --git a/briar-gtk/briar_gtk/views/main_window.py b/briar-gtk/briar_gtk/views/main_window.py index b35831bc5d08cbf6e7b0805ead15bc86df9a3f36..8019f0d59c55c0d95f46b3de9da023e8187f0cd0 100644 --- a/briar-gtk/briar_gtk/views/main_window.py +++ b/briar-gtk/briar_gtk/views/main_window.py @@ -14,8 +14,14 @@ from briar_wrapper.models.contacts import Contacts from briar_wrapper.models.private_chat import PrivateChat from briar_gtk.containers.private_chat import PrivateChatContainer +from briar_gtk.controllers.main_menu import MainMenuController +from briar_gtk.controllers.private_chat import PrivateChatController +from briar_gtk.controllers.sidebar import SidebarController from briar_gtk.define import APP, NOTIFICATION_CONTACT_ADDED, RESOURCES_DIR from briar_gtk.define import NOTIFICATION_PRIVATE_MESSAGE +from briar_gtk.views.main_menu import MainMenuView +from briar_gtk.views.private_chat import PrivateChatView +from briar_gtk.views.sidebar import SidebarView from briar_gtk.widgets.about_dialog import AboutDialogWidget from briar_gtk.widgets.contact_row import ContactRowWidget from briar_gtk.widgets.edit_dialog import EditDialog @@ -23,44 +29,61 @@ from briar_gtk.widgets.edit_dialog import EditDialog class MainWindowView(Gtk.Overlay): - CONTAINER_UI = "main_window.ui" MAIN_MENU_UI = "main_menu.ui" CHAT_MENU_UI = "chat_menu.ui" + MAIN_WINDOW_UI = "main_window.ui" _current_contact_id = 0 _current_private_chat_widget = None def __init__(self): super().__init__() - self.builder = Gtk.Builder() + self._builder = Gtk.Builder() + self._add_from_resource(self.MAIN_MENU_UI) + self._add_from_resource(self.CHAT_MENU_UI) + self._add_from_resource(self.MAIN_WINDOW_UI) + self._builder.connect_signals(self) + + self._sidebar_view = SidebarView(self._builder) + self._sidebar_controller = SidebarController( + self._sidebar_view, APP().api) + + self._private_chat_view = PrivateChatView(self._builder) + self._private_chat_controller = PrivateChatController( + self._private_chat_view, APP().api) + + self._main_menu_view = MainMenuView() + self._main_menu_controller = MainMenuController( + self._main_menu_view, APP().api) + self._signals = list() self._setup_view() self._load_content() def _add_from_resource(self, ui_filename): - self.builder.add_from_resource( + self._builder.add_from_resource( os.path.join(RESOURCES_DIR, ui_filename) ) @property def main_window_leaflet(self): - return self.builder.get_object("main_window_leaflet") + return self._builder.get_object("main_window_leaflet") @property def contact_name_label(self): - return self.builder.get_object("contact_name") + return self._builder.get_object("contact_name") @property def contacts_list_box(self): - return self.builder.get_object("contacts_list_box") + return self._builder.get_object("contacts_list_box") @property def main_content_stack(self): - return self.builder.get_object("main_content_stack") + return self._builder.get_object("main_content_stack") @property def main_content_container(self): - return self.builder.get_object("main_content_container") + return self._builder.get_object("main_content_container") @property def chat_placeholder(self): @@ -72,11 +95,11 @@ class MainWindowView(Gtk.Overlay): @property def history_container(self): - return self.builder.get_object("history_container") + return self._builder.get_object("history_container") @property def chat_entry(self): - return self.builder.get_object("chat_entry") + return self._builder.get_object("chat_entry") @staticmethod def open_about_page(): @@ -91,14 +114,14 @@ class MainWindowView(Gtk.Overlay): def show_sidebar(self): self.main_window_leaflet.set_visible_child( - self.builder.get_object("sidebar_box")) + self._builder.get_object("sidebar_box")) self.chat_view.hide() self.chat_placeholder.show() self._clear_history_container() self.contacts_list_box.unselect_all() self.contact_name_label.set_text("") self._current_contact_id = 0 - self.builder.get_object("chat_menu_button").hide() + self._builder.get_object("chat_menu_button").hide() def open_change_contact_alias_dialog(self): if self._current_contact_id == 0: @@ -182,7 +205,7 @@ class MainWindowView(Gtk.Overlay): self.main_window_leaflet.set_visible_child( self.main_content_container) self.contact_name_label.set_text(contact_name) - self.builder.get_object("chat_menu_button").show() + self._builder.get_object("chat_menu_button").show() def _setup_private_chat_widget(self, contact_name, contact_id): self._current_private_chat_widget = PrivateChatContainer( @@ -213,7 +236,7 @@ class MainWindowView(Gtk.Overlay): def _get_contact_name(self, contact_id): name = "" - for contact in self.contacts_list: + for contact in Contacts(APP().api).get(): if contact["contactId"] == contact_id: name = contact["author"]["name"] if "alias" in contact: @@ -229,24 +252,17 @@ class MainWindowView(Gtk.Overlay): del self._selected_contact def _setup_view(self): - self._add_from_resource(self.MAIN_MENU_UI) - self._add_from_resource(self.CHAT_MENU_UI) - self._add_from_resource(self.CONTAINER_UI) - self.builder.connect_signals(self) - self._setup_main_window_stack() self._setup_headerbar_stack_holder() - self.contact_name_label.set_text("") - self.builder.get_object("chat_menu_button").hide() self._setup_destroy_listener() def _setup_main_window_stack(self): - main_window_stack = self.builder.get_object("main_window_stack") + main_window_stack = self._builder.get_object("main_window_stack") main_window_stack.show_all() self.add(main_window_stack) def _setup_headerbar_stack_holder(self): - headerbar_stack_holder = self.builder.get_object( + headerbar_stack_holder = self._builder.get_object( "headerbar_stack_holder") headerbar_stack_holder.show_all() APP().window.set_titlebar(headerbar_stack_holder) @@ -259,60 +275,26 @@ class MainWindowView(Gtk.Overlay): self._disconnect_signals() def _disconnect_signals(self): + self._sidebar_controller.disconnect_signals() for signal in self._signals: APP().api.socket_listener.disconnect(signal) # pylint: disable=no-member def _load_content(self): - self._contacts = Contacts(APP().api) - self._load_contacts() socket_listener = APP().api.socket_listener self._setup_contact_added_listeners(socket_listener) self._setup_message_received_listeners(socket_listener) - self._setup_contact_connection_listener(socket_listener) - - def _load_contacts(self): - self.contacts_list = self._contacts.get() - - selected_contact = -1 - if hasattr(self, "_selected_contact"): - selected_contact = self._selected_contact - - for contact in self.contacts_list: - contact_row = ContactRowWidget(contact) - self.contacts_list_box.add(contact_row) - if contact["contactId"] == selected_contact: - self.contacts_list_box.select_row(contact_row) def _setup_contact_added_listeners(self, socket_listener): - signal_id = socket_listener.connect("ContactAddedEvent", - self._refresh_contacts_async) - self._signals.append(signal_id) signal_id = socket_listener.connect("ContactAddedEvent", self._notify_contact_added) self._signals.append(signal_id) def _setup_message_received_listeners(self, socket_listener): - signal_id = socket_listener.connect("ConversationMessageReceivedEvent", - self._refresh_contacts_async) - self._signals.append(signal_id) signal_id = socket_listener.connect("ConversationMessageReceivedEvent", self._notify_message_received) self._signals.append(signal_id) - def _setup_contact_connection_listener(self, socket_listener): - callback = self._refresh_contact_connection_state - signal_ids = self._contacts.watch_connections(callback) - self._signals.extend(signal_ids) - - # pylint: disable=unused-argument - def _refresh_contacts_async(self, message): - GLib.idle_add(self._refresh_contacts) - - # pylint: disable=unused-argument - def _refresh_contact_connection_state(self, contact_id, connected): - self._refresh_contacts_async(None) - # pylint: disable=unused-argument def _notify_contact_added(self, message): self._notify( @@ -336,17 +318,4 @@ class MainWindowView(Gtk.Overlay): APP().send_notification(identifier, notification) def _refresh_contacts(self): - self._save_selected_row() - self._clear_contact_list() - self._load_contacts() - - def _save_selected_row(self): - row = self.contacts_list_box.get_selected_row() - if row is None: - return - self._selected_contact = row.get_action_target_value().get_int32() - - def _clear_contact_list(self): - contacts_list_box_children = self.contacts_list_box.get_children() - for child in contacts_list_box_children: - child.destroy() + self._sidebar_controller.refresh_contacts() diff --git a/briar-gtk/briar_gtk/views/private_chat.py b/briar-gtk/briar_gtk/views/private_chat.py new file mode 100644 index 0000000000000000000000000000000000000000..61c55c80c3f683c7dbaa54ae98a289da089b9f6d --- /dev/null +++ b/briar-gtk/briar_gtk/views/private_chat.py @@ -0,0 +1,20 @@ +# Copyright (c) 2020 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md + +import os + +from gi.repository import Gtk + +from briar_gtk.define import RESOURCES_DIR + + +class PrivateChatView(): + + def __init__(self, builder): + self._builder = builder + self._setup_view() + + def _setup_view(self): + contact_name_label = self._builder.get_object("contact_name") + contact_name_label.set_text("") diff --git a/briar-gtk/briar_gtk/views/sidebar.py b/briar-gtk/briar_gtk/views/sidebar.py new file mode 100644 index 0000000000000000000000000000000000000000..5fb2372374ba90f3e5cf521cdcc45de7006d8ab7 --- /dev/null +++ b/briar-gtk/briar_gtk/views/sidebar.py @@ -0,0 +1,39 @@ +# Copyright (c) 2020 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md + +import os + +from gi.repository import Gtk + +from briar_gtk.define import RESOURCES_DIR +from briar_gtk.widgets.contact_row import ContactRowWidget + + +class SidebarView(): + + def __init__(self, builder): + self._builder = builder + + def show_contacts(self, contact_list, selected_contact): + self._clear_contact_list() + + for contact in contact_list: + contact_row = ContactRowWidget(contact) + contacts_list_box = self._builder.get_object("contacts_list_box") + contacts_list_box.add(contact_row) + if contact["contactId"] == selected_contact: + contacts_list_box.select_row(contact_row) + + def get_selected_row(self): + contacts_list_box = self._builder.get_object("contacts_list_box") + row = contacts_list_box.get_selected_row() + if row is None: + return -1 + return row.get_action_target_value().get_int32() + + def _clear_contact_list(self): + contacts_list_box = self._builder.get_object("contacts_list_box") + contacts_list_box_children = contacts_list_box.get_children() + for child in contacts_list_box_children: + child.destroy()