Extract pane border rendering to main UI
This commit is contained in:
parent
3efca6a6d1
commit
ccf6a59f39
3 changed files with 129 additions and 90 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
mod input;
|
mod input;
|
||||||
mod layout;
|
mod layout;
|
||||||
mod overlays;
|
mod overlays;
|
||||||
|
mod pane;
|
||||||
mod room;
|
mod room;
|
||||||
mod rooms;
|
mod rooms;
|
||||||
mod textline;
|
mod textline;
|
||||||
|
|
@ -16,7 +17,7 @@ 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 tokio::sync::Mutex;
|
||||||
use tui::backend::CrosstermBackend;
|
use tui::backend::CrosstermBackend;
|
||||||
use tui::layout::{Constraint, Direction, Layout};
|
use tui::layout::{Constraint, Direction, Layout, Rect};
|
||||||
use tui::{Frame, Terminal};
|
use tui::{Frame, Terminal};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
|
@ -25,8 +26,9 @@ use crate::ui::overlays::OverlayReaction;
|
||||||
|
|
||||||
use self::input::EventHandler;
|
use self::input::EventHandler;
|
||||||
use self::overlays::{JoinRoom, JoinRoomState};
|
use self::overlays::{JoinRoom, JoinRoomState};
|
||||||
|
use self::pane::PaneInfo;
|
||||||
use self::room::RoomInfo;
|
use self::room::RoomInfo;
|
||||||
use self::rooms::{Rooms, RoomsState};
|
use self::rooms::Rooms;
|
||||||
|
|
||||||
pub type Backend = CrosstermBackend<Stdout>;
|
pub type Backend = CrosstermBackend<Stdout>;
|
||||||
|
|
||||||
|
|
@ -49,9 +51,14 @@ pub struct Ui {
|
||||||
config: &'static Config,
|
config: &'static Config,
|
||||||
event_tx: UnboundedSender<UiEvent>,
|
event_tx: UnboundedSender<UiEvent>,
|
||||||
rooms: HashMap<String, Arc<Mutex<Room>>>,
|
rooms: HashMap<String, Arc<Mutex<Room>>>,
|
||||||
rooms_state: RoomsState,
|
|
||||||
|
rooms_pane: PaneInfo,
|
||||||
|
users_pane: PaneInfo,
|
||||||
|
|
||||||
room: Option<RoomInfo>,
|
room: Option<RoomInfo>,
|
||||||
overlay: Option<Overlay>,
|
overlay: Option<Overlay>,
|
||||||
|
|
||||||
|
last_area: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ui {
|
impl Ui {
|
||||||
|
|
@ -60,9 +67,14 @@ impl Ui {
|
||||||
config,
|
config,
|
||||||
event_tx,
|
event_tx,
|
||||||
rooms: HashMap::new(),
|
rooms: HashMap::new(),
|
||||||
rooms_state: RoomsState::default(),
|
|
||||||
|
rooms_pane: PaneInfo::default(),
|
||||||
|
users_pane: PaneInfo::default(),
|
||||||
|
|
||||||
room: None,
|
room: None,
|
||||||
overlay: None,
|
overlay: None,
|
||||||
|
|
||||||
|
last_area: Rect::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,6 +110,7 @@ impl Ui {
|
||||||
terminal.autoresize()?;
|
terminal.autoresize()?;
|
||||||
|
|
||||||
let mut frame = terminal.get_frame();
|
let mut frame = terminal.get_frame();
|
||||||
|
self.last_area = frame.size();
|
||||||
self.render(&mut frame).await?;
|
self.render(&mut frame).await?;
|
||||||
|
|
||||||
// Do a little dance to please the borrow checker
|
// 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<EventHandleResult> {
|
async fn handle_mouse_event(&mut self, event: MouseEvent) -> anyhow::Result<EventHandleResult> {
|
||||||
let rooms_width = event.column + 1;
|
let rooms_width = event.column;
|
||||||
let over_rooms = self.rooms_state.width() == rooms_width;
|
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 {
|
match event.kind {
|
||||||
MouseEventKind::Moved => self.rooms_state.hover(over_rooms),
|
MouseEventKind::Moved => {
|
||||||
MouseEventKind::Down(_) => self.rooms_state.drag(over_rooms),
|
self.rooms_pane.hover(rooms_hover);
|
||||||
MouseEventKind::Up(_) => self.rooms_state.drag(false),
|
self.users_pane.hover(users_hover);
|
||||||
MouseEventKind::Drag(_) => self.rooms_state.drag_to(rooms_width),
|
}
|
||||||
|
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::ScrollDown => todo!(),
|
||||||
// MouseEventKind::ScrollUp => todo!(),
|
// MouseEventKind::ScrollUp => todo!(),
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -197,22 +224,29 @@ impl Ui {
|
||||||
let areas = Layout::default()
|
let areas = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Length(self.rooms_state.width()), // Rooms list
|
Constraint::Length(self.rooms_pane.width()),
|
||||||
Constraint::Min(1), // Main panel
|
Constraint::Length(1),
|
||||||
|
Constraint::Min(0),
|
||||||
|
Constraint::Length(1),
|
||||||
|
Constraint::Length(self.users_pane.width()),
|
||||||
])
|
])
|
||||||
.split(entire_area);
|
.split(entire_area);
|
||||||
let rooms_list_area = areas[0];
|
let rooms_pane_area = areas[0];
|
||||||
let main_panel_area = areas[1];
|
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
|
// Rooms pane
|
||||||
frame.render_stateful_widget(
|
frame.render_widget(Rooms::new(&self.rooms), rooms_pane_area);
|
||||||
Rooms::new(&self.rooms),
|
|
||||||
rooms_list_area,
|
|
||||||
&mut self.rooms_state,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Main panel
|
// TODO Main pane and users pane
|
||||||
// TODO Implement
|
|
||||||
|
// 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
|
// Overlays
|
||||||
if let Some(overlay) = &mut self.overlay {
|
if let Some(overlay) = &mut self.overlay {
|
||||||
|
|
|
||||||
69
cove-tui/src/ui/pane.rs
Normal file
69
cove-tui/src/ui/pane.rs
Normal file
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use std::cmp;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -7,7 +6,7 @@ use tui::buffer::Buffer;
|
||||||
use tui::layout::Rect;
|
use tui::layout::Rect;
|
||||||
use tui::style::{Color, Modifier, Style};
|
use tui::style::{Color, Modifier, Style};
|
||||||
use tui::text::{Span, Spans};
|
use tui::text::{Span, Spans};
|
||||||
use tui::widgets::{Block, Borders, Paragraph, StatefulWidget, Widget};
|
use tui::widgets::{Paragraph, Widget};
|
||||||
|
|
||||||
use crate::room::Room;
|
use crate::room::Room;
|
||||||
|
|
||||||
|
|
@ -60,21 +59,12 @@ impl Rooms {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StatefulWidget for Rooms {
|
impl Widget for Rooms {
|
||||||
type State = RoomsState;
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
|
||||||
let title_style = Style::default().add_modifier(Modifier::BOLD);
|
let title_style = Style::default().add_modifier(Modifier::BOLD);
|
||||||
let room_style = Style::default().fg(Color::LightBlue);
|
let room_style = Style::default().fg(Color::LightBlue);
|
||||||
let selected_room_style = room_style.add_modifier(Modifier::BOLD);
|
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 {
|
let title = if let Some(selected) = self.selected {
|
||||||
format!("Rooms ({}/{})", selected + 1, self.rooms.len())
|
format!("Rooms ({}/{})", selected + 1, self.rooms.len())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -95,60 +85,6 @@ impl StatefulWidget for Rooms {
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Paragraph::new(lines).render(left, buf);
|
Paragraph::new(lines).render(area, 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue