From df19641c191a19297f144fe49ae336b286678d8d Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Fri, 16 Jan 2026 15:49:20 -0800 Subject: [PATCH 1/8] temp progress. UI looks ok but isn't perfect yet. --- src/home/mod.rs | 3 +- src/home/rooms_list.rs | 19 + src/home/space_lobby.rs | 765 ++++++++++++++++++++++++++++++++++++-- src/space_service_sync.rs | 89 ++++- 4 files changed, 849 insertions(+), 27 deletions(-) diff --git a/src/home/mod.rs b/src/home/mod.rs index 40f91289..7ed2fa8b 100644 --- a/src/home/mod.rs +++ b/src/home/mod.rs @@ -1,4 +1,4 @@ -use makepad_widgets::Cx; +use makepad_widgets::{Cx, LiveRegister}; pub mod add_room; pub mod edited_indicator; @@ -34,6 +34,7 @@ pub fn live_design(cx: &mut Cx) { location_preview::live_design(cx); add_room::live_design(cx); space_lobby::live_design(cx); + space_lobby::DrawTreeLine::live_register(cx); rooms_list_entry::live_design(cx); rooms_list_header::live_design(cx); rooms_list::live_design(cx); diff --git a/src/home/rooms_list.rs b/src/home/rooms_list.rs index 165a037d..9dddcc28 100644 --- a/src/home/rooms_list.rs +++ b/src/home/rooms_list.rs @@ -1034,6 +1034,8 @@ impl RoomsList { kind: PopupKind::Error, }); } + // DetailedChildren is handled by SpaceLobbyScreen, not RoomsList. + SpaceRoomListAction::DetailedChildren { .. } => { } } } @@ -1395,6 +1397,23 @@ impl RoomsListRef { pub fn get_selected_space_id(&self) -> Option { self.borrow()?.selected_space.as_ref().map(|ss| ss.room_id().clone()) } + + /// Returns a clone of the space request sender channel, if available. + /// + /// This allows other widgets to submit space-related requests directly + /// to the background space service. + pub fn get_space_request_sender(&self) -> Option> { + self.borrow()?.space_request_sender.clone() + } + + /// Returns the set of direct child rooms and subspaces for the given space. + /// + /// Returns a tuple of `(direct_child_rooms, direct_subspaces)`. + pub fn get_space_children(&self, space_id: &OwnedRoomId) -> Option<(Arc>, Arc>)> { + let inner = self.borrow()?; + let smv = inner.space_map.get(space_id)?; + Some((Arc::clone(&smv.direct_child_rooms), Arc::clone(&smv.direct_subspaces))) + } } pub struct RoomsListScopeProps { diff --git a/src/home/space_lobby.rs b/src/home/space_lobby.rs index 9a7c60d4..118672ba 100644 --- a/src/home/space_lobby.rs +++ b/src/home/space_lobby.rs @@ -6,8 +6,16 @@ //! that allows the user to click on it to show the `SpaceLobby`. //! +use std::{collections::{HashMap, HashSet}, sync::Arc}; use makepad_widgets::*; -use crate::utils::RoomNameId; +use matrix_sdk::ruma::OwnedRoomId; +use tokio::sync::mpsc::UnboundedSender; +use crate::{ + home::rooms_list::RoomsListRef, + shared::avatar::{AvatarWidgetExt, AvatarWidgetRefExt}, + space_service_sync::{ParentChain, SpaceRequest, SpaceRoomInfo, SpaceRoomListAction}, + utils::{self, RoomNameId}, +}; live_design! { @@ -19,6 +27,8 @@ live_design! { use crate::shared::helpers::*; use crate::shared::avatar::*; + ICON_COLLAPSE = dep("crate://self/resources/icons/triangle_fill.svg") + // An entry in the RoomsList that will show the SpaceLobby when clicked. pub SpaceLobbyEntry = {{SpaceLobbyEntry}} { visible: false, // only visible when a space is selected @@ -171,26 +181,300 @@ live_design! { } } + // A view that draws the hierarchical tree structure lines. + DrawTreeLine = {{DrawTreeLine}} { + + } + + TreeLines = {{TreeLines}} { + width: 0, height: Fill + draw_bg: { + indent_width: 44.0 + level: 0.0 + is_last: 0.0 + parent_mask: 0.0 + + fn pixel(self) -> vec4 { + let pos = self.pos * self.rect_size; + let indent = self.indent_width; + let half_indent = indent * 0.5; + let line_width = 1.0; + let half_line = 0.5; + + let c = vec4(0.0); + + // Iterate for parent levels and current level + for i in 0..20 { + if float(i) > self.level { break; } + + if float(i) < self.level { + // Check mask for parent levels + let mask_bit = mod(floor(self.parent_mask / pow(2.0, float(i))), 2.0); + if mask_bit > 0.5 { + // Draw full vertical line + if abs(pos.x - (float(i) * indent + half_indent)) < half_line && pos.y < self.rect_size.y { + return vec4(0.8, 0.8, 0.8, 1.0); + } + } + } else { + // Current level: connection to self + + // Horizontal line to content + let hy = self.rect_size.y * 0.5; + if abs(pos.y - hy) < half_line && pos.x > (float(i) * indent + half_indent) { + return vec4(0.8, 0.8, 0.8, 1.0); + } + + // Vertical line (L shape) + if abs(pos.x - (float(i) * indent + half_indent)) < half_line && pos.y < (self.rect_size.y * (1.0 - 0.5 * self.is_last)) { + return vec4(0.8, 0.8, 0.8, 1.0); + } + } + } + return c; + } + } + } + + // Entry for a child subspace (can be expanded) + pub SubspaceEntry = {{SubspaceEntry}} { + width: Fill, + height: 44, + flow: Right, + align: {y: 0.5} + padding: {left: 8, right: 12, top: 4, bottom: 4} + cursor: Hand + + show_bg: true + draw_bg: { + instance hover: 0.0 + color: #fff + uniform color_hover: #f5f5f5 + fn pixel(self) -> vec4 { + return mix(self.color, self.color_hover, self.hover); + } + } + + // Tree lines replace the spacer + tree_lines = {} + + // Expand/collapse icon + expand_icon = { + width: 16, + height: 16, + margin: { right: 4 } + draw_icon: { + svg_file: (ICON_COLLAPSE) + rotation_angle: 90.0 + color: #888 + } + icon_walk: { width: 10, height: 10 } + } + + avatar = { width: 32, height: 32, margin: {right: 12} } + + content = { + width: Fill, height: Fit, flow: Down, spacing: 0, + name_label =