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 bars = new HashMap(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 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? accounts = null; private DrawingArea drawing_area = new DrawingArea() { hexpand=true, vexpand=true }; public AllAccountsBar(Gee.List 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 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(); } } }