From 2fcb853c305aa9d5e7cef9d9f8b281a942eee4aa Mon Sep 17 00:00:00 2001 From: restitux Date: Wed, 28 Jan 2026 06:03:23 +0000 Subject: [PATCH] gui: improve channel selection behavior (#21) # Summary This change improves the channel selection behavior to be more similar to the official client and generally more usable. It's currently mildly broken due to the details element grabbing click events from the whole row and the row text being selectable. This change also makes it more obvious that the channel title can be clicked. I'm not sure how this works on mobile, so we might need to make more changes there in the future to work better with touchscreens. # Changes - Channels can only be expanded or collapsed by clicking on the adjacent arrow - Expand/collapse arrows are only displayed on channels with children or users - Channel can only be joined by double clicking to the right of the collapse/expand arrow - The channel title background (and the empty space to the right) display a highlight when the user hovers over them. - All text inside the channel view is no longer selectable. # Testing I tested on the desktop client. I didn't test on mobile but I'll give it a shot after I merge and maybe come back with another PR to make this behavior more intuitive over there. Reviewed-on: https://git.ohea.xyz/mumble/mumble-web2/pulls/21 --- gui/assets/main.scss | 40 ++++++++++++++++++++++++++++++++++++- gui/src/app.rs | 47 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/gui/assets/main.scss b/gui/assets/main.scss index 4e7f13c..f6054ab 100644 --- a/gui/assets/main.scss +++ b/gui/assets/main.scss @@ -83,6 +83,44 @@ a:visited { } } + +.channel_header { + display: flex; + flex-direction: row; + align-items: center; +} + +.channel_arrow { + width: 1em; + text-align: center; + margin-right: 0.25rem; +} + +.channel_arrow--placeholder { + pointer-events: none; + visibility: hidden; +} + +/* The whole right side of the row is the dblclick target */ +.channel_row_click { + flex: 1; + padding: 0.1rem 0.25rem 0.1rem 0.5rem; + cursor: pointer; +} + +/* Hover highlight for whole row area (title + blank space) */ +.channel_row_click:hover { + background-color: var(--channel-hover-bg, #222); /* pick your color */ +} + + +/* still keep text non-selectable if desired */ +.channel_details { + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} + .channel { &_details { flex: 0 0 100%; @@ -392,4 +430,4 @@ a:visited { color: red; } } -} \ No newline at end of file +} diff --git a/gui/src/app.rs b/gui/src/app.rs index 0a865d3..d60dad1 100644 --- a/gui/src/app.rs +++ b/gui/src/app.rs @@ -291,22 +291,53 @@ pub fn Channel(id: ChannelId) -> Element { return rsx!("missing channel {id}"); }; + let mut open = use_signal(|| true); + + let has_children = !state.users.is_empty() || !state.children.is_empty(); + rsx!( - details { + div { class: "channel_details", - open: true, - summary { - span { - role: "button", - ondoubleclick: move |evt| { + + div { + class: "channel_header", + // Arrow: only toggles open + if has_children { + span { + class: "channel_arrow", + onclick: move |evt| { + evt.stop_propagation(); + evt.prevent_default(); + let mut w = open.write(); + *w = !*w; + }, + if *open.read() { "▾" } else { "▸" } + } + } else { + span { + class: "channel_arrow channel_arrow--placeholder", + " " + } + } + + // Clickable row area (everything except the arrow) + div { + class: "channel_row_click", + ondblclick: move |evt| { evt.stop_propagation(); evt.prevent_default(); net.send(EnterChannel { channel: id, user }) }, - "{state.name}" + // remove dblclick from the inner span + span { + class: "channel_title", + "{state.name}" + } + // if you add icons/badges later, put them here too } } - if state.users.len() + state.children.len() > 0 { + + if *open.read() && has_children { div { class: "channel_children", for id in state.users.iter() {