diff --git a/crates/cordiale-ui/src/channels.rs b/crates/cordiale-ui/src/channels.rs index 14f017d..c4c0697 100644 --- a/crates/cordiale-ui/src/channels.rs +++ b/crates/cordiale-ui/src/channels.rs @@ -38,6 +38,11 @@ pub(crate) async fn handle_select_channel( state.windows.current_query = false; state.windows.current_query_ready = false; open_window(state, &key); + state + .windows + .window_messages + .insert(window_counts_key(&network, &channel), 0); + refresh_network_groups(state, ui); push_mute_bar(state, ui); let mut settings = persistence::load_settings().unwrap_or_default(); diff --git a/crates/cordiale-ui/src/frames.rs b/crates/cordiale-ui/src/frames.rs index 9cdffaf..d2169be 100644 --- a/crates/cordiale-ui/src/frames.rs +++ b/crates/cordiale-ui/src/frames.rs @@ -1224,8 +1224,12 @@ pub(crate) fn apply_window_counts_join_reply( fn apply_window_count_snapshot( state: &mut WorkerState, key: WindowCountsKey, - counts: WindowCountSnapshot, + mut counts: WindowCountSnapshot, ) -> bool { + // Whatever arrives in the window being read is read already. + if selected_window(state) == Some((key.clone(), false)) { + counts.messages = 0; + } let mentions_changed = apply_window_mention_count(state, key.clone(), counts.mentions); let messages_changed = state.windows.window_messages.get(&key) != Some(&counts.messages); if messages_changed { diff --git a/crates/cordiale-ui/src/main.rs b/crates/cordiale-ui/src/main.rs index 8a63284..2139d97 100644 --- a/crates/cordiale-ui/src/main.rs +++ b/crates/cordiale-ui/src/main.rs @@ -2965,6 +2965,7 @@ async fn finish_connect( window_states, window_mentions, window_messages, + None, ); ui.set_sidebar_widest_label(widest_sidebar_label(&groups).into()); ui.set_network_groups(Rc::new(slint::VecModel::from(groups)).into()); @@ -7562,6 +7563,16 @@ fn apply_connecting_labels( } } +/// The open window as a counts key plus whether it is a query, for the +/// sidebar's selected row. +fn selected_window(state: &WorkerState) -> Option<(WindowCountsKey, bool)> { + let (network, target) = state.windows.current_channel.as_ref()?; + Some(( + window_counts_key(network, target), + state.windows.current_query, + )) +} + /// Builds the actual sidebar `NetworkGroup` Slint model out of /// `network_groups_data`'s plain grouping — must run on the UI thread, /// see that function's doc comment for why. @@ -7570,6 +7581,7 @@ fn network_groups_model( window_states: HashMap<(String, String), ChannelWindowState>, window_mentions: HashMap, window_messages: HashMap, + selected: Option<(WindowCountsKey, bool)>, ) -> Vec { data.into_iter() .enumerate() @@ -7594,6 +7606,9 @@ fn network_groups_model( .get(&window_counts_key(&network, &channel)) .copied(), ); + let selected = selected.as_ref().is_some_and(|(key, query)| { + !query && *key == window_counts_key(&network, &channel) + }); ChannelEntry { network: network.clone().into(), channel: channel.into(), @@ -7606,6 +7621,7 @@ fn network_groups_model( kicked, invited, joined, + selected, } }) .collect(); @@ -7617,12 +7633,16 @@ fn network_groups_model( .copied() .map(mention_count_labels) .unwrap_or_default(); + let selected = selected.as_ref().is_some_and(|(key, query)| { + *query && *key == window_counts_key(&network, &nick) + }); QueryEntry { network: network.clone().into(), nick: nick.into(), label: label.into(), mention_badge: mention_badge.into(), mentions_description: mentions_description.into(), + selected, } }) .collect(); @@ -7702,9 +7722,16 @@ fn refresh_network_groups(state: &WorkerState, ui: &slint::Weak) { let window_states = state.windows.window_states.clone(); let window_mentions = state.windows.window_mentions.clone(); let window_messages = state.windows.window_messages.clone(); + let selected = selected_window(state); let ui = ui.clone(); let _ = ui.upgrade_in_event_loop(move |ui| { - let groups = network_groups_model(data, window_states, window_mentions, window_messages); + let groups = network_groups_model( + data, + window_states, + window_mentions, + window_messages, + selected, + ); ui.set_sidebar_widest_label(widest_sidebar_label(&groups).into()); ui.set_network_groups(Rc::new(slint::VecModel::from(groups)).into()); }); diff --git a/crates/cordiale-ui/src/queries.rs b/crates/cordiale-ui/src/queries.rs index e4649ae..558b8bc 100644 --- a/crates/cordiale-ui/src/queries.rs +++ b/crates/cordiale-ui/src/queries.rs @@ -31,6 +31,7 @@ pub(crate) async fn handle_select_query( state.windows.current_query = true; state.windows.current_query_ready = state.transcript.query_ready.contains(&identity); open_window(state, &key); + refresh_network_groups(state, ui); push_mute_bar(state, ui); show_query_window(state, ui, &query, &key); diff --git a/crates/cordiale-ui/ui/appwindow.slint b/crates/cordiale-ui/ui/appwindow.slint index 1661929..9fc4dc0 100644 --- a/crates/cordiale-ui/ui/appwindow.slint +++ b/crates/cordiale-ui/ui/appwindow.slint @@ -39,6 +39,7 @@ struct ChannelEntry { kicked: bool, invited: bool, joined: bool, + selected: bool, } // One open private-conversation window under its network. @@ -48,6 +49,7 @@ struct QueryEntry { label: string, mention-badge: string, mentions-description: string, + selected: bool, } // One row of a server reply screen (WHO/WHOIS/WHOWAS/MOTD/ban list/LUSERS). @@ -3207,8 +3209,13 @@ export component AppWindow inherits Window { Button { horizontal-stretch: 1; - text: (entry.invited ? "🔔 " : "") + entry.label + entry.mention-badge; + text: (entry.selected ? "● " : "") + (entry.invited ? "🔔 " : "") + entry.label + entry.mention-badge; + // The filled (primary) style marks the open + // channel; the bullet keeps it readable when + // a theme gives the two styles little contrast. + primary: entry.selected; accessible-label: entry.label + entry.mentions-description + + (entry.selected ? " — current" : "") + (entry.invited ? " — invited" : (entry.kicked @@ -3250,9 +3257,11 @@ export component AppWindow inherits Window { for query in group.queries : Button { horizontal-stretch: 1; - text: "↳ " + query.label + query.mention-badge; + text: (query.selected ? "● " : "") + "↳ " + query.label + query.mention-badge; + primary: query.selected; accessible-label: @tr("Private conversation with {} on {}", query.nick, query.network) - + query.mentions-description; + + query.mentions-description + + (query.selected ? " — current" : ""); clicked => { query-selected(query.network, query.nick); } } }