diff --git a/cove-tui/src/ui.rs b/cove-tui/src/ui.rs index 46e5af4..e0a1364 100644 --- a/cove-tui/src/ui.rs +++ b/cove-tui/src/ui.rs @@ -1,6 +1,7 @@ mod input; mod layout; mod overlays; +mod pane; mod room; mod rooms; mod textline; @@ -16,7 +17,7 @@ use tokio::sync::mpsc::error::TryRecvError; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use tokio::sync::Mutex; use tui::backend::CrosstermBackend; -use tui::layout::{Constraint, Direction, Layout}; +use tui::layout::{Constraint, Direction, Layout, Rect}; use tui::{Frame, Terminal}; use crate::config::Config; @@ -25,8 +26,9 @@ use crate::ui::overlays::OverlayReaction; use self::input::EventHandler; use self::overlays::{JoinRoom, JoinRoomState}; +use self::pane::PaneInfo; use self::room::RoomInfo; -use self::rooms::{Rooms, RoomsState}; +use self::rooms::Rooms; pub type Backend = CrosstermBackend; @@ -49,9 +51,14 @@ pub struct Ui { config: &'static Config, event_tx: UnboundedSender, rooms: HashMap>>, - rooms_state: RoomsState, + + rooms_pane: PaneInfo, + users_pane: PaneInfo, + room: Option, overlay: Option, + + last_area: Rect, } impl Ui { @@ -60,9 +67,14 @@ impl Ui { config, event_tx, rooms: HashMap::new(), - rooms_state: RoomsState::default(), + + rooms_pane: PaneInfo::default(), + users_pane: PaneInfo::default(), + room: None, overlay: None, + + last_area: Rect::default(), } } @@ -98,6 +110,7 @@ impl Ui { terminal.autoresize()?; let mut frame = terminal.get_frame(); + self.last_area = frame.size(); self.render(&mut frame).await?; // Do a little dance to please the borrow checker @@ -178,13 +191,27 @@ impl Ui { } async fn handle_mouse_event(&mut self, event: MouseEvent) -> anyhow::Result { - let rooms_width = event.column + 1; - let over_rooms = self.rooms_state.width() == rooms_width; + let rooms_width = event.column; + let users_width = self.last_area.width - event.column - 1; + let rooms_hover = rooms_width == self.rooms_pane.width(); + let users_hover = users_width == self.users_pane.width(); match event.kind { - MouseEventKind::Moved => self.rooms_state.hover(over_rooms), - MouseEventKind::Down(_) => self.rooms_state.drag(over_rooms), - MouseEventKind::Up(_) => self.rooms_state.drag(false), - MouseEventKind::Drag(_) => self.rooms_state.drag_to(rooms_width), + MouseEventKind::Moved => { + self.rooms_pane.hover(rooms_hover); + self.users_pane.hover(users_hover); + } + MouseEventKind::Down(_) => { + self.rooms_pane.drag(rooms_hover); + self.users_pane.drag(users_hover); + } + MouseEventKind::Up(_) => { + self.rooms_pane.drag(false); + self.users_pane.drag(false); + } + MouseEventKind::Drag(_) => { + self.rooms_pane.drag_to(rooms_width); + self.users_pane.drag_to(users_width); + } // MouseEventKind::ScrollDown => todo!(), // MouseEventKind::ScrollUp => todo!(), _ => {} @@ -197,22 +224,29 @@ impl Ui { let areas = Layout::default() .direction(Direction::Horizontal) .constraints([ - Constraint::Length(self.rooms_state.width()), // Rooms list - Constraint::Min(1), // Main panel + Constraint::Length(self.rooms_pane.width()), + Constraint::Length(1), + Constraint::Min(0), + Constraint::Length(1), + Constraint::Length(self.users_pane.width()), ]) .split(entire_area); - let rooms_list_area = areas[0]; - let main_panel_area = areas[1]; + let rooms_pane_area = areas[0]; + let rooms_pane_border = areas[1]; + let main_pane_area = areas[2]; + let users_pane_border = areas[3]; + let users_pane_area = areas[4]; - // Rooms list - frame.render_stateful_widget( - Rooms::new(&self.rooms), - rooms_list_area, - &mut self.rooms_state, - ); + // Rooms pane + frame.render_widget(Rooms::new(&self.rooms), rooms_pane_area); - // Main panel - // TODO Implement + // TODO Main pane and users pane + + // Pane borders and width + self.rooms_pane.restrict_width(rooms_pane_area.width); + frame.render_widget(self.rooms_pane.border(), rooms_pane_border); + self.users_pane.restrict_width(users_pane_area.width); + frame.render_widget(self.users_pane.border(), users_pane_border); // Overlays if let Some(overlay) = &mut self.overlay { diff --git a/cove-tui/src/ui/pane.rs b/cove-tui/src/ui/pane.rs new file mode 100644 index 0000000..7807097 --- /dev/null +++ b/cove-tui/src/ui/pane.rs @@ -0,0 +1,69 @@ +use tui::buffer::Buffer; +use tui::layout::Rect; +use tui::style::{Modifier, Style}; +use tui::widgets::{Block, Borders, Widget}; + +#[derive(Debug)] +pub struct PaneInfo { + width: u16, + hovering: bool, + dragging: bool, +} + +impl Default for PaneInfo { + fn default() -> Self { + Self { + width: 24, + hovering: false, + dragging: false, + } + } +} + +impl PaneInfo { + pub fn width(&self) -> u16 { + self.width + } + + pub fn restrict_width(&mut self, width: u16) { + self.width = self.width.min(width); + } + + pub fn hover(&mut self, active: bool) { + self.hovering = active; + } + + pub fn drag(&mut self, active: bool) { + self.dragging = active; + } + + pub fn drag_to(&mut self, width: u16) { + if self.dragging { + self.width = width; + } + } +} + +// Rendering the pane's border (not part of the pane's area) + +struct Border { + hovering: bool, +} + +impl Widget for Border { + fn render(self, area: Rect, buf: &mut Buffer) { + let mut block = Block::default().borders(Borders::LEFT); + if self.hovering { + block = block.style(Style::default().add_modifier(Modifier::REVERSED)); + } + block.render(area, buf); + } +} + +impl PaneInfo { + pub fn border(&self) -> impl Widget { + Border { + hovering: self.hovering, + } + } +} diff --git a/cove-tui/src/ui/rooms.rs b/cove-tui/src/ui/rooms.rs index f0edd97..2a28162 100644 --- a/cove-tui/src/ui/rooms.rs +++ b/cove-tui/src/ui/rooms.rs @@ -1,4 +1,3 @@ -use std::cmp; use std::collections::HashMap; use std::sync::Arc; @@ -7,7 +6,7 @@ use tui::buffer::Buffer; use tui::layout::Rect; use tui::style::{Color, Modifier, Style}; use tui::text::{Span, Spans}; -use tui::widgets::{Block, Borders, Paragraph, StatefulWidget, Widget}; +use tui::widgets::{Paragraph, Widget}; use crate::room::Room; @@ -60,21 +59,12 @@ impl Rooms { } } -impl StatefulWidget for Rooms { - type State = RoomsState; - - fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { +impl Widget for Rooms { + fn render(self, area: Rect, buf: &mut Buffer) { let title_style = Style::default().add_modifier(Modifier::BOLD); let room_style = Style::default().fg(Color::LightBlue); let selected_room_style = room_style.add_modifier(Modifier::BOLD); - state.width = cmp::min(state.width, area.width); - - // Actual room names - let left = Rect { - width: area.width - 1, - ..area - }; let title = if let Some(selected) = self.selected { format!("Rooms ({}/{})", selected + 1, self.rooms.len()) } else { @@ -95,60 +85,6 @@ impl StatefulWidget for Rooms { ])); } } - Paragraph::new(lines).render(left, buf); - - // The panel's border - let right = Rect { - x: area.right() - 1, - width: 1, - ..area - }; - let style = if state.hovering { - Style::default().add_modifier(Modifier::REVERSED) - } else { - Style::default() - }; - Block::default() - .borders(Borders::RIGHT) - .style(style) - .render(right, buf); - } -} - -// TODO Figure out some sort of scroll offset solution -#[derive(Debug)] -pub struct RoomsState { - width: u16, - hovering: bool, - dragging: bool, -} - -impl Default for RoomsState { - fn default() -> Self { - Self { - width: 24, - hovering: false, - dragging: false, - } - } -} - -impl RoomsState { - pub fn width(&self) -> u16 { - self.width - } - - pub fn hover(&mut self, active: bool) { - self.hovering = active; - } - - pub fn drag(&mut self, active: bool) { - self.dragging = active; - } - - pub fn drag_to(&mut self, width: u16) { - if self.dragging { - self.width = width; - } + Paragraph::new(lines).render(area, buf); } }