From d1ce57af27e5958975261ae2fac6bc6ddb417f2e Mon Sep 17 00:00:00 2001 From: predrag Date: Thu, 29 Jan 2026 09:39:44 +0100 Subject: [PATCH] feat: add sync symbol to all panels button Add a "Sync All" button in the expanded ticker card that applies the selected symbol to all active panels at once, preserving each panel's chart type (Heatmap, Footprint, Candlestick, etc.). Changes: - Add SyncToAllPanes action in tickers_table and sidebar - Add switch_all_panes_to_ticker() method in dashboard - Add "Sync All" button in expanded ticker card header --- src/main.rs | 12 ++++++++++ src/screen/dashboard.rs | 32 +++++++++++++++++++++++++++ src/screen/dashboard/sidebar.rs | 4 ++++ src/screen/dashboard/tickers_table.rs | 28 ++++++++++++++++++++++- 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 6286c462..5ab7650b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -485,6 +485,18 @@ impl Flowsurface { event: msg, }); } + Some(dashboard::sidebar::Action::SyncToAllPanes(ticker_info)) => { + let main_window_id = self.main_window.id; + + let task = self + .active_dashboard_mut() + .switch_all_panes_to_ticker(main_window_id, ticker_info); + + return task.map(move |msg| Message::Dashboard { + layout_id: None, + event: msg, + }); + } Some(dashboard::sidebar::Action::ErrorOccurred(err)) => { self.notifications.push(Toast::error(err.to_string())); } diff --git a/src/screen/dashboard.rs b/src/screen/dashboard.rs index b41a6abc..e982b077 100644 --- a/src/screen/dashboard.rs +++ b/src/screen/dashboard.rs @@ -790,6 +790,38 @@ impl Dashboard { } } + pub fn switch_all_panes_to_ticker( + &mut self, + main_window: window::Id, + ticker_info: TickerInfo, + ) -> Task { + let pane_infos: Vec<(window::Id, pane_grid::Pane, ContentKind)> = self + .iter_all_panes_mut(main_window) + .filter_map(|(window, pane, state)| { + // Skip Starter panes (empty panes) + match state.content.kind() { + ContentKind::Starter => None, + kind => Some((window, pane, kind)), + } + }) + .collect(); + + if pane_infos.is_empty() { + return Task::done(Message::Notification(Toast::warn( + "No active panels to sync".to_string(), + ))); + } + + let tasks: Vec> = pane_infos + .iter() + .map(|(window, pane, content_kind)| { + self.init_pane(main_window, *window, *pane, ticker_info, *content_kind) + }) + .collect(); + + Task::batch(tasks) + } + pub fn toggle_trade_fetch(&mut self, is_enabled: bool, main_window: &Window) { exchange::fetcher::toggle_trade_fetch(is_enabled); diff --git a/src/screen/dashboard/sidebar.rs b/src/screen/dashboard/sidebar.rs index 07607154..b1464402 100644 --- a/src/screen/dashboard/sidebar.rs +++ b/src/screen/dashboard/sidebar.rs @@ -31,6 +31,7 @@ pub enum Action { exchange::TickerInfo, Option, ), + SyncToAllPanes(exchange::TickerInfo), ErrorOccurred(data::InternalError), } @@ -70,6 +71,9 @@ impl Sidebar { Some(Action::TickerSelected(ticker_info, content)), ); } + Some(tickers_table::Action::SyncToAllPanes(ticker_info)) => { + return (Task::none(), Some(Action::SyncToAllPanes(ticker_info))); + } Some(tickers_table::Action::Fetch(task)) => { return (task.map(Message::TickersTable), None); } diff --git a/src/screen/dashboard/tickers_table.rs b/src/screen/dashboard/tickers_table.rs index d01b473d..86893005 100644 --- a/src/screen/dashboard/tickers_table.rs +++ b/src/screen/dashboard/tickers_table.rs @@ -73,6 +73,7 @@ pub fn fetch_tickers_info() -> Task { pub enum Action { TickerSelected(TickerInfo, Option), + SyncToAllPanes(TickerInfo), ErrorOccurred(data::InternalError), Fetch(Task), FocusWidget(iced::widget::Id), @@ -84,6 +85,7 @@ pub enum Message { ChangeSortOption(SortOptions), ShowSortingOptions, TickerSelected(Ticker, Option), + SyncToAllPanes(Ticker), ExpandTickerCard(Option), FavoriteTicker(Ticker), Scrolled(scrollable::Viewport), @@ -202,6 +204,18 @@ impl TickersTable { ); } } + Message::SyncToAllPanes(ticker) => { + let ticker_info = self.tickers_info.get(&ticker).cloned().flatten(); + + if let Some(ticker_info) = ticker_info { + return Some(Action::SyncToAllPanes(ticker_info)); + } else { + log::warn!( + "Ticker info not found for {ticker:?} on {:?}", + ticker.exchange + ); + } + } Message::ToggleTable => { self.is_shown = !self.is_shown; @@ -1284,8 +1298,20 @@ fn expanded_ticker_card<'a>( }) .on_press(Message::FavoriteTicker(*ticker)) .style(move |theme, status| { style::button::transparent(theme, status, false) }), + Space::new().width(Length::Fill).height(Length::Shrink), + button( + row![ + icon_text(Icon::Link, 12), + text("Sync All").size(11) + ] + .spacing(4) + .align_y(Alignment::Center) + ) + .on_press(Message::SyncToAllPanes(*ticker)) + .style(|theme, status| style::button::confirm(theme, status, false)), ] - .spacing(2), + .spacing(2) + .align_y(Alignment::Center), row![ icon_text(exchange_icon, 12), text(