From 5a7ffbc1870aeee52fed3c457817010b608e1e52 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Sun, 27 Jan 2019 15:24:58 +0100 Subject: Move app menu into window, make conversation details dialog directly accessible, no-CSD option for main window --- main/src/ui/application.vala | 58 +++-- main/src/ui/conversation_list_titlebar.vala | 37 +--- main/src/ui/conversation_list_titlebar_csd.vala | 37 ++++ main/src/ui/conversation_titlebar/menu_entry.vala | 16 +- .../ui/conversation_titlebar/occupants_entry.vala | 16 +- .../src/ui/conversation_titlebar/search_entry.vala | 6 +- main/src/ui/conversation_titlebar/view.vala | 98 ++++----- main/src/ui/conversation_titlebar/view_csd.vala | 38 ++++ main/src/ui/global_search.vala | 2 +- main/src/ui/occupant_menu/view.vala | 4 +- main/src/ui/unified_window.vala | 244 +++++++-------------- main/src/ui/unified_window_controller.vala | 204 +++++++++++++++++ main/src/ui/util/helper.vala | 4 + 13 files changed, 476 insertions(+), 288 deletions(-) create mode 100644 main/src/ui/conversation_list_titlebar_csd.vala create mode 100644 main/src/ui/conversation_titlebar/view_csd.vala create mode 100644 main/src/ui/unified_window_controller.vala (limited to 'main/src') diff --git a/main/src/ui/application.vala b/main/src/ui/application.vala index c34752a2..22c3256f 100644 --- a/main/src/ui/application.vala +++ b/main/src/ui/application.vala @@ -7,6 +7,7 @@ using Xmpp; public class Dino.Ui.Application : Gtk.Application, Dino.Application { private Notifications notifications; private UnifiedWindow window; + private UnifiedWindowController controller; public Database db { get; set; } public Dino.Entities.Settings settings { get; set; } @@ -28,8 +29,10 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { activate.connect(() => { if (window == null) { - create_set_app_menu(); + controller = new UnifiedWindowController(this, stream_interactor, db); window = new UnifiedWindow(this, stream_interactor, db); + controller.set_window(window); + notifications = new Notifications(stream_interactor, window); notifications.start(); notifications.conversation_selected.connect((conversation) => window.on_conversation_selected(conversation)); @@ -79,6 +82,19 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { } private void create_actions() { + SimpleAction accounts_action = new SimpleAction("accounts", null); + accounts_action.activate.connect(show_accounts_window); + add_action(accounts_action); + + SimpleAction settings_action = new SimpleAction("settings", null); + settings_action.activate.connect(show_settings_window); + add_action(settings_action); + + SimpleAction quit_action = new SimpleAction("quit", null); + quit_action.activate.connect(quit); + add_action(quit_action); + set_accels_for_action("app.quit", new string[]{"Q"}); + SimpleAction open_conversation_action = new SimpleAction("open-conversation", VariantType.INT32); open_conversation_action.activate.connect((variant) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32()); @@ -94,6 +110,26 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { stream_interactor.get_module(PresenceManager.IDENTITY).deny_subscription(conversation.account, conversation.counterpart); }); add_action(deny_subscription_action); + + SimpleAction contacts_action = new SimpleAction("add_chat", null); + contacts_action.activate.connect(() => { + AddChatDialog add_chat_dialog = new AddChatDialog(stream_interactor, stream_interactor.get_accounts()); + add_chat_dialog.set_transient_for(window); + add_chat_dialog.added.connect((conversation) => { + window.on_conversation_selected(conversation); + }); + add_chat_dialog.present(); + }); + add_action(contacts_action); + + SimpleAction conference_action = new SimpleAction("add_conference", null); + conference_action.activate.connect(() => { + AddConferenceDialog add_conference_dialog = new AddConferenceDialog(stream_interactor); + add_conference_dialog.set_transient_for(window); + add_conference_dialog.conversation_opened.connect(conversation => controller.select_conversation(conversation)); + add_conference_dialog.present(); + }); + add_action(conference_action); } private void show_accounts_window() { @@ -109,25 +145,5 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { dialog.set_transient_for(get_active_window()); dialog.present(); } - - private void create_set_app_menu() { - SimpleAction accounts_action = new SimpleAction("accounts", null); - accounts_action.activate.connect(show_accounts_window); - add_action(accounts_action); - - SimpleAction settings_action = new SimpleAction("settings", null); - settings_action.activate.connect(show_settings_window); - add_action(settings_action); - - SimpleAction quit_action = new SimpleAction("quit", null); - quit_action.activate.connect(quit); - add_action(quit_action); - set_accels_for_action("app.quit", new string[]{"Q"}); - - Builder builder = new Builder.from_resource("/im/dino/Dino/menu_app.ui"); - MenuModel menu = builder.get_object("menu_app") as MenuModel; - - set_app_menu(menu); - } } diff --git a/main/src/ui/conversation_list_titlebar.vala b/main/src/ui/conversation_list_titlebar.vala index 60d9a6fb..abce2a6c 100644 --- a/main/src/ui/conversation_list_titlebar.vala +++ b/main/src/ui/conversation_list_titlebar.vala @@ -5,47 +5,28 @@ using Dino.Entities; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/conversation_list_titlebar.ui")] -public class ConversationListTitlebar : Gtk.HeaderBar { +public class ConversationListTitlebar : Gtk.Box { public signal void conversation_opened(Conversation conversation); [GtkChild] private MenuButton add_button; + [GtkChild] private MenuButton menu_button; private StreamInteractor stream_interactor; public ConversationListTitlebar(StreamInteractor stream_interactor, Window window) { this.stream_interactor = stream_interactor; - - custom_title = new Label("Dino") { visible = true, hexpand = true, xalign = 0 }; - custom_title.get_style_context().add_class("title"); - create_add_menu(window); } private void create_add_menu(Window window) { - SimpleAction contacts_action = new SimpleAction("add_chat", null); - contacts_action.activate.connect(() => { - AddChatDialog add_chat_dialog = new AddChatDialog(stream_interactor, stream_interactor.get_accounts()); - add_chat_dialog.set_transient_for(window); - add_chat_dialog.added.connect((conversation) => { - conversation_opened(conversation); - }); - add_chat_dialog.present(); - }); - GLib.Application.get_default().add_action(contacts_action); - - SimpleAction conference_action = new SimpleAction("add_conference", null); - conference_action.activate.connect(() => { - AddConferenceDialog add_conference_dialog = new AddConferenceDialog(stream_interactor); - add_conference_dialog.set_transient_for(window); - add_conference_dialog.conversation_opened.connect((conversation) => conversation_opened(conversation)); - add_conference_dialog.present(); - }); - GLib.Application.get_default().add_action(conference_action); - - Builder builder = new Builder.from_resource("/im/dino/Dino/menu_add.ui"); - MenuModel menu = builder.get_object("menu_add") as MenuModel; - add_button.set_menu_model(menu); + Builder add_builder = new Builder.from_resource("/im/dino/Dino/menu_add.ui"); + MenuModel add_menu_model = add_builder.get_object("menu_add") as MenuModel; + add_button.set_menu_model(add_menu_model); + + Builder menu_builder = new Builder.from_resource("/im/dino/Dino/menu_app.ui"); + MenuModel menu_menu_model = menu_builder.get_object("menu_app") as MenuModel; + menu_button.set_menu_model(menu_menu_model); } } diff --git a/main/src/ui/conversation_list_titlebar_csd.vala b/main/src/ui/conversation_list_titlebar_csd.vala new file mode 100644 index 00000000..85ff4867 --- /dev/null +++ b/main/src/ui/conversation_list_titlebar_csd.vala @@ -0,0 +1,37 @@ +using Gtk; + +using Dino.Entities; + +namespace Dino.Ui { + +[GtkTemplate (ui = "/im/dino/Dino/conversation_list_titlebar_csd.ui")] +public class ConversationListTitlebarCsd : Gtk.HeaderBar { + + public signal void conversation_opened(Conversation conversation); + + [GtkChild] private MenuButton add_button; + [GtkChild] private MenuButton menu_button; + + private StreamInteractor stream_interactor; + + public ConversationListTitlebarCsd(StreamInteractor stream_interactor, Window window) { + this.stream_interactor = stream_interactor; + + custom_title = new Label("Dino") { visible = true, hexpand = true, xalign = 0 }; + custom_title.get_style_context().add_class("title"); + + create_add_menu(window); + } + + private void create_add_menu(Window window) { + Builder add_builder = new Builder.from_resource("/im/dino/Dino/menu_add.ui"); + MenuModel add_menu_model = add_builder.get_object("menu_add") as MenuModel; + add_button.set_menu_model(add_menu_model); + + Builder menu_builder = new Builder.from_resource("/im/dino/Dino/menu_app.ui"); + MenuModel menu_menu_model = menu_builder.get_object("menu_app") as MenuModel; + menu_button.set_menu_model(menu_menu_model); + } +} + +} diff --git a/main/src/ui/conversation_titlebar/menu_entry.vala b/main/src/ui/conversation_titlebar/menu_entry.vala index e77204a8..e62989a0 100644 --- a/main/src/ui/conversation_titlebar/menu_entry.vala +++ b/main/src/ui/conversation_titlebar/menu_entry.vala @@ -8,6 +8,7 @@ class MenuEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "menu"; } } StreamInteractor stream_interactor; + MenuWidget widget; public MenuEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; @@ -16,30 +17,27 @@ class MenuEntry : Plugins.ConversationTitlebarEntry, Object { public double order { get { return 0; } } public Plugins.ConversationTitlebarWidget? get_widget(Plugins.WidgetType type) { if (type == Plugins.WidgetType.GTK) { - return new MenuWidget(stream_interactor) { visible=true }; + if (widget == null) { + widget = new MenuWidget(stream_interactor) { visible=true }; + } + return widget; } return null; } } -class MenuWidget : MenuButton, Plugins.ConversationTitlebarWidget { +class MenuWidget : Button, Plugins.ConversationTitlebarWidget { private Conversation? conversation; public MenuWidget(StreamInteractor stream_interactor) { image = new Image.from_icon_name("open-menu-symbolic", IconSize.MENU); - Builder builder = new Builder.from_resource("/im/dino/Dino/menu_conversation.ui"); - MenuModel menu = builder.get_object("menu_conversation") as MenuModel; - set_menu_model(menu); - - SimpleAction contact_details_action = new SimpleAction("contact_details", null); - contact_details_action.activate.connect(() => { + clicked.connect(() => { ContactDetails.Dialog contact_details_dialog = new ContactDetails.Dialog(stream_interactor, conversation); contact_details_dialog.set_transient_for((Window) get_toplevel()); contact_details_dialog.present(); }); - GLib.Application.get_default().add_action(contact_details_action); } public new void set_conversation(Conversation conversation) { diff --git a/main/src/ui/conversation_titlebar/occupants_entry.vala b/main/src/ui/conversation_titlebar/occupants_entry.vala index 0646eee7..af553e35 100644 --- a/main/src/ui/conversation_titlebar/occupants_entry.vala +++ b/main/src/ui/conversation_titlebar/occupants_entry.vala @@ -8,17 +8,19 @@ class OccupantsEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "occupants"; } } StreamInteractor stream_interactor; - Window window; + OccupantsWidget widget; - public OccupantsEntry(StreamInteractor stream_interactor, Window window) { + public OccupantsEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; - this.window = window; } public double order { get { return 3; } } public Plugins.ConversationTitlebarWidget? get_widget(Plugins.WidgetType type) { if (type == Plugins.WidgetType.GTK) { - return new OccupantsWidget(stream_interactor, window) { visible=true }; + if (widget == null) { + widget = new OccupantsWidget(stream_interactor) { visible=true }; + } + return widget; } return null; } @@ -28,14 +30,12 @@ class OccupantsWidget : MenuButton, Plugins.ConversationTitlebarWidget { private Conversation? conversation; private StreamInteractor stream_interactor; - private Window window; private OccupantMenu.View menu = null; - public OccupantsWidget(StreamInteractor stream_interactor, Window window) { + public OccupantsWidget(StreamInteractor stream_interactor) { image = new Image.from_icon_name("system-users-symbolic", IconSize.MENU); this.stream_interactor = stream_interactor; - this.window = window; set_use_popover(true); } @@ -44,7 +44,7 @@ class OccupantsWidget : MenuButton, Plugins.ConversationTitlebarWidget { visible = conversation.type_ == Conversation.Type.GROUPCHAT; if (conversation.type_ == Conversation.Type.GROUPCHAT) { - OccupantMenu.View new_menu = new OccupantMenu.View(stream_interactor, window, conversation); + OccupantMenu.View new_menu = new OccupantMenu.View(stream_interactor, conversation); set_popover(new_menu); if (menu != null) menu.destroy(); menu = new_menu; diff --git a/main/src/ui/conversation_titlebar/search_entry.vala b/main/src/ui/conversation_titlebar/search_entry.vala index b452bdce..ff0ff325 100644 --- a/main/src/ui/conversation_titlebar/search_entry.vala +++ b/main/src/ui/conversation_titlebar/search_entry.vala @@ -8,10 +8,10 @@ namespace Dino.Ui { public class SearchMenuEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "search"; } } - Plugins.ConversationTitlebarWidget search_button; + public GlobalSearchButton search_button = new GlobalSearchButton() { visible = true }; - public SearchMenuEntry(Plugins.ConversationTitlebarWidget search_button) { - this.search_button = search_button; + public SearchMenuEntry() { + search_button.set_image(new Gtk.Image.from_icon_name("system-search-symbolic", Gtk.IconSize.MENU) { visible = true }); } public double order { get { return 1; } } diff --git a/main/src/ui/conversation_titlebar/view.vala b/main/src/ui/conversation_titlebar/view.vala index 7ced9830..cb31527e 100644 --- a/main/src/ui/conversation_titlebar/view.vala +++ b/main/src/ui/conversation_titlebar/view.vala @@ -1,82 +1,66 @@ using Gtk; using Gee; +using Pango; using Dino.Entities; namespace Dino.Ui { -public class ConversationTitlebar : Gtk.HeaderBar { +public class ConversationTitlebar : Gtk.Box { + + public string? title { + get { return title_label.label; } + set { this.title_label.label = value; } + } + + public string? subtitle { + get { return subtitle_label.label; } + set { + this.subtitle_label.label = "" + value + ""; + this.subtitle_label.visible = (value != null); + } + } private StreamInteractor stream_interactor; - private Window window; private Conversation? conversation; - private Gee.List widgets = new ArrayList(); + + private Box content_box = new Box(Orientation.HORIZONTAL, 0) { margin=5, margin_start=15, margin_end=5, hexpand=true, visible=true }; + private Label title_label = new Label("") { visible=true }; + private Label subtitle_label = new Label("") { use_markup=true, ellipsize=EllipsizeMode.END, visible=false }; public GlobalSearchButton search_button = new GlobalSearchButton() { visible = true }; - public ConversationTitlebar(StreamInteractor stream_interactor, Window window) { + construct { + this.add(content_box); + + Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true, visible=true }; + content_box.add(titles_box); + + titles_box.add(title_label); + subtitle_label.attributes = new AttrList(); + subtitle_label.get_style_context().add_class("dim-label"); + titles_box.add(subtitle_label); + + Box placeholder_box = new Box(Orientation.VERTICAL, 0) { visible=true }; + placeholder_box.add(new Label("") { xalign=0, visible=true }); + placeholder_box.add(new Label(" ") { use_markup=true, xalign=0, visible=true }); + content_box.add(placeholder_box); + } + + public ConversationTitlebar(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; - this.window = window; - this.get_style_context().add_class("dino-right"); - show_close_button = true; + this.get_style_context().add_class("dino-header-right"); hexpand = true; search_button.set_image(new Gtk.Image.from_icon_name("system-search-symbolic", Gtk.IconSize.MENU) { visible = true }); Application app = GLib.Application.get_default() as Application; - app.plugin_registry.register_contact_titlebar_entry(new MenuEntry(stream_interactor)); - app.plugin_registry.register_contact_titlebar_entry(new SearchMenuEntry(search_button)); - app.plugin_registry.register_contact_titlebar_entry(new OccupantsEntry(stream_interactor, window)); - foreach(var e in app.plugin_registry.conversation_titlebar_entries) { Plugins.ConversationTitlebarWidget widget = e.get_widget(Plugins.WidgetType.GTK); if (widget != null) { - widgets.add(widget); - pack_end((Gtk.Widget)widget); - } - } - - - stream_interactor.get_module(MucManager.IDENTITY).room_name_set.connect((account, jid, room_name) => { - if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { - update_title(); - } - }); - - stream_interactor.get_module(MucManager.IDENTITY).private_room_occupant_updated.connect((account, room, occupant) => { - if (conversation != null && conversation.counterpart.equals_bare(room.bare_jid) && conversation.account.equals(account)) { - update_title(); + Button gtk_widget = (Gtk.Button)widget; + gtk_widget.relief = ReliefStyle.NONE; + content_box.add(gtk_widget); } - }); - - stream_interactor.get_module(MucManager.IDENTITY).subject_set.connect((account, jid, subject) => { - if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { - update_subtitle(subject); - } - }); - } - - public void initialize_for_conversation(Conversation conversation) { - this.conversation = conversation; - update_title(); - update_subtitle(); - - foreach (Plugins.ConversationTitlebarWidget widget in widgets) { - widget.set_conversation(conversation); - } - } - - private void update_title() { - set_title(Util.get_conversation_display_name(stream_interactor, conversation)); - } - - private void update_subtitle(string? subtitle = null) { - if (subtitle != null) { - set_subtitle(subtitle); - } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { - string subject = stream_interactor.get_module(MucManager.IDENTITY).get_groupchat_subject(conversation.counterpart, conversation.account); - set_subtitle(subject != "" ? subject : null); - } else { - set_subtitle(null); } } } diff --git a/main/src/ui/conversation_titlebar/view_csd.vala b/main/src/ui/conversation_titlebar/view_csd.vala new file mode 100644 index 00000000..83a1a474 --- /dev/null +++ b/main/src/ui/conversation_titlebar/view_csd.vala @@ -0,0 +1,38 @@ +using Gtk; +using Gee; + +using Dino.Entities; + +namespace Dino.Ui { + +public class ConversationTitlebarCsd : Gtk.HeaderBar { + + private StreamInteractor stream_interactor; + private Window window; + private Conversation? conversation; + + public ConversationTitlebarCsd(StreamInteractor stream_interactor, Window window) { + this.stream_interactor = stream_interactor; + this.window = window; + + this.get_style_context().add_class("dino-right"); + show_close_button = true; + hexpand = true; + + Application app = GLib.Application.get_default() as Application; + ArrayList widgets = new ArrayList(); + foreach(var e in app.plugin_registry.conversation_titlebar_entries) { + Plugins.ConversationTitlebarWidget widget = e.get_widget(Plugins.WidgetType.GTK); + if (widget != null) { + widgets.insert(0, widget); + } + } + foreach (var w in widgets) { + Button gtk_widget = (Gtk.Button)w; + gtk_widget.relief = ReliefStyle.NONE; + this.pack_end(gtk_widget); + } + } +} + +} diff --git a/main/src/ui/global_search.vala b/main/src/ui/global_search.vala index 99a69e1b..73a61dc5 100644 --- a/main/src/ui/global_search.vala +++ b/main/src/ui/global_search.vala @@ -7,7 +7,7 @@ using Dino.Entities; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/global_search.ui")] -class GlobalSearch : Overlay { +public class GlobalSearch : Overlay { public signal void selected_item(MessageItem item); private StreamInteractor stream_interactor; private string search = ""; diff --git a/main/src/ui/occupant_menu/view.vala b/main/src/ui/occupant_menu/view.vala index 1115d0ab..0d11a310 100644 --- a/main/src/ui/occupant_menu/view.vala +++ b/main/src/ui/occupant_menu/view.vala @@ -15,7 +15,7 @@ public class View : Popover { private ListBox invite_list; private Box? jid_menu = null; - public View(StreamInteractor stream_interactor, Window window, Conversation conversation) { + public View(StreamInteractor stream_interactor, Conversation conversation) { this.stream_interactor = stream_interactor; this.conversation = conversation; @@ -31,7 +31,7 @@ public class View : Popover { Gee.List acc_list = new ArrayList(Account.equals_func); acc_list.add(conversation.account); SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list); - add_chat_dialog.set_transient_for(window); + add_chat_dialog.set_transient_for((Window) get_toplevel()); add_chat_dialog.title = _("Invite to Conference"); add_chat_dialog.ok_button.label = _("Invite"); add_chat_dialog.selected.connect((account, jid) => { diff --git a/main/src/ui/unified_window.vala b/main/src/ui/unified_window.vala index 28bec42d..8338573e 100644 --- a/main/src/ui/unified_window.vala +++ b/main/src/ui/unified_window.vala @@ -8,22 +8,53 @@ namespace Dino.Ui { public class UnifiedWindow : Gtk.Window { - private WelcomePlceholder welcome_placeholder = new WelcomePlceholder() { visible=true }; - private NoAccountsPlaceholder accounts_placeholder = new NoAccountsPlaceholder() { visible=true }; - private NoConversationsPlaceholder conversations_placeholder = new NoConversationsPlaceholder() { visible=true }; - private ChatInput.View chat_input; - private ConversationListTitlebar conversation_list_titlebar; - private ConversationSelector.View filterable_conversation_list; - private ConversationSummary.ConversationView conversation_frame; - private ConversationTitlebar conversation_titlebar; - private HeaderBar placeholder_headerbar = new HeaderBar() { title="Dino", show_close_button=true, visible=true }; - private Paned headerbar_paned = new Paned(Orientation.HORIZONTAL) { visible=true }; - private Paned paned; - private Revealer goto_end_revealer; - private Button goto_end_button; - private Revealer search_revealer; - private SearchEntry search_entry; - private GlobalSearch search_box; + public signal void conversation_selected(Conversation conversation); + + public new string? title { + get { + return Util.use_csd() ? conversation_titlebar_csd.title : conversation_titlebar.title; + } + set { + if (Util.use_csd()) { + conversation_titlebar_csd.title = value; + } else { + conversation_titlebar.title = value; + } + } + } + + public string? subtitle { + get { + return Util.use_csd() ? conversation_titlebar_csd.subtitle : conversation_titlebar.subtitle; + } + set { + if (Util.use_csd()) { + conversation_titlebar_csd.subtitle = value; + } else { + conversation_titlebar.subtitle = value; + } + } + } + + 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 ConversationListTitlebar conversation_list_titlebar; + public ConversationListTitlebarCsd conversation_list_titlebar_csd; + public ConversationSelector.View filterable_conversation_list; + public ConversationSummary.ConversationView conversation_frame; + public ConversationTitlebar conversation_titlebar; + public ConversationTitlebarCsd conversation_titlebar_csd; + public HeaderBar placeholder_headerbar = new HeaderBar() { title="Dino", show_close_button=true, visible=true }; + 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; private Stack stack = new Stack() { visible=true }; private StreamInteractor stream_interactor; @@ -37,118 +68,34 @@ public class UnifiedWindow : Gtk.Window { this.stream_interactor = stream_interactor; this.db = db; - restore_window_size(); - - this.get_style_context().add_class("dino-main"); setup_headerbar(); setup_unified(); setup_stack(); - var vadjustment = conversation_frame.scrolled.vadjustment; - vadjustment.notify["value"].connect(() => { - goto_end_revealer.reveal_child = vadjustment.value < vadjustment.upper - vadjustment.page_size; - }); - goto_end_button.clicked.connect(() => { - conversation_frame.initialize_for_conversation(conversation); - }); - - conversation_titlebar.search_button.clicked.connect(() => { - search_revealer.reveal_child = conversation_titlebar.search_button.active; - }); - search_revealer.notify["child-revealed"].connect(() => { - if (search_revealer.child_revealed) { - if (conversation_frame.conversation != null && search_box.search_entry.text == "") { - reset_search_entry(); - } - search_box.search_entry.grab_focus(); - } - }); - search_box.selected_item.connect((item) => { - on_conversation_selected(item.conversation, false, false); - conversation_frame.initialize_around_message(item.conversation, item); - close_search(); - }); - event.connect((event) => { - if (event.type == EventType.BUTTON_PRESS) { - int dest_x, dest_y; - bool ret = search_box.translate_coordinates(this, 0, 0, out dest_x, out dest_y); - int geometry_x, geometry_y, geometry_width, geometry_height; - this.get_window().get_geometry(out geometry_x, out geometry_y, out geometry_width, out geometry_height); - if (ret && event.button.x_root - geometry_x < dest_x || event.button.y_root - geometry_y < dest_y) { - close_search(); - } - } else if (event.type == EventType.KEY_RELEASE) { - if (event.key.keyval == Gdk.Key.Escape) { - close_search(); - } - } - return false; - }); - - paned.bind_property("position", headerbar_paned, "position", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); - - focus_in_event.connect(on_focus_in_event); - focus_out_event.connect(on_focus_out_event); - stream_interactor.account_added.connect((account) => { check_stack(true); }); stream_interactor.account_removed.connect((account) => { check_stack(); }); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(() => check_stack()); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(() => check_stack()); - welcome_placeholder.primary_button.clicked.connect(() => { - ManageAccounts.AddAccountDialog dialog = new ManageAccounts.AddAccountDialog(stream_interactor); - dialog.set_transient_for(app.get_active_window()); - dialog.present(); - }); - accounts_placeholder.primary_button.clicked.connect(() => { get_application().activate_action("accounts", null); }); - conversations_placeholder.primary_button.clicked.connect(() => { get_application().activate_action("add_chat", null); }); - conversations_placeholder.secondary_button.clicked.connect(() => { get_application().activate_action("add_conference", null); }); - filterable_conversation_list.conversation_list.conversation_selected.connect((conversation) => on_conversation_selected(conversation)); - conversation_list_titlebar.conversation_opened.connect((conversation) => on_conversation_selected(conversation)); - check_stack(); - } + paned.bind_property("position", headerbar_paned, "position", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); - private void reset_search_entry() { - if (conversation_frame.conversation != null) { - switch (conversation.type_) { - case Conversation.Type.CHAT: - case Conversation.Type.GROUPCHAT_PM: - search_box.search_entry.text = @"with:$(conversation.counterpart) "; - break; - case Conversation.Type.GROUPCHAT: - search_box.search_entry.text = @"in:$(conversation.counterpart) "; - break; - } - } + check_stack(); } public void on_conversation_selected(Conversation conversation, bool do_reset_search = true, bool default_initialize_conversation = true) { if (this.conversation == null || !this.conversation.equals(conversation)) { this.conversation = conversation; - stream_interactor.get_module(ChatInteraction.IDENTITY).on_conversation_selected(conversation); - conversation.active = true; // only for conversation_selected - filterable_conversation_list.conversation_list.on_conversation_selected(conversation); // only for conversation_opened + conversation_selected(conversation); - if (do_reset_search) { - reset_search_entry(); - } - chat_input.initialize_for_conversation(conversation); - if (default_initialize_conversation) { - conversation_frame.initialize_for_conversation(conversation); - } - conversation_titlebar.initialize_for_conversation(conversation); - } - } - private void close_search() { - conversation_titlebar.search_button.active = false; - search_revealer.reveal_child = false; + } } private void setup_unified() { Builder builder = new Builder.from_resource("/im/dino/Dino/unified_main_content.ui"); paned = (Paned) builder.get_object("paned"); + box.add(paned); chat_input = ((ChatInput.View) builder.get_object("chat_input")).init(stream_interactor); conversation_frame = ((ConversationSummary.ConversationView) builder.get_object("conversation_frame")).init(stream_interactor); filterable_conversation_list = ((ConversationSelector.View) builder.get_object("conversation_list")).init(stream_interactor); @@ -160,27 +107,36 @@ public class UnifiedWindow : Gtk.Window { } private void setup_headerbar() { - conversation_titlebar = new ConversationTitlebar(stream_interactor, this) { visible=true }; - conversation_list_titlebar = new ConversationListTitlebar(stream_interactor, this) { visible=true }; - headerbar_paned.pack1(conversation_list_titlebar, false, false); - headerbar_paned.pack2(conversation_titlebar, true, false); - - // Distribute start/end decoration_layout buttons to left/right headerbar. Ensure app menu fallback. - Gtk.Settings? gtk_settings = Gtk.Settings.get_default(); - if (gtk_settings != null) { - if (!gtk_settings.gtk_decoration_layout.contains("menu")) { - gtk_settings.gtk_decoration_layout = "menu:" + gtk_settings.gtk_decoration_layout; - } - string[] decoration_layout = gtk_settings.gtk_decoration_layout.split(":"); - if (decoration_layout.length == 2) { - conversation_list_titlebar.decoration_layout = decoration_layout[0] + ":"; - conversation_titlebar.decoration_layout = ":" + decoration_layout[1]; + if (Util.use_csd()) { + conversation_titlebar_csd = new ConversationTitlebarCsd(stream_interactor, this) { visible=true }; + conversation_list_titlebar_csd = new ConversationListTitlebarCsd(stream_interactor, this) { visible=true }; + headerbar_paned.pack1(conversation_list_titlebar_csd, false, false); + headerbar_paned.pack2(conversation_titlebar_csd, true, false); + + // Distribute start/end decoration_layout buttons to left/right headerbar. Ensure app menu fallback. + Gtk.Settings? gtk_settings = Gtk.Settings.get_default(); + if (gtk_settings != null) { + if (!gtk_settings.gtk_decoration_layout.contains("menu")) { + gtk_settings.gtk_decoration_layout = "menu:" + gtk_settings.gtk_decoration_layout; + } + string[] decoration_layout = gtk_settings.gtk_decoration_layout.split(":"); + if (decoration_layout.length == 2) { + conversation_list_titlebar_csd.decoration_layout = decoration_layout[0] + ":"; + conversation_titlebar_csd.decoration_layout = ":" + decoration_layout[1]; + } } + } else { + conversation_list_titlebar = new ConversationListTitlebar(stream_interactor, this) { visible=true }; + conversation_titlebar = new ConversationTitlebar(stream_interactor) { visible=true }; + headerbar_paned.pack1(conversation_list_titlebar, false, false); + headerbar_paned.pack2(conversation_titlebar, true, false); + + box.add(headerbar_paned); } } private void setup_stack() { - stack.add_named(paned, "main"); + stack.add_named(box, "main"); stack.add_named(welcome_placeholder, "welcome_placeholder"); stack.add_named(accounts_placeholder, "accounts_placeholder"); stack.add_named(conversations_placeholder, "conversations_placeholder"); @@ -192,53 +148,23 @@ public class UnifiedWindow : Gtk.Window { if (!know_exists && accounts.size == 0) { if (db.get_accounts().size == 0) { stack.set_visible_child_name("welcome_placeholder"); - set_titlebar(placeholder_headerbar); } else { stack.set_visible_child_name("accounts_placeholder"); + } + if (Util.use_csd()) { set_titlebar(placeholder_headerbar); } } else if (stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations().size == 0) { stack.set_visible_child_name("conversations_placeholder"); - set_titlebar(placeholder_headerbar); + if (Util.use_csd()) { + set_titlebar(placeholder_headerbar); + } } else { stack.set_visible_child_name("main"); - set_titlebar(headerbar_paned); - } - } - - private void restore_window_size() { - default_width = app.settings.current_width; - default_height = app.settings.current_height; - if (app.settings.is_maximized) this.maximize(); - if (app.settings.position_x != -1 && app.settings.position_y != -1) { - move(app.settings.position_x, app.settings.position_y); + if (Util.use_csd()) { + set_titlebar(headerbar_paned); + } } - - delete_event.connect(() => { - int x, y; - get_position(out x, out y); - app.settings.position_x = x; - app.settings.position_y = y; - - int width, height; - get_size(out width, out height); - app.settings.current_width = width; - app.settings.current_height = height; - - app.settings.is_maximized = is_maximized; - return false; - }); - } - - private bool on_focus_in_event() { - stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_in(conversation); - urgency_hint = false; - return false; - } - - private bool on_focus_out_event() { - stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_out(conversation); - return false; } } diff --git a/main/src/ui/unified_window_controller.vala b/main/src/ui/unified_window_controller.vala new file mode 100644 index 00000000..dce5b39f --- /dev/null +++ b/main/src/ui/unified_window_controller.vala @@ -0,0 +1,204 @@ +using Gee; +using Gdk; +using Gtk; + +using Dino.Entities; + +namespace Dino.Ui { + +public class UnifiedWindowController : Object { + + public new string? conversation_display_name { get; set; } + public string? conversation_topic { get; set; } + + private StreamInteractor stream_interactor; + private Conversation? conversation; + private Application app; + private Database db; + private UnifiedWindow window; + + private SearchMenuEntry search_menu_entry = new SearchMenuEntry(); + + public UnifiedWindowController(Application application, StreamInteractor stream_interactor, Database db) { + this.app = application; + this.stream_interactor = stream_interactor; + this.db = db; + + stream_interactor.get_module(MucManager.IDENTITY).room_name_set.connect((account, jid, room_name) => { + if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { + update_conversation_display_name(); + } + }); + + stream_interactor.get_module(MucManager.IDENTITY).private_room_occupant_updated.connect((account, room, occupant) => { + if (conversation != null && conversation.counterpart.equals_bare(room.bare_jid) && conversation.account.equals(account)) { + update_conversation_display_name(); + } + }); + + stream_interactor.get_module(MucManager.IDENTITY).subject_set.connect((account, jid, subject) => { + if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { + update_conversation_topic(subject); + } + }); + + app.plugin_registry.register_contact_titlebar_entry(new MenuEntry(stream_interactor)); + app.plugin_registry.register_contact_titlebar_entry(search_menu_entry); + app.plugin_registry.register_contact_titlebar_entry(new OccupantsEntry(stream_interactor)); + } + + public void set_window(UnifiedWindow window) { + this.window = window; + + 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 == "") { + reset_search_entry(); + } + window.search_box.search_entry.grab_focus(); + } + }); + window.search_box.selected_item.connect((item) => { + select_conversation(item.conversation, false, false); + window.conversation_frame.initialize_around_message(item.conversation, item); + close_search(); + }); + + window.welcome_placeholder.primary_button.clicked.connect(() => { + ManageAccounts.AddAccountDialog dialog = new ManageAccounts.AddAccountDialog(stream_interactor); + dialog.set_transient_for(app.get_active_window()); + dialog.present(); + }); + window.accounts_placeholder.primary_button.clicked.connect(() => { app.activate_action("accounts", null); }); + window.conversations_placeholder.primary_button.clicked.connect(() => { app.activate_action("add_chat", null); }); + window.conversations_placeholder.secondary_button.clicked.connect(() => { app.activate_action("add_conference", null); }); + window.filterable_conversation_list.conversation_list.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; + bool ret = window.search_box.translate_coordinates(window, 0, 0, out dest_x, out dest_y); + int geometry_x, geometry_y, geometry_width, geometry_height; + window.get_window().get_geometry(out geometry_x, out geometry_y, out geometry_width, out geometry_height); + if (ret && event.button.x_root - geometry_x < dest_x || event.button.y_root - geometry_y < dest_y) { + close_search(); + } + } else if (event.type == EventType.KEY_RELEASE) { + if (event.key.keyval == Gdk.Key.Escape) { + close_search(); + } + } + return false; + }); + window.focus_in_event.connect(() => { + stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_in(conversation); + window.urgency_hint = false; + return false; + }); + window.focus_out_event.connect(() => { + stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_out(conversation); + return false; + }); + + window.conversation_selected.connect(conversation => select_conversation(conversation)); + + restore_window_size(); + } + + public void select_conversation(Conversation conversation, bool do_reset_search = true, bool default_initialize_conversation = true) { + this.conversation = conversation; + + update_conversation_display_name(); + update_conversation_topic(); + + foreach(var e in this.app.plugin_registry.conversation_titlebar_entries) { + Plugins.ConversationTitlebarWidget widget = e.get_widget(Plugins.WidgetType.GTK); + if (widget != null) { + widget.set_conversation(conversation); + } + } + + stream_interactor.get_module(ChatInteraction.IDENTITY).on_conversation_selected(conversation); + conversation.active = true; // only for conversation_selected + window.filterable_conversation_list.conversation_list.on_conversation_selected(conversation); // only for conversation_opened + + if (do_reset_search) { + reset_search_entry(); + } + window.chat_input.initialize_for_conversation(conversation); + if (default_initialize_conversation) { + window.conversation_frame.initialize_for_conversation(conversation); + } + } + + private void update_conversation_display_name() { + conversation_display_name = Util.get_conversation_display_name(stream_interactor, conversation); + } + + private void update_conversation_topic(string? subtitle = null) { + if (conversation_topic != null) { + conversation_topic = subtitle; + } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { + string subject = stream_interactor.get_module(MucManager.IDENTITY).get_groupchat_subject(conversation.counterpart, conversation.account); + conversation_topic = subject != "" ? subject : null; + } else { + conversation_topic = null; + } + } + + private void reset_search_entry() { + if (window.conversation_frame.conversation != null) { + switch (conversation.type_) { + case Conversation.Type.CHAT: + case Conversation.Type.GROUPCHAT_PM: + window.search_box.search_entry.text = @"with:$(conversation.counterpart) "; + break; + case Conversation.Type.GROUPCHAT: + window.search_box.search_entry.text = @"in:$(conversation.counterpart) "; + break; + } + } + } + + private void close_search() { + search_menu_entry.search_button.active = false; + window.search_revealer.reveal_child = false; + } + + private void restore_window_size() { + window.default_width = app.settings.current_width; + window.default_height = app.settings.current_height; + if (app.settings.is_maximized) window.maximize(); + if (app.settings.position_x != -1 && app.settings.position_y != -1) { + window.move(app.settings.position_x, app.settings.position_y); + } + + window.delete_event.connect(() => { + int x, y; + window.get_position(out x, out y); + app.settings.position_x = x; + app.settings.position_y = y; + + int width, height; + window.get_size(out width, out height); + app.settings.current_width = width; + app.settings.current_height = height; + + app.settings.is_maximized = window.is_maximized; + return false; + }); + } +} + +} diff --git a/main/src/ui/util/helper.vala b/main/src/ui/util/helper.vala index 406a879e..5feda242 100644 --- a/main/src/ui/util/helper.vala +++ b/main/src/ui/util/helper.vala @@ -264,4 +264,8 @@ public int get_only_emoji_count(string markup_text) { return emoji_no; } +public bool use_csd() { + return Environment.get_variable("GTK_CSD") != "0"; +} + } -- cgit v1.2.3-54-g00ecf