aboutsummaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorfrosty <frostinator33@airmail.cc>2026-10-27 12:35:27 -0400
committerfrosty <frostinator33@airmail.cc>2026-10-27 12:35:27 -0400
commitbff7a3a4a6512f48a7b58c2b20404e2d3173050e (patch)
tree44cff6c45b158251890c1e24dc0986c3a80021f5 /main
parentc7426c9fb97f1afdf2b33a055d977762af92c795 (diff)
feat: begin working on account switcherHEADmaster
Diffstat (limited to 'main')
-rw-r--r--main/data/main_window.ui50
-rw-r--r--main/data/style.css54
-rw-r--r--main/meson.build1
-rw-r--r--main/src/ui/conversation_selector/account_switcher.vala252
-rw-r--r--main/src/ui/conversation_selector/conversation_selector.vala10
-rw-r--r--main/src/ui/main_window.vala5
6 files changed, 332 insertions, 40 deletions
diff --git a/main/data/main_window.ui b/main/data/main_window.ui
index e62c952b..ee094714 100644
--- a/main/data/main_window.ui
+++ b/main/data/main_window.ui
@@ -28,32 +28,44 @@
<property name="child">
<object class="AdwToolbarView">
<child type="top">
- <object class="GtkBox" id="conversation_list_toolbar">
- <property name="spacing">6</property>
- <style>
- <class name="toolbar"/>
- <class name="dino-conversation-list-toolbar"/>
- </style>
+ <object class="GtkOverlay">
<child>
- <object class="GtkMenuButton" id="menu_button">
- <property name="has-frame">False</property>
- <property name="valign">center</property>
+ <object class="GtkBox" id="conversation_list_toolbar">
+ <property name="spacing">6</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <style>
+ <class name="toolbar"/>
+ <class name="dino-conversation-list-toolbar"/>
+ </style>
<child>
- <object class="GtkImage">
- <property name="icon-name">dino-open-menu-symbolic</property>
- <property name="icon-size">normal</property>
+ <object class="GtkMenuButton" id="menu_button">
+ <property name="has-frame">False</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">dino-open-menu-symbolic</property>
+ <property name="icon-size">normal</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSearchEntry" id="conversation_search_entry">
+ <property name="hexpand">True</property>
+ <property name="valign">center</property>
+ <property name="placeholder-text" translatable="yes">Search conversations</property>
+ <style>
+ <class name="dino-conversation-search-entry"/>
+ </style>
</object>
</child>
</object>
</child>
- <child>
- <object class="GtkSearchEntry" id="conversation_search_entry">
+ <child type="overlay">
+ <object class="DinoUiAccountSwitcher" id="account_switcher">
+ <property name="valign">end</property>
<property name="hexpand">True</property>
- <property name="valign">center</property>
- <property name="placeholder-text" translatable="yes">Search conversations</property>
- <style>
- <class name="dino-conversation-search-entry"/>
- </style>
</object>
</child>
</object>
diff --git a/main/data/style.css b/main/data/style.css
index 7038f901..12000b12 100644
--- a/main/data/style.css
+++ b/main/data/style.css
@@ -30,6 +30,7 @@ window.dino-main .dino-conversation-list-toolbar {
margin: 0;
background-color: @window_bg_color;
background-image: none;
+ padding: 0;
}
window.dino-main .dino-conversation-list-toolbar {
@@ -37,26 +38,6 @@ window.dino-main .dino-conversation-list-toolbar {
box-shadow: 0 3px 12px alpha(black, 0.18);
}
-window.dino-main headerbar.dino-chat-headerbar {
- border-bottom: none;
- box-shadow: 0 3px 12px alpha(black, 0.18);
-}
-
-window.dino-main headerbar.dino-chat-headerbar windowtitle {
- min-height: 2.5em;
-}
-
-window.dino-main headerbar.dino-chat-headerbar > windowhandle > box,
-window.dino-main .dino-conversation-list-toolbar {
- padding-top: 0;
- padding-bottom: 0;
-}
-
-window.dino-main .dino-conversation-list-toolbar {
- padding-left: 6px;
- padding-right: 6px;
-}
-
window.dino-main headerbar button,
window.dino-main .dino-conversation-list-toolbar entry,
window.dino-main .dino-conversation-list-toolbar button,
@@ -70,6 +51,39 @@ window.dino-main popover row {
window.dino-main .dino-conversation-list-toolbar entry.dino-conversation-search-entry {
border-radius: 6px;
+ min-height: 28px;
+}
+
+window.dino-main .dino-conversation-list-toolbar menubutton,
+window.dino-main .dino-conversation-list-toolbar menubutton > button {
+ min-height: 28px;
+}
+
+window.dino-main .dino-account-switcher {
+ min-height: 6px;
+ background-color: transparent;
+ margin: 0;
+ padding: 0;
+}
+
+window.dino-main .dino-account-switcher-box,
+window.dino-main .dino-account-switcher-bars {
+ margin: 0;
+ padding: 0;
+}
+
+window.dino-main .dino-account-color-bar {
+ min-height: 6px;
+ margin: 0;
+ padding: 0;
+}
+
+window.dino-main .dino-account-all-bar {
+ min-width: 44px;
+}
+
+window.dino-main .dino-account-all-bar.dimmed {
+ opacity: 0.4;
}
window.dino-main button.circular,
diff --git a/main/meson.build b/main/meson.build
index b733e0c8..ca097dd0 100644
--- a/main/meson.build
+++ b/main/meson.build
@@ -61,6 +61,7 @@ sources = files(
'src/ui/conversation_content_view/subscription_notification.vala',
'src/ui/conversation_content_view/unread_indicator_populator.vala',
'src/ui/conversation_details.vala',
+ 'src/ui/conversation_selector/account_switcher.vala',
'src/ui/conversation_selector/conversation_selector.vala',
'src/ui/conversation_selector/conversation_selector_row.vala',
'src/ui/conversation_titlebar/call_entry.vala',
diff --git a/main/src/ui/conversation_selector/account_switcher.vala b/main/src/ui/conversation_selector/account_switcher.vala
new file mode 100644
index 00000000..500fd96a
--- /dev/null
+++ b/main/src/ui/conversation_selector/account_switcher.vala
@@ -0,0 +1,252 @@
+using Gee;
+using Gdk;
+using Gtk;
+
+using Dino.Entities;
+
+namespace Dino.Ui {
+
+public class AccountSwitcher : Box {
+
+ public signal void account_filter_changed(Account? account);
+
+ public Account? selected_account { get; private set; default = null; }
+
+ private StreamInteractor stream_interactor;
+ private Box bars_box = new Box(Orientation.HORIZONTAL, 0);
+ private HashMap<Account, AccountColorBar> bars = new HashMap<Account, AccountColorBar>(Account.hash_func, Account.equals_func);
+ private AllAccountsBar? all_bar = null;
+ private ulong account_color_changed_handler_id = 0;
+
+ construct {
+ orientation = Orientation.HORIZONTAL;
+ add_css_class("dino-account-switcher");
+ bars_box.margin_start = 0;
+ bars_box.margin_end = 0;
+ bars_box.margin_top = 0;
+ bars_box.margin_bottom = 0;
+ bars_box.hexpand = true;
+ bars_box.halign = Align.FILL;
+ bars_box.add_css_class("dino-account-switcher-bars");
+ append(bars_box);
+ }
+
+ public AccountSwitcher init(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+
+ stream_interactor.account_added.connect(on_account_added);
+ stream_interactor.account_removed.connect(on_account_removed);
+ account_color_changed_handler_id = Dino.Application.get_default().settings.account_color_changed.connect(on_account_color_changed);
+
+ rebuild();
+
+ weak_ref((self_obj) => {
+ AccountSwitcher self = (AccountSwitcher) self_obj;
+ self.stream_interactor.account_added.disconnect(self.on_account_added);
+ self.stream_interactor.account_removed.disconnect(self.on_account_removed);
+ if (self.account_color_changed_handler_id != 0) {
+ Dino.Application.get_default().settings.disconnect(self.account_color_changed_handler_id);
+ self.account_color_changed_handler_id = 0;
+ }
+ });
+
+ return this;
+ }
+
+ private void rebuild() {
+ Widget? child = bars_box.get_first_child();
+ while (child != null) {
+ Widget? next = child.get_next_sibling();
+ bars_box.remove(child);
+ child = next;
+ }
+ bars.clear();
+
+ Gee.List<Account> accounts = stream_interactor.get_accounts();
+ all_bar = new AllAccountsBar(accounts);
+ all_bar.clicked.connect(on_all_clicked);
+ bars_box.append(all_bar);
+
+ foreach (Account account in accounts) {
+ add_account(account);
+ }
+
+ if (selected_account != null && !bars.has_key(selected_account)) {
+ selected_account = null;
+ account_filter_changed(null);
+ }
+
+ update_selection();
+ }
+
+ private void on_account_added(Account account) {
+ if (bars.has_key(account)) return;
+ add_account(account);
+ if (all_bar != null) all_bar.set_accounts(stream_interactor.get_accounts());
+ update_selection();
+ }
+
+ private void on_account_removed(Account account) {
+ if (bars.has_key(account)) {
+ AccountColorBar bar = bars[account];
+ bars.unset(account);
+ bars_box.remove(bar);
+ }
+
+ if (all_bar != null) all_bar.set_accounts(stream_interactor.get_accounts());
+
+ if (selected_account != null && selected_account.equals(account)) {
+ selected_account = null;
+ update_selection();
+ account_filter_changed(null);
+ }
+ }
+
+ private void add_account(Account account) {
+ AccountColorBar bar = new AccountColorBar(account);
+ bar.clicked.connect(() => on_bar_clicked(account));
+ bars_box.append(bar);
+ bars[account] = bar;
+ }
+
+ private void on_all_clicked() {
+ if (selected_account == null) return;
+
+ selected_account = null;
+ update_selection();
+ account_filter_changed(null);
+ }
+
+ private void on_bar_clicked(Account account) {
+ if (selected_account != null && selected_account.equals(account)) {
+ selected_account = null;
+ } else {
+ selected_account = account;
+ }
+
+ update_selection();
+ account_filter_changed(selected_account);
+ }
+
+ private void update_selection() {
+ if (all_bar != null) {
+ all_bar.set_active(selected_account == null);
+ }
+ foreach (var entry in bars.entries) {
+ entry.value.set_active(selected_account != null && entry.key.equals(selected_account));
+ }
+ }
+
+ private void on_account_color_changed(Account account, string color) {
+ if (bars.has_key(account)) {
+ bars[account].redraw();
+ }
+ if (all_bar != null) {
+ all_bar.redraw();
+ }
+ }
+}
+
+private class AccountColorBar : Button {
+
+ public Account account { get; private set; }
+
+ private DrawingArea drawing_area = new DrawingArea() { hexpand=true, vexpand=true };
+ private double bar_opacity = 1.0;
+
+ public AccountColorBar(Account account) {
+ this.account = account;
+
+ add_css_class("flat");
+ add_css_class("dino-account-color-bar");
+ hexpand = true;
+ set_child(drawing_area);
+ drawing_area.set_draw_func(draw);
+
+ set_tooltip_text(account.display_name);
+ }
+
+ public void set_active(bool active) {
+ bar_opacity = active ? 1.0 : 0.4;
+ redraw();
+ }
+
+ public void redraw() {
+ drawing_area.queue_draw();
+ }
+
+ private void draw(DrawingArea area, Cairo.Context context, int width, int height) {
+ RGBA color = RGBA();
+ if (!color.parse(Dino.Application.get_default().settings.get_account_color(account))) return;
+
+ color.alpha = (float) (color.alpha * bar_opacity);
+ Gdk.cairo_set_source_rgba(context, color);
+ context.rectangle(0, 0, width, height);
+ context.fill();
+ }
+}
+
+private class AllAccountsBar : Button {
+
+ private Gee.List<Account>? accounts = null;
+ private DrawingArea drawing_area = new DrawingArea() { hexpand=true, vexpand=true };
+
+ public AllAccountsBar(Gee.List<Account> accounts) {
+ this.accounts = accounts;
+
+ add_css_class("flat");
+ add_css_class("dino-account-color-bar");
+ add_css_class("dino-account-all-bar");
+ set_child(drawing_area);
+ drawing_area.set_draw_func(draw);
+ set_tooltip_text(_("All conversations"));
+ }
+
+ public void set_accounts(Gee.List<Account> accounts) {
+ this.accounts = accounts;
+ redraw();
+ }
+
+ public void redraw() {
+ drawing_area.queue_draw();
+ }
+
+ public void set_active(bool active) {
+ if (active) {
+ remove_css_class("dimmed");
+ } else {
+ add_css_class("dimmed");
+ }
+ }
+
+ private void draw(DrawingArea area, Cairo.Context context, int width, int height) {
+ if (accounts == null || accounts.size == 0) {
+ context.set_source_rgba(0.5, 0.5, 0.5, 0.3);
+ context.rectangle(0, 0, width, height);
+ context.fill();
+ return;
+ }
+
+ if (accounts.size == 1) {
+ RGBA color = RGBA();
+ if (!color.parse(Dino.Application.get_default().settings.get_account_color(accounts[0]))) return;
+ Gdk.cairo_set_source_rgba(context, color);
+ context.rectangle(0, 0, width, height);
+ context.fill();
+ return;
+ }
+
+ Cairo.Pattern gradient = new Cairo.Pattern.linear(0, 0, width, 0);
+ for (int i = 0; i < accounts.size; i++) {
+ RGBA color = RGBA();
+ if (!color.parse(Dino.Application.get_default().settings.get_account_color(accounts[i]))) continue;
+ gradient.add_color_stop_rgba((double) i / (accounts.size - 1), color.red, color.green, color.blue, color.alpha);
+ }
+
+ context.set_source(gradient);
+ context.rectangle(0, 0, width, height);
+ context.fill();
+ }
+}
+
+}
diff --git a/main/src/ui/conversation_selector/conversation_selector.vala b/main/src/ui/conversation_selector/conversation_selector.vala
index 25ea1b1e..c087cde0 100644
--- a/main/src/ui/conversation_selector/conversation_selector.vala
+++ b/main/src/ui/conversation_selector/conversation_selector.vala
@@ -15,6 +15,7 @@ public class ConversationSelector : Widget {
private StreamInteractor stream_interactor;
private string search_text = "";
+ private Account? account_filter = null;
private HashMap<Conversation, ConversationSelectorRow> rows = new HashMap<Conversation, ConversationSelectorRow>(Conversation.hash_func, Conversation.equals_func);
public ConversationSelector init(StreamInteractor stream_interactor) {
@@ -60,9 +61,16 @@ public class ConversationSelector : Widget {
list_box.invalidate_filter();
}
+ public void set_account_filter(Account? account) {
+ account_filter = account;
+ list_box.invalidate_filter();
+ }
+
private bool filter(ListBoxRow list_box_row) {
ConversationSelectorRow? row = list_box_row as ConversationSelectorRow;
- return row != null && (search_text == "" || row.matches_search(search_text));
+ if (row == null) return false;
+ if (account_filter != null && !row.conversation.account.equals(account_filter)) return false;
+ return search_text == "" || row.matches_search(search_text);
}
public void row_activated(ListBoxRow r) {
diff --git a/main/src/ui/main_window.vala b/main/src/ui/main_window.vala
index fc18d2bd..4a20f41d 100644
--- a/main/src/ui/main_window.vala
+++ b/main/src/ui/main_window.vala
@@ -17,6 +17,7 @@ public class MainWindow : Gtk.ApplicationWindow {
[GtkChild] public unowned SearchEntry conversation_search_entry;
[GtkChild] public unowned MenuButton menu_button;
[GtkChild] private unowned Box conversation_list_toolbar;
+ [GtkChild] public unowned AccountSwitcher account_switcher;
[GtkChild] public unowned Adw.HeaderBar conversation_headerbar;
[GtkChild] public unowned Button conversation_title_button;
@@ -72,6 +73,10 @@ public class MainWindow : Gtk.ApplicationWindow {
conversation_search_entry.stop_search.connect(() => {
conversation_search_entry.text = "";
});
+ account_switcher.init(stream_interactor);
+ account_switcher.account_filter_changed.connect((account) => {
+ conversation_selector.set_account_filter(account);
+ });
global_search = new GlobalSearch(stream_interactor);
search_frame.set_child(global_search.get_widget());