Port rendering logic
This commit is contained in:
parent
583c82148f
commit
3af1193869
4 changed files with 110 additions and 98 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
mod blocks;
|
mod blocks;
|
||||||
// mod cursor;
|
// mod cursor;
|
||||||
mod layout;
|
mod layout;
|
||||||
// mod render;
|
mod render;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -14,7 +14,7 @@ use toss::frame::{Frame, Size};
|
||||||
use crate::store::{Msg, MsgStore};
|
use crate::store::{Msg, MsgStore};
|
||||||
use crate::ui::widgets::Widget;
|
use crate::ui::widgets::Widget;
|
||||||
|
|
||||||
use self::blocks::Blocks;
|
use self::blocks::{Block, BlockBody, Blocks, MarkerBlock};
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// State //
|
// State //
|
||||||
|
|
@ -47,6 +47,21 @@ enum Cursor<I> {
|
||||||
Placeholder(LastChild<I>),
|
Placeholder(LastChild<I>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<I: Eq> Cursor<I> {
|
||||||
|
fn matches_block(&self, block: &Block<I>) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Bottom => matches!(&block.body, BlockBody::Marker(MarkerBlock::Bottom)),
|
||||||
|
Self::Msg(id) => matches!(&block.body, BlockBody::Msg(msg) if msg.id == *id),
|
||||||
|
Self::Compose(lc) | Self::Placeholder(lc) => match &lc.after {
|
||||||
|
Some(bid) => {
|
||||||
|
matches!(&block.body, BlockBody::Marker(MarkerBlock::After(aid)) if aid == bid)
|
||||||
|
}
|
||||||
|
None => matches!(&block.body, BlockBody::Marker(MarkerBlock::Bottom)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct InnerTreeViewState<M: Msg, S: MsgStore<M>> {
|
struct InnerTreeViewState<M: Msg, S: MsgStore<M>> {
|
||||||
store: S,
|
store: S,
|
||||||
last_blocks: Blocks<M::Id>,
|
last_blocks: Blocks<M::Id>,
|
||||||
|
|
@ -104,7 +119,6 @@ where
|
||||||
async fn render(self: Box<Self>, frame: &mut Frame) {
|
async fn render(self: Box<Self>, frame: &mut Frame) {
|
||||||
let mut guard = self.0.lock().await;
|
let mut guard = self.0.lock().await;
|
||||||
guard.relayout(frame).await;
|
guard.relayout(frame).await;
|
||||||
// Draw layout to screen
|
guard.draw_blocks(frame);
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,6 @@ use crate::store::{Msg, MsgStore, Path, Tree};
|
||||||
use super::blocks::{Block, BlockBody, Blocks, MarkerBlock, MsgBlock};
|
use super::blocks::{Block, BlockBody, Blocks, MarkerBlock, MsgBlock};
|
||||||
use super::{util, Cursor, InnerTreeViewState};
|
use super::{util, Cursor, InnerTreeViewState};
|
||||||
|
|
||||||
impl<I: Eq> Cursor<I> {
|
|
||||||
fn matches_block(&self, block: &Block<I>) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Bottom => matches!(&block.body, BlockBody::Marker(MarkerBlock::Bottom)),
|
|
||||||
Self::Msg(id) => matches!(&block.body, BlockBody::Msg(msg) if msg.id == *id),
|
|
||||||
Self::Compose(lc) | Self::Placeholder(lc) => match &lc.after {
|
|
||||||
Some(bid) => {
|
|
||||||
matches!(&block.body, BlockBody::Marker(MarkerBlock::After(aid)) if aid == bid)
|
|
||||||
}
|
|
||||||
None => matches!(&block.body, BlockBody::Marker(MarkerBlock::Bottom)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
async fn cursor_path(&self, cursor: &Cursor<M::Id>) -> Path<M::Id> {
|
async fn cursor_path(&self, cursor: &Cursor<M::Id>) -> Path<M::Id> {
|
||||||
match cursor {
|
match cursor {
|
||||||
|
|
@ -47,10 +32,10 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
) -> Option<&'a M::Id> {
|
) -> Option<&'a M::Id> {
|
||||||
match cursor {
|
match cursor {
|
||||||
Cursor::Bottom => None,
|
Cursor::Bottom => None,
|
||||||
Cursor::Msg(id) => Some(cursor_path.first()),
|
Cursor::Msg(_) => Some(cursor_path.first()),
|
||||||
Cursor::Compose(lc) | Cursor::Placeholder(lc) => match &lc.after {
|
Cursor::Compose(lc) | Cursor::Placeholder(lc) => match &lc.after {
|
||||||
None => None,
|
None => None,
|
||||||
Some(id) => Some(cursor_path.first()),
|
Some(_) => Some(cursor_path.first()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +65,7 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
let nick = msg.nick();
|
let nick = msg.nick();
|
||||||
let content = msg.content();
|
let content = msg.content();
|
||||||
|
|
||||||
let content_width = size.width as i32 - util::after_nick(frame, indent, &nick.text());
|
let content_width = size.width as i32 - util::after_nick(frame, indent, &nick);
|
||||||
if content_width < util::MIN_CONTENT_WIDTH as i32 {
|
if content_width < util::MIN_CONTENT_WIDTH as i32 {
|
||||||
Block::placeholder(Some(msg.time()), indent, msg.id())
|
Block::placeholder(Some(msg.time()), indent, msg.id())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,95 +1,107 @@
|
||||||
//! Rendering blocks to a [`Frame`].
|
//! Rendering blocks to a [`Frame`].
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use toss::frame::{Frame, Pos, Size};
|
use toss::frame::{Frame, Pos};
|
||||||
use toss::styled::Styled;
|
use toss::styled::Styled;
|
||||||
|
|
||||||
use crate::store::Msg;
|
use crate::store::{Msg, MsgStore};
|
||||||
|
|
||||||
use super::blocks::{Block, BlockBody, Blocks};
|
use super::blocks::{Block, BlockBody, MsgBlock, MsgContent};
|
||||||
use super::util::{
|
use super::{util, InnerTreeViewState};
|
||||||
self, style_indent, style_indent_inverted, style_placeholder, style_time, style_time_inverted,
|
|
||||||
INDENT, PLACEHOLDER, TIME_EMPTY, TIME_FORMAT,
|
|
||||||
};
|
|
||||||
use super::TreeView;
|
|
||||||
|
|
||||||
fn render_time(frame: &mut Frame, x: i32, y: i32, cursor: bool, time: Option<DateTime<Utc>>) {
|
impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
let pos = Pos::new(x, y);
|
fn render_time(frame: &mut Frame, line: i32, time: Option<DateTime<Utc>>, is_cursor: bool) {
|
||||||
|
let pos = Pos::new(0, line);
|
||||||
let style = if cursor {
|
let style = if is_cursor {
|
||||||
style_time_inverted()
|
util::style_time_inverted()
|
||||||
} else {
|
} else {
|
||||||
style_time()
|
util::style_time()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(time) = time {
|
if let Some(time) = time {
|
||||||
let time = format!("{}", time.format(TIME_FORMAT));
|
let time = format!("{}", time.format(util::TIME_FORMAT));
|
||||||
frame.write(pos, (&time, style));
|
frame.write(pos, (&time, style));
|
||||||
} else {
|
} else {
|
||||||
frame.write(pos, (TIME_EMPTY, style));
|
frame.write(pos, (util::TIME_EMPTY, style));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_indent(frame: &mut Frame, x: i32, y: i32, cursor: bool, indent: usize) {
|
fn render_indent(frame: &mut Frame, line: i32, indent: usize, is_cursor: bool) {
|
||||||
let style = if cursor {
|
let pos = Pos::new(util::after_indent(0), line);
|
||||||
style_indent_inverted()
|
let style = if is_cursor {
|
||||||
|
util::style_indent_inverted()
|
||||||
} else {
|
} else {
|
||||||
style_indent()
|
util::style_indent()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut styled = Styled::default();
|
let mut styled = Styled::default();
|
||||||
for _ in 0..indent {
|
for _ in 0..indent {
|
||||||
styled = styled.then((INDENT, style));
|
styled = styled.then((util::INDENT, style));
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.write(Pos::new(x + util::after_indent(0), y), styled);
|
frame.write(pos, styled);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_nick(frame: &mut Frame, x: i32, y: i32, indent: usize, nick: Styled) {
|
fn render_nick(frame: &mut Frame, line: i32, indent: usize, nick: Styled) {
|
||||||
let nick_pos = Pos::new(x + util::after_indent(indent), y);
|
let nick_pos = Pos::new(util::after_indent(indent), line);
|
||||||
let styled = Styled::new("[").and_then(nick).then("]");
|
let styled = Styled::new("[").and_then(nick).then("]");
|
||||||
frame.write(nick_pos, styled);
|
frame.write(nick_pos, styled);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_block<M: Msg>(frame: &mut Frame, pos: Pos, size: Size, block: Block<M::Id>) {
|
fn draw_msg_block(
|
||||||
match block.body {
|
frame: &mut Frame,
|
||||||
BlockBody::Msg(msg) => {
|
line: i32,
|
||||||
let after_nick = util::after_nick(frame, block.indent, &msg.nick.text());
|
time: Option<DateTime<Utc>>,
|
||||||
|
indent: usize,
|
||||||
|
msg: &MsgBlock<M::Id>,
|
||||||
|
is_cursor: bool,
|
||||||
|
) {
|
||||||
|
match &msg.content {
|
||||||
|
MsgContent::Msg { nick, lines } => {
|
||||||
|
let height: i32 = frame.size().height.into();
|
||||||
|
let after_nick = util::after_nick(frame, indent, nick);
|
||||||
|
|
||||||
for (i, line) in msg.lines.into_iter().enumerate() {
|
for (i, text) in lines.iter().enumerate() {
|
||||||
let y = pos.y + block.line + i as i32;
|
let line = line + i as i32;
|
||||||
if y < pos.y || y >= pos.y + size.height as i32 {
|
if line < 0 || line >= height {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
render_indent(frame, pos.x, y, block.cursor, block.indent);
|
Self::render_indent(frame, line, indent, is_cursor);
|
||||||
render_time(frame, pos.x, y, block.cursor, block.time);
|
Self::render_time(frame, line, time, is_cursor);
|
||||||
render_nick(frame, pos.x, y, block.indent, msg.nick.clone());
|
Self::render_nick(frame, line, indent, nick.clone());
|
||||||
} else {
|
} else {
|
||||||
render_indent(frame, pos.x, y, false, block.indent + 1);
|
Self::render_indent(frame, line, indent + 1, false);
|
||||||
render_indent(frame, pos.x, y, block.cursor, block.indent);
|
Self::render_indent(frame, line, indent, is_cursor);
|
||||||
render_time(frame, pos.x, y, block.cursor, None);
|
Self::render_time(frame, line, None, is_cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
let line_pos = Pos::new(pos.x + after_nick, y);
|
frame.write(Pos::new(after_nick, line), text.clone());
|
||||||
frame.write(line_pos, line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BlockBody::Placeholder => {
|
MsgContent::Placeholder => {
|
||||||
let y = pos.y + block.line;
|
Self::render_time(frame, line, time, is_cursor);
|
||||||
render_time(frame, pos.x, y, block.cursor, block.time);
|
Self::render_indent(frame, line, indent, is_cursor);
|
||||||
render_indent(frame, pos.x, y, block.cursor, block.indent);
|
let pos = Pos::new(util::after_indent(indent), line);
|
||||||
let pos = Pos::new(pos.x + util::after_indent(block.indent), y);
|
frame.write(pos, (util::PLACEHOLDER, util::style_placeholder()));
|
||||||
frame.write(pos, (PLACEHOLDER, style_placeholder()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Msg> TreeView<M> {
|
fn draw_block(frame: &mut Frame, block: &Block<M::Id>, is_cursor: bool) {
|
||||||
pub fn render_blocks(frame: &mut Frame, pos: Pos, size: Size, layout: Blocks<M::Id>) {
|
match &block.body {
|
||||||
for block in layout.blocks {
|
BlockBody::Marker(_) => {}
|
||||||
render_block::<M>(frame, pos, size, block);
|
BlockBody::Msg(msg) => {
|
||||||
|
Self::draw_msg_block(frame, block.line, block.time, block.indent, msg, is_cursor)
|
||||||
|
}
|
||||||
|
BlockBody::Compose(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_blocks(&self, frame: &mut Frame) {
|
||||||
|
for block in self.last_blocks.iter() {
|
||||||
|
Self::draw_block(frame, block, self.cursor.matches_block(block));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use crossterm::style::{ContentStyle, Stylize};
|
use crossterm::style::{ContentStyle, Stylize};
|
||||||
use toss::frame::Frame;
|
use toss::frame::Frame;
|
||||||
|
use toss::styled::Styled;
|
||||||
|
|
||||||
pub const TIME_FORMAT: &str = "%F %R ";
|
pub const TIME_FORMAT: &str = "%F %R ";
|
||||||
pub const TIME_EMPTY: &str = " ";
|
pub const TIME_EMPTY: &str = " ";
|
||||||
|
|
@ -38,6 +39,6 @@ pub fn after_indent(indent: usize) -> i32 {
|
||||||
(TIME_WIDTH + indent * INDENT_WIDTH) as i32
|
(TIME_WIDTH + indent * INDENT_WIDTH) as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn after_nick(frame: &mut Frame, indent: usize, nick: &str) -> i32 {
|
pub fn after_nick(frame: &mut Frame, indent: usize, nick: &Styled) -> i32 {
|
||||||
after_indent(indent) + 1 + frame.width(nick) as i32 + 2
|
after_indent(indent) + 1 + frame.width_styled(nick) as i32 + 2
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue