Render list of rooms

This commit is contained in:
Joscha 2022-02-24 02:07:54 +01:00
parent 2d31d3d4e2
commit e5910f45b4
2 changed files with 98 additions and 16 deletions

View file

@ -1,16 +1,21 @@
mod rooms; mod rooms;
use std::collections::HashMap;
use std::io::Stdout; use std::io::Stdout;
use std::sync::Arc;
use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, MouseEvent, MouseEventKind}; use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, MouseEvent, MouseEventKind};
use futures::StreamExt; use futures::StreamExt;
use tokio::sync::mpsc::error::TryRecvError; use tokio::sync::mpsc::error::TryRecvError;
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use tokio::sync::Mutex;
use tui::backend::CrosstermBackend; use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout}; use tui::layout::{Constraint, Direction, Layout};
use tui::widgets::Paragraph; use tui::widgets::Paragraph;
use tui::{Frame, Terminal}; use tui::{Frame, Terminal};
use crate::room::Room;
use self::rooms::{Rooms, RoomsState}; use self::rooms::{Rooms, RoomsState};
pub type Backend = CrosstermBackend<Stdout>; pub type Backend = CrosstermBackend<Stdout>;
@ -28,6 +33,7 @@ enum EventHandleResult {
pub struct Ui { pub struct Ui {
event_tx: UnboundedSender<UiEvent>, event_tx: UnboundedSender<UiEvent>,
rooms: HashMap<String, Arc<Mutex<Room>>>,
rooms_state: RoomsState, rooms_state: RoomsState,
log: Vec<String>, log: Vec<String>,
} }
@ -36,6 +42,7 @@ impl Ui {
fn new(event_tx: UnboundedSender<UiEvent>) -> Self { fn new(event_tx: UnboundedSender<UiEvent>) -> Self {
Self { Self {
event_tx, event_tx,
rooms: HashMap::new(),
rooms_state: RoomsState::default(), rooms_state: RoomsState::default(),
log: vec!["Hello world!".to_string()], log: vec!["Hello world!".to_string()],
} }
@ -155,7 +162,8 @@ impl Ui {
]) ])
.split(frame.size()); .split(frame.size());
frame.render_stateful_widget(Rooms::new(), outer[0], &mut self.rooms_state); // frame.render_stateful_widget(Rooms::new(&self.rooms), outer[0], &mut self.rooms_state);
frame.render_stateful_widget(Rooms::dummy(), outer[0], &mut self.rooms_state);
let scroll = if self.log.len() as u16 > outer[1].height { let scroll = if self.log.len() as u16 > outer[1].height {
self.log.len() as u16 - outer[1].height self.log.len() as u16 - outer[1].height

View file

@ -1,15 +1,62 @@
use std::cmp; use std::cmp;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
use tui::buffer::Buffer; use tui::buffer::Buffer;
use tui::layout::Rect; use tui::layout::Rect;
use tui::style::{Modifier, Style}; use tui::style::{Color, Modifier, Style};
use tui::widgets::{Block, Borders, StatefulWidget, Widget}; use tui::text::{Span, Spans};
use tui::widgets::{Block, Borders, Paragraph, StatefulWidget, Widget};
pub struct Rooms {} use crate::room::Room;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct RoomInfo {
name: String,
}
pub struct Rooms {
rooms: Vec<RoomInfo>,
selected: Option<usize>,
}
impl Rooms { impl Rooms {
pub fn new() -> Self { pub fn new(rooms: &HashMap<String, Arc<Mutex<Room>>>) -> Self {
Self {} let mut rooms = rooms
.iter()
.map(|(name, _room)| RoomInfo { name: name.clone() })
.collect::<Vec<_>>();
rooms.sort();
Self {
rooms,
selected: None,
}
}
pub fn select(mut self, name: &str) -> Self {
for (i, room) in self.rooms.iter().enumerate() {
if room.name == name {
self.selected = Some(i);
}
}
self
}
pub fn dummy() -> Self {
fn r(s: &str) -> RoomInfo {
RoomInfo {
name: s.to_string(),
}
}
let mut rooms = vec![r("xkcd"), r("test"), r("welcome"), r("music")];
rooms.sort();
Rooms {
rooms,
selected: None,
}
.select("welcome")
} }
} }
@ -17,12 +64,44 @@ impl StatefulWidget for Rooms {
type State = RoomsState; type State = RoomsState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let title_style = Style::default().add_modifier(Modifier::BOLD);
let empty_style = Style::default()
.fg(Color::Gray)
.add_modifier(Modifier::ITALIC);
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); state.width = cmp::min(state.width, area.width);
// let left = Rect { // Actual room names
// width: area.width - 1, let left = Rect {
// ..area width: area.width - 1,
// }; ..area
};
let mut lines = vec![Spans::from(Span::styled("Rooms", title_style))];
if self.rooms.is_empty() {
lines.push(Spans::from(vec![
Span::raw("\r\n"),
Span::styled("none", empty_style),
]));
}
for (i, room) in self.rooms.iter().enumerate() {
let name = format!("&{}", room.name);
if Some(i) == self.selected {
lines.push(Spans::from(vec![
Span::raw("\n>"),
Span::styled(name, selected_room_style),
]));
} else {
lines.push(Spans::from(vec![
Span::raw("\n "),
Span::styled(name, room_style),
]));
}
}
Paragraph::new(lines).render(left, buf);
// The panel's border
let right = Rect { let right = Rect {
x: area.right() - 1, x: area.right() - 1,
width: 1, width: 1,
@ -40,10 +119,10 @@ impl StatefulWidget for Rooms {
} }
} }
// TODO Figure out some sort of scroll offset solution
#[derive(Debug)] #[derive(Debug)]
pub struct RoomsState { pub struct RoomsState {
width: u16, width: u16,
offset: u16,
hovering: bool, hovering: bool,
dragging: bool, dragging: bool,
} }
@ -52,7 +131,6 @@ impl Default for RoomsState {
fn default() -> Self { fn default() -> Self {
Self { Self {
width: 24, width: 24,
offset: 0,
hovering: false, hovering: false,
dragging: false, dragging: false,
} }
@ -72,10 +150,6 @@ impl RoomsState {
self.dragging = active; self.dragging = active;
} }
pub fn dragging(&self) -> bool {
self.dragging
}
pub fn drag_to(&mut self, width: u16) { pub fn drag_to(&mut self, width: u16) {
if self.dragging { if self.dragging {
self.width = width; self.width = width;