more use of generic components
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
parent
682deadb48
commit
0f9c0aa1f6
@ -2,7 +2,7 @@
|
||||
name = "ghost-eye"
|
||||
authors = ["str3tch <stretch@ghostchain.io>"]
|
||||
description = "Application for interacting with Casper/Ghost nodes that are exposing RPC only to the localhost"
|
||||
version = "0.3.67"
|
||||
version = "0.3.68"
|
||||
edition = "2021"
|
||||
homepage = "https://git.ghostchain.io/ghostchain"
|
||||
repository = "https://git.ghostchain.io/ghostchain/ghost-eye"
|
||||
|
@ -1,5 +1,4 @@
|
||||
use color_eyre::Result;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use crossterm::event::{KeyEvent, KeyCode};
|
||||
use ratatui::{
|
||||
layout::{Alignment, Rect},
|
||||
@ -10,13 +9,20 @@ use ratatui::{
|
||||
|
||||
use super::Component;
|
||||
use crate::{
|
||||
components::generic::Activatable,
|
||||
action::Action, app::Mode
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Empty {
|
||||
is_active: bool,
|
||||
action_tx: Option<UnboundedSender<Action>>,
|
||||
}
|
||||
|
||||
impl Activatable for Empty {
|
||||
fn is_active(&self) -> bool { self.is_active }
|
||||
fn is_inactive(&self) -> bool { !self.is_active }
|
||||
fn set_inactive(&mut self) { self.is_active = false; }
|
||||
fn set_active(&mut self) { self.is_active = true; }
|
||||
}
|
||||
|
||||
impl Empty {
|
||||
@ -88,20 +94,15 @@ impl Empty {
|
||||
}
|
||||
|
||||
fn move_out(&mut self) -> Result<Option<Action>> {
|
||||
self.is_active = false;
|
||||
self.set_inactive();
|
||||
Ok(Some(Action::SetActiveScreen(Mode::Menu)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Empty {
|
||||
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
|
||||
self.action_tx = Some(tx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::SetActiveScreen(Mode::Empty) => self.is_active = true,
|
||||
Action::SetActiveScreen(Mode::Empty) => self.set_active(),
|
||||
_ => {}
|
||||
};
|
||||
Ok(None)
|
||||
@ -109,15 +110,20 @@ impl Component for Empty {
|
||||
|
||||
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
|
||||
match key.code {
|
||||
KeyCode::Esc if self.is_active => self.move_out(),
|
||||
KeyCode::Esc if self.is_active() => self.move_out(),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let screen = super::screen_layout(area);
|
||||
let screen = super::layouts::screen_layout(area);
|
||||
|
||||
let lines = if self.is_active() {
|
||||
self.prepare_active_text()
|
||||
} else {
|
||||
self.prepare_inactive_text()
|
||||
};
|
||||
|
||||
let lines = if self.is_active { self.prepare_active_text() } else { self.prepare_inactive_text() };
|
||||
let lines_len = lines.len() as u16;
|
||||
let padding_top = screen
|
||||
.as_size()
|
||||
|
@ -1,5 +1,5 @@
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{KeyCode, KeyEvent};
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::layout::{Constraint, Margin};
|
||||
use ratatui::widgets::{Padding, Scrollbar, ScrollbarOrientation};
|
||||
use ratatui::{
|
||||
@ -9,13 +9,11 @@ use ratatui::{
|
||||
Frame
|
||||
};
|
||||
use subxt::utils::H256;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::{Component, PartialComponent, CurrentTab};
|
||||
use super::{Component, CurrentTab};
|
||||
use crate::{
|
||||
action::Action,
|
||||
config::Config,
|
||||
palette::StylePalette,
|
||||
components::generic::{Activatable, Scrollable, PartialComponent},
|
||||
action::Action, config::Config, palette::StylePalette,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -26,7 +24,6 @@ struct BlockInfo {
|
||||
|
||||
pub struct BlockExplorer {
|
||||
is_active: bool,
|
||||
action_tx: Option<UnboundedSender<Action>>,
|
||||
blocks: std::collections::VecDeque<BlockInfo>,
|
||||
block_headers: std::collections::HashMap<u32, H256>,
|
||||
block_authors: std::collections::HashMap<H256, String>,
|
||||
@ -41,6 +38,49 @@ impl Default for BlockExplorer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Activatable for BlockExplorer {
|
||||
fn is_active(&self) -> bool { self.is_active }
|
||||
fn is_inactive(&self) -> bool { !self.is_active }
|
||||
fn set_inactive(&mut self) { self.is_active = false; }
|
||||
fn set_active(&mut self) { self.is_active = true; }
|
||||
}
|
||||
|
||||
impl Scrollable for BlockExplorer {
|
||||
type IndexType = usize;
|
||||
|
||||
fn selected_index(&self) -> Option<Self::IndexType> {
|
||||
self.table_state.selected()
|
||||
}
|
||||
|
||||
fn items_length(&self) -> Self::IndexType {
|
||||
self.blocks.len()
|
||||
}
|
||||
|
||||
fn apply_next_row(&mut self, new_index: Self::IndexType) -> Result<Option<Action>> {
|
||||
self.table_state.select(Some(new_index));
|
||||
self.scroll_state = self.scroll_state.position(new_index);
|
||||
self.send_used_explorer_block(new_index)
|
||||
}
|
||||
|
||||
fn apply_prev_row(&mut self, new_index: Self::IndexType) -> Result<Option<Action>> {
|
||||
self.apply_next_row(new_index)
|
||||
}
|
||||
|
||||
fn apply_first_row(&mut self) -> Result<Option<Action>> {
|
||||
match self.items_length() > 0 {
|
||||
true => self.apply_next_row(0),
|
||||
false => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_last_row(&mut self) -> Result<Option<Action>> {
|
||||
match self.items_length().checked_sub(1) {
|
||||
Some(last_idx) => self.apply_next_row(last_idx),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockExplorer {
|
||||
const MAX_BLOCKS: usize = 50;
|
||||
|
||||
@ -48,7 +88,6 @@ impl BlockExplorer {
|
||||
Self {
|
||||
is_active: false,
|
||||
blocks: Default::default(),
|
||||
action_tx: None,
|
||||
block_authors: Default::default(),
|
||||
block_headers: Default::default(),
|
||||
scroll_state: ScrollbarState::new(0),
|
||||
@ -61,7 +100,7 @@ impl BlockExplorer {
|
||||
&mut self,
|
||||
hash: H256,
|
||||
block_number: u32,
|
||||
) {
|
||||
) -> Result<Option<Action>> {
|
||||
let front_block_number = self.blocks
|
||||
.front()
|
||||
.map(|block| block.block_number)
|
||||
@ -74,7 +113,7 @@ impl BlockExplorer {
|
||||
});
|
||||
|
||||
self.block_headers.insert(block_number, hash);
|
||||
if self.blocks.len() > Self::MAX_BLOCKS {
|
||||
if self.items_length() > Self::MAX_BLOCKS {
|
||||
if let Some(block) = self.blocks.pop_back() {
|
||||
if let Some(hash) = self.block_headers.remove(&block.block_number) {
|
||||
self.block_authors.remove(&hash);
|
||||
@ -82,19 +121,21 @@ impl BlockExplorer {
|
||||
}
|
||||
}
|
||||
|
||||
self.scroll_state = self.scroll_state.content_length(self.blocks.len());
|
||||
if self.table_state.selected().is_some() {
|
||||
self.next_row();
|
||||
self.scroll_state = self.scroll_state.content_length(self.items_length());
|
||||
return match self.table_state.selected() {
|
||||
Some(_) => self.next_row(),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn update_finalized_block_info(
|
||||
&mut self,
|
||||
header: H256,
|
||||
block_number: u32,
|
||||
) {
|
||||
for idx in 0..self.blocks.len() {
|
||||
) -> Result<Option<Action>> {
|
||||
for idx in 0..self.items_length() {
|
||||
if self.blocks[idx].finalized { break; }
|
||||
else if self.blocks[idx].block_number > block_number { continue; }
|
||||
else {
|
||||
@ -102,86 +143,30 @@ impl BlockExplorer {
|
||||
self.blocks[idx].finalized = true;
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn send_used_explorer_block(&mut self, index: usize) {
|
||||
if let Some(action_tx) = &self.action_tx {
|
||||
fn send_used_explorer_block(&mut self, index: usize) -> Result<Option<Action>> {
|
||||
let maybe_block_number = self.blocks.get(index).map(|info| info.block_number);
|
||||
let _ = action_tx.send(Action::UsedExplorerBlock(maybe_block_number));
|
||||
Ok(Some(Action::UsedExplorerBlock(maybe_block_number)))
|
||||
}
|
||||
}
|
||||
|
||||
fn first_row(&mut self) {
|
||||
if self.blocks.len() > 0 {
|
||||
self.table_state.select(Some(0));
|
||||
self.scroll_state = self.scroll_state.position(0);
|
||||
self.send_used_explorer_block(0);
|
||||
}
|
||||
}
|
||||
|
||||
fn next_row(&mut self) {
|
||||
let i = match self.table_state.selected() {
|
||||
Some(i) => {
|
||||
if i >= self.blocks.len() - 1 {
|
||||
i
|
||||
} else {
|
||||
i + 1
|
||||
}
|
||||
},
|
||||
None => 0,
|
||||
};
|
||||
self.table_state.select(Some(i));
|
||||
self.scroll_state = self.scroll_state.position(i);
|
||||
self.send_used_explorer_block(i);
|
||||
}
|
||||
|
||||
fn last_row(&mut self) {
|
||||
if self.blocks.len() > 0 {
|
||||
let last = self.blocks.len() - 1;
|
||||
self.table_state.select(Some(last));
|
||||
self.scroll_state = self.scroll_state.position(last);
|
||||
self.send_used_explorer_block(last);
|
||||
}
|
||||
}
|
||||
|
||||
fn previous_row(&mut self) {
|
||||
let i = match self.table_state.selected() {
|
||||
Some(i) => {
|
||||
if i == 0 {
|
||||
0
|
||||
} else {
|
||||
i - 1
|
||||
}
|
||||
},
|
||||
None => 0
|
||||
};
|
||||
self.table_state.select(Some(i));
|
||||
self.scroll_state = self.scroll_state.position(i);
|
||||
self.send_used_explorer_block(i);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for BlockExplorer {
|
||||
fn set_active(&mut self, current_tab: CurrentTab) {
|
||||
impl PartialComponent<CurrentTab> for BlockExplorer {
|
||||
fn set_active_tab(&mut self, current_tab: CurrentTab) {
|
||||
match current_tab {
|
||||
CurrentTab::Blocks => self.is_active = true,
|
||||
CurrentTab::Extrinsics => self.is_active = false,
|
||||
CurrentTab::Blocks => self.set_active(),
|
||||
CurrentTab::Extrinsics => self.set_inactive(),
|
||||
CurrentTab::Nothing => {
|
||||
self.is_active = false;
|
||||
self.set_inactive();
|
||||
self.table_state.select(None);
|
||||
self.scroll_state = self.scroll_state.position(0);
|
||||
self.send_used_explorer_block(usize::MAX);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for BlockExplorer {
|
||||
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
|
||||
self.action_tx = Some(tx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
|
||||
self.palette.with_normal_style(style.get("normal_style").copied());
|
||||
@ -202,28 +187,23 @@ impl Component for BlockExplorer {
|
||||
Action::FinalizedBlockInformation(header, block_number) => self.update_finalized_block_info(header, block_number),
|
||||
Action::SetBlockAuthor(header, author) => {
|
||||
let _ = self.block_authors.insert(header, author);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
Ok(None)
|
||||
},
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
|
||||
match key.code {
|
||||
KeyCode::Char('k') | KeyCode::Up if self.is_active => self.previous_row(),
|
||||
KeyCode::Char('j') | KeyCode::Down if self.is_active => self.next_row(),
|
||||
KeyCode::Char('g') if self.is_active => self.first_row(),
|
||||
KeyCode::Char('G') if self.is_active => self.last_row(),
|
||||
_ => {},
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
match self.is_active() {
|
||||
true => self.handle_scrollable_key_codes(key.code),
|
||||
false => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [place, _] = super::explorer_scrollbars_layout(area);
|
||||
let [place, _] = super::layouts::explorer_scrollbars_layout(area);
|
||||
|
||||
let (border_style, border_type) = self.palette.create_border_style(self.is_active);
|
||||
let (border_style, border_type) = self.palette.create_border_style(self.is_active());
|
||||
let default_hash = H256::repeat_byte(69u8);
|
||||
let default_author = "...".to_string();
|
||||
let rows = self.blocks
|
||||
|
@ -7,12 +7,11 @@ use ratatui::{
|
||||
Frame
|
||||
};
|
||||
|
||||
use super::{Component, PartialComponent, CurrentTab};
|
||||
use super::{Component, CurrentTab};
|
||||
use crate::{
|
||||
config::Config,
|
||||
components::generic::PartialComponent,
|
||||
config::Config, action::Action, palette::StylePalette,
|
||||
widgets::{BigText, PixelSize},
|
||||
action::Action,
|
||||
palette::StylePalette,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -37,18 +36,15 @@ impl BlockTicker {
|
||||
}
|
||||
}
|
||||
|
||||
fn block_found(&mut self, block: u32) -> Result<()> {
|
||||
fn block_found(&mut self, block: u32) {
|
||||
if self.last_block < block {
|
||||
self.last_block_time = Instant::now();
|
||||
self.last_block = block;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for BlockTicker {
|
||||
fn set_active(&mut self, _current_tab: CurrentTab) {}
|
||||
}
|
||||
impl PartialComponent<CurrentTab> for BlockTicker {}
|
||||
|
||||
impl Component for BlockTicker {
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
@ -65,14 +61,14 @@ impl Component for BlockTicker {
|
||||
|
||||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::BestBlockUpdated(block) => self.block_found(block)?,
|
||||
Action::BestBlockUpdated(block) => self.block_found(block),
|
||||
_ => {}
|
||||
};
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [place, _, _] = super::explorer_block_info_layout(area);
|
||||
let [place, _, _] = super::layouts::explorer_block_info_layout(area);
|
||||
|
||||
let passed = (Instant::now() - self.last_block_time).as_secs_f64();
|
||||
let text = if passed < 60.0 {
|
||||
|
@ -6,11 +6,10 @@ use ratatui::{
|
||||
Frame,
|
||||
};
|
||||
|
||||
use super::{Component, PartialComponent, CurrentTab};
|
||||
use super::{Component, CurrentTab};
|
||||
use crate::{
|
||||
config::Config,
|
||||
action::Action,
|
||||
palette::StylePalette,
|
||||
components::generic::PartialComponent,
|
||||
config::Config, action::Action, palette::StylePalette,
|
||||
widgets::{PixelSize, BigText},
|
||||
};
|
||||
|
||||
@ -26,17 +25,9 @@ impl CurrentEpoch {
|
||||
const SESSION_LENGTH: u64 = 2_400;
|
||||
const SECONDS_IN_DAY: u64 = 86_400;
|
||||
const SECONDS_IN_HOUR: u64 = 3_600;
|
||||
|
||||
fn update_epoch_progress(&mut self, number: u64, progress: u64) -> Result<()> {
|
||||
self.number = number;
|
||||
self.progress = progress;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for CurrentEpoch {
|
||||
fn set_active(&mut self, _current_tab: CurrentTab) {}
|
||||
}
|
||||
impl PartialComponent<CurrentTab> for CurrentEpoch {}
|
||||
|
||||
impl Component for CurrentEpoch {
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
@ -53,15 +44,17 @@ impl Component for CurrentEpoch {
|
||||
|
||||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::SetEpochProgress(number, progress) =>
|
||||
self.update_epoch_progress(number, progress)?,
|
||||
Action::SetEpochProgress(number, progress) => {
|
||||
self.number = number;
|
||||
self.progress = progress;
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [place, _] = super::explorer_era_info_layout(area);
|
||||
let [place, _] = super::layouts::explorer_era_info_layout(area);
|
||||
|
||||
let seconds_to_next = Self::SESSION_LENGTH.saturating_sub(self.progress) * Self::SECONDS_IN_BLOCK;
|
||||
let hours = (seconds_to_next % Self::SECONDS_IN_DAY) / Self::SECONDS_IN_HOUR;
|
||||
|
@ -6,13 +6,10 @@ use ratatui::{
|
||||
Frame,
|
||||
};
|
||||
|
||||
use super::{Component, PartialComponent, CurrentTab};
|
||||
use super::{Component, CurrentTab};
|
||||
use crate::{
|
||||
config::Config,
|
||||
action::Action,
|
||||
palette::StylePalette,
|
||||
types::EraInfo,
|
||||
widgets::{PixelSize, BigText},
|
||||
config::Config, action::Action, palette::StylePalette, types::EraInfo,
|
||||
components::generic::PartialComponent, widgets::{PixelSize, BigText},
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -28,16 +25,9 @@ impl CurrentEra {
|
||||
const MILLIS_IN_DAY: u64 = 86_400_000;
|
||||
const MILLIS_IN_HOUR: u64 = 3_600_000;
|
||||
const MILLIS_IN_MINUTE: u64 = 60_000;
|
||||
|
||||
fn update_era(&mut self, era_info: EraInfo) -> Result<()> {
|
||||
self.era = era_info;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for CurrentEra {
|
||||
fn set_active(&mut self, _current_tab: CurrentTab) {}
|
||||
}
|
||||
impl PartialComponent<CurrentTab> for CurrentEra {}
|
||||
|
||||
impl Component for CurrentEra {
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
@ -54,14 +44,14 @@ impl Component for CurrentEra {
|
||||
|
||||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::SetActiveEra(era_info) => self.update_era(era_info)?,
|
||||
_ => {}
|
||||
Action::SetActiveEra(era_info) => self.era = era_info,
|
||||
_ => {},
|
||||
};
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [_, place] = super::explorer_era_info_layout(area);
|
||||
let [_, place] = super::layouts::explorer_era_info_layout(area);
|
||||
|
||||
let current_time = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::usize;
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{KeyCode, KeyEvent};
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::layout::{Constraint, Margin};
|
||||
use ratatui::widgets::{Padding, Scrollbar, ScrollbarOrientation};
|
||||
use ratatui::{
|
||||
@ -10,19 +10,16 @@ use ratatui::{
|
||||
widgets::{Block, ScrollbarState, Cell, Row, Table, TableState},
|
||||
Frame
|
||||
};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::{Component, CurrentTab, PartialComponent};
|
||||
use super::{Component, CurrentTab};
|
||||
use crate::{
|
||||
components::generic::{Activatable, Scrollable, PartialComponent},
|
||||
types::CasperExtrinsicDetails,
|
||||
action::Action,
|
||||
config::Config,
|
||||
palette::StylePalette,
|
||||
action::Action, config::Config, palette::StylePalette,
|
||||
};
|
||||
|
||||
pub struct ExtrinsicExplorer {
|
||||
is_active: bool,
|
||||
action_tx: Option<UnboundedSender<Action>>,
|
||||
extrinsics: HashMap<u32, Vec<CasperExtrinsicDetails>>,
|
||||
current_extrinsics: Option<Vec<CasperExtrinsicDetails>>,
|
||||
block_numbers: VecDeque<u32>,
|
||||
@ -37,13 +34,58 @@ impl Default for ExtrinsicExplorer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Activatable for ExtrinsicExplorer {
|
||||
fn is_active(&self) -> bool { self.is_active }
|
||||
fn is_inactive(&self) -> bool { !self.is_active }
|
||||
fn set_inactive(&mut self) { self.is_active = false; }
|
||||
fn set_active(&mut self) { self.is_active = true; }
|
||||
}
|
||||
|
||||
impl Scrollable for ExtrinsicExplorer {
|
||||
type IndexType = usize;
|
||||
|
||||
fn selected_index(&self) -> Option<Self::IndexType> {
|
||||
self.table_state.selected()
|
||||
}
|
||||
|
||||
fn items_length(&self) -> Self::IndexType {
|
||||
self.current_extrinsics
|
||||
.as_ref()
|
||||
.map(|exts| exts.len())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn apply_next_row(&mut self, new_index: Self::IndexType) -> Result<Option<Action>> {
|
||||
self.table_state.select(Some(new_index));
|
||||
self.scroll_state = self.scroll_state.position(new_index);
|
||||
self.send_used_explorer_log(new_index)
|
||||
}
|
||||
|
||||
fn apply_prev_row(&mut self, new_index: Self::IndexType) -> Result<Option<Action>> {
|
||||
self.apply_next_row(new_index)
|
||||
}
|
||||
|
||||
fn apply_first_row(&mut self) -> Result<Option<Action>> {
|
||||
match self.items_length() > 0 {
|
||||
true => self.apply_next_row(0),
|
||||
false => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_last_row(&mut self) -> Result<Option<Action>> {
|
||||
match self.items_length().checked_sub(1) {
|
||||
Some(last_idx) => self.apply_next_row(last_idx),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtrinsicExplorer {
|
||||
const MAX_BLOCKS: usize = 50;
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
is_active: false,
|
||||
action_tx: None,
|
||||
current_extrinsics: None,
|
||||
extrinsics: Default::default(),
|
||||
block_numbers: Default::default(),
|
||||
@ -53,6 +95,20 @@ impl ExtrinsicExplorer {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_used_explorer_block(&mut self, block_number: u32) {
|
||||
let maybe_exts = self.extrinsics
|
||||
.get(&block_number)
|
||||
.map(|exts| exts.to_vec());
|
||||
let exts_length = self.extrinsics
|
||||
.get(&block_number)
|
||||
.map(|exts| exts.len())
|
||||
.unwrap_or_default();
|
||||
|
||||
self.current_extrinsics = maybe_exts;
|
||||
self.scroll_state = self.scroll_state.content_length(exts_length);
|
||||
}
|
||||
|
||||
|
||||
fn update_extrinsics_for_header(
|
||||
&mut self,
|
||||
block_number: u32,
|
||||
@ -68,109 +124,30 @@ impl ExtrinsicExplorer {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_used_explorer_log(&mut self, index: usize) {
|
||||
if let Some(action_tx) = &self.action_tx {
|
||||
fn send_used_explorer_log(&mut self, index: usize) -> Result<Option<Action>> {
|
||||
let maybe_log = self.current_extrinsics
|
||||
.as_ref()
|
||||
.map(|ext| {
|
||||
ext.get(index).map(|ext| {
|
||||
.map(|ext| ext.get(index).map(|ext| {
|
||||
hex::encode(&ext.field_bytes.clone())
|
||||
})
|
||||
})
|
||||
}))
|
||||
.flatten();
|
||||
let _ = action_tx.send(Action::UsedExplorerLog(maybe_log.clone()));
|
||||
Ok(Some(Action::UsedExplorerLog(maybe_log.clone())))
|
||||
}
|
||||
}
|
||||
|
||||
fn first_row(&mut self) {
|
||||
match &self.current_extrinsics {
|
||||
Some(exts) if exts.len() > 0 => self.table_state.select(Some(0)),
|
||||
_ => self.table_state.select(None),
|
||||
}
|
||||
self.scroll_state = self.scroll_state.position(0);
|
||||
self.send_used_explorer_log(0);
|
||||
}
|
||||
|
||||
fn next_row(&mut self) {
|
||||
match &self.current_extrinsics {
|
||||
Some(exts) => {
|
||||
let i = match self.table_state.selected() {
|
||||
Some(i) => {
|
||||
if i >= exts.len() - 1 {
|
||||
i
|
||||
} else {
|
||||
i + 1
|
||||
}
|
||||
},
|
||||
None => 0,
|
||||
};
|
||||
self.table_state.select(Some(i));
|
||||
self.scroll_state = self.scroll_state.position(i);
|
||||
self.send_used_explorer_log(i);
|
||||
},
|
||||
None => {
|
||||
self.table_state.select(None);
|
||||
self.scroll_state = self.scroll_state.position(0);
|
||||
self.send_used_explorer_log(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn last_row(&mut self) {
|
||||
match &self.current_extrinsics {
|
||||
Some(exts) => {
|
||||
let last = exts.len().saturating_sub(1);
|
||||
self.table_state.select(Some(last));
|
||||
self.scroll_state = self.scroll_state.position(last);
|
||||
self.send_used_explorer_log(last);
|
||||
},
|
||||
None => {
|
||||
self.table_state.select(None);
|
||||
self.scroll_state = self.scroll_state.position(0);
|
||||
self.send_used_explorer_log(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn previous_row(&mut self) {
|
||||
match &self.current_extrinsics {
|
||||
Some(_) => {
|
||||
let i = self.table_state
|
||||
.selected()
|
||||
.map(|i| i.saturating_sub(1))
|
||||
.unwrap_or_default();
|
||||
self.table_state.select(Some(i));
|
||||
self.scroll_state = self.scroll_state.position(i);
|
||||
self.send_used_explorer_log(i);
|
||||
},
|
||||
None => {
|
||||
self.table_state.select(None);
|
||||
self.scroll_state = self.scroll_state.position(0);
|
||||
self.send_used_explorer_log(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for ExtrinsicExplorer {
|
||||
fn set_active(&mut self, current_tab: CurrentTab) {
|
||||
impl PartialComponent<CurrentTab> for ExtrinsicExplorer {
|
||||
fn set_active_tab(&mut self, current_tab: CurrentTab) {
|
||||
if current_tab == CurrentTab::Extrinsics {
|
||||
self.is_active = true;
|
||||
self.set_active();
|
||||
} else {
|
||||
self.is_active = false;
|
||||
self.set_inactive();
|
||||
self.table_state.select(None);
|
||||
self.scroll_state = self.scroll_state.position(0);
|
||||
self.send_used_explorer_log(usize::MAX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for ExtrinsicExplorer {
|
||||
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
|
||||
self.action_tx = Some(tx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
if let Some(style) = config.styles.get(&crate::app::Mode::Explorer) {
|
||||
self.palette.with_normal_style(style.get("normal_style").copied());
|
||||
@ -187,16 +164,8 @@ impl Component for ExtrinsicExplorer {
|
||||
|
||||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::UsedExplorerBlock(maybe_block_number) => {
|
||||
let block_number = maybe_block_number.unwrap_or_default();
|
||||
if let Some(exts) = self.extrinsics.get(&block_number) {
|
||||
self.current_extrinsics = Some(exts.to_vec());
|
||||
self.scroll_state = self.scroll_state.content_length(exts.len());
|
||||
} else {
|
||||
self.current_extrinsics = None;
|
||||
self.scroll_state = self.scroll_state.content_length(0);
|
||||
}
|
||||
},
|
||||
Action::UsedExplorerBlock(maybe_block_number) =>
|
||||
self.update_used_explorer_block(maybe_block_number.unwrap_or_default()),
|
||||
Action::ExtrinsicsForBlock(block_number, extrinsics) =>
|
||||
self.update_extrinsics_for_header(block_number, extrinsics),
|
||||
_ => {},
|
||||
@ -205,21 +174,15 @@ impl Component for ExtrinsicExplorer {
|
||||
}
|
||||
|
||||
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
|
||||
match key.code {
|
||||
KeyCode::Char('k') | KeyCode::Up if self.is_active => self.previous_row(),
|
||||
KeyCode::Char('j') | KeyCode::Down if self.is_active => self.next_row(),
|
||||
KeyCode::Char('g') if self.is_active => self.first_row(),
|
||||
KeyCode::Char('G') if self.is_active => self.last_row(),
|
||||
_ => {},
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
match self.is_active() {
|
||||
true => self.handle_scrollable_key_codes(key.code),
|
||||
false => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [_, place] = super::explorer_scrollbars_layout(area);
|
||||
|
||||
let (border_style, border_type) = self.palette.create_border_style(self.is_active);
|
||||
let [_, place] = super::layouts::explorer_scrollbars_layout(area);
|
||||
let (border_style, border_type) = self.palette.create_border_style(self.is_active());
|
||||
|
||||
let mut longest_pallet_name_length = 0;
|
||||
let mut longest_variant_name_length = 0;
|
||||
|
@ -7,8 +7,11 @@ use ratatui::{
|
||||
Frame,
|
||||
};
|
||||
|
||||
use super::{Component, PartialComponent, CurrentTab};
|
||||
use crate::{palette::StylePalette, config::Config, action::Action};
|
||||
use super::{Component, CurrentTab};
|
||||
use crate::{
|
||||
components::generic::PartialComponent,
|
||||
palette::StylePalette, config::Config, action::Action,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ExtrinsicsChart {
|
||||
@ -70,9 +73,7 @@ impl ExtrinsicsChart {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for ExtrinsicsChart {
|
||||
fn set_active(&mut self, _current_tab: CurrentTab) {}
|
||||
}
|
||||
impl PartialComponent<CurrentTab> for ExtrinsicsChart {}
|
||||
|
||||
impl Component for ExtrinsicsChart {
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
@ -96,7 +97,7 @@ impl Component for ExtrinsicsChart {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [_, place] = super::explorer_header_layout(area);
|
||||
let [_, place] = super::layouts::explorer_header_layout(area);
|
||||
frame.render_widget(self.extrinsics_bar_chart(place.as_size().width), place);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -5,11 +5,10 @@ use ratatui::{
|
||||
Frame,
|
||||
};
|
||||
|
||||
use super::{Component, PartialComponent, CurrentTab};
|
||||
use super::{Component, CurrentTab};
|
||||
use crate::{
|
||||
config::Config,
|
||||
action::Action,
|
||||
palette::StylePalette,
|
||||
components::generic::PartialComponent,
|
||||
config::Config, action::Action, palette::StylePalette,
|
||||
widgets::{PixelSize, BigText},
|
||||
};
|
||||
|
||||
@ -26,9 +25,7 @@ impl FinalizedBlock {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for FinalizedBlock {
|
||||
fn set_active(&mut self, _current_tab: CurrentTab) {}
|
||||
}
|
||||
impl PartialComponent<CurrentTab> for FinalizedBlock {}
|
||||
|
||||
impl Component for FinalizedBlock {
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
@ -52,7 +49,7 @@ impl Component for FinalizedBlock {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [_, _, place] = super::explorer_block_info_layout(area);
|
||||
let [_, _, place] = super::layouts::explorer_block_info_layout(area);
|
||||
|
||||
let text = self.number.to_string();
|
||||
let (border_style, border_type) = self.palette.create_border_style(false);
|
||||
|
@ -5,11 +5,10 @@ use ratatui::{
|
||||
Frame,
|
||||
};
|
||||
|
||||
use super::{Component, PartialComponent, CurrentTab};
|
||||
use super::{Component, CurrentTab};
|
||||
use crate::{
|
||||
config::Config,
|
||||
action::Action,
|
||||
palette::StylePalette,
|
||||
components::generic::PartialComponent,
|
||||
config::Config, action::Action, palette::StylePalette,
|
||||
widgets::{PixelSize, BigText},
|
||||
};
|
||||
|
||||
@ -26,9 +25,7 @@ impl LatestBlock {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for LatestBlock {
|
||||
fn set_active(&mut self, _current_tab: CurrentTab) {}
|
||||
}
|
||||
impl PartialComponent<CurrentTab> for LatestBlock {}
|
||||
|
||||
impl Component for LatestBlock {
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
@ -52,7 +49,7 @@ impl Component for LatestBlock {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [_, place, _] = super::explorer_block_info_layout(area);
|
||||
let [_, place, _] = super::layouts::explorer_block_info_layout(area);
|
||||
|
||||
let text = self.number.to_string();
|
||||
let (border_style, border_type) = self.palette.create_border_style(false);
|
||||
|
50
src/components/explorer/layouts.rs
Normal file
50
src/components/explorer/layouts.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||
|
||||
pub fn explorer_layout(area: Rect) -> [Rect; 3] {
|
||||
Layout::vertical([
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(40),
|
||||
Constraint::Percentage(30),
|
||||
]).areas(area)
|
||||
}
|
||||
|
||||
pub fn explorer_header_layout(area: Rect) -> [Rect; 2] {
|
||||
let [header, _, _] = explorer_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Percentage(50),
|
||||
]).areas(header)
|
||||
}
|
||||
|
||||
pub fn explorer_info_layout(area: Rect) -> [Rect; 2] {
|
||||
let [info, _] = explorer_header_layout(area);
|
||||
Layout::vertical([
|
||||
Constraint::Percentage(100),
|
||||
Constraint::Percentage(100),
|
||||
]).areas(info)
|
||||
}
|
||||
|
||||
pub fn explorer_block_info_layout(area: Rect) -> [Rect; 3] {
|
||||
let [blocks, _] = explorer_info_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Percentage(33),
|
||||
Constraint::Percentage(33),
|
||||
Constraint::Percentage(33),
|
||||
]).flex(Flex::SpaceBetween).areas(blocks)
|
||||
}
|
||||
|
||||
pub fn explorer_era_info_layout(area: Rect) -> [Rect; 2] {
|
||||
let [_, blocks] = explorer_info_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Percentage(50),
|
||||
]).flex(Flex::SpaceBetween).areas(blocks)
|
||||
}
|
||||
|
||||
pub fn explorer_scrollbars_layout(area: Rect) -> [Rect; 2] {
|
||||
let [_, place, _] = explorer_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Percentage(50),
|
||||
]).areas(place)
|
||||
}
|
@ -6,38 +6,19 @@ use ratatui::{
|
||||
Frame
|
||||
};
|
||||
|
||||
use super::{Component, CurrentTab, PartialComponent};
|
||||
use super::{Component, CurrentTab};
|
||||
use crate::{
|
||||
action::Action,
|
||||
config::Config,
|
||||
palette::StylePalette,
|
||||
components::generic::PartialComponent,
|
||||
action::Action, config::Config, palette::StylePalette
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LogExplorer {
|
||||
is_active: bool,
|
||||
maybe_log: Option<String>,
|
||||
palette: StylePalette,
|
||||
}
|
||||
|
||||
impl Default for LogExplorer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl LogExplorer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
is_active: false,
|
||||
maybe_log: None,
|
||||
palette: StylePalette::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for LogExplorer {
|
||||
fn set_active(&mut self, _current_tab: CurrentTab) {}
|
||||
}
|
||||
impl PartialComponent<CurrentTab> for LogExplorer {}
|
||||
|
||||
impl Component for LogExplorer {
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
@ -58,9 +39,9 @@ impl Component for LogExplorer {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [_, _, place] = super::explorer_layout(area);
|
||||
let [_, _, place] = super::layouts::explorer_layout(area);
|
||||
|
||||
let (border_style, border_type) = self.palette.create_border_style(self.is_active);
|
||||
let (border_style, border_type) = self.palette.create_border_style(false);
|
||||
|
||||
let line = match &self.maybe_log {
|
||||
Some(log) => Line::from(hex::encode(log)),
|
||||
|
@ -1,14 +1,15 @@
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{KeyCode, KeyEvent};
|
||||
use ratatui::{
|
||||
layout::{Constraint, Flex, Layout, Rect},
|
||||
Frame,
|
||||
};
|
||||
use ratatui::{layout::Rect, Frame};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::Component;
|
||||
use super::{
|
||||
generic::{Activatable, PartialComponent},
|
||||
Component,
|
||||
};
|
||||
use crate::{action::Action, app::Mode, config::Config};
|
||||
|
||||
pub mod layouts;
|
||||
mod latest_block;
|
||||
mod finalized_block;
|
||||
mod block_ticker;
|
||||
@ -29,21 +30,17 @@ use block_explorer::BlockExplorer;
|
||||
use extrinsic_explorer::ExtrinsicExplorer;
|
||||
use log_explorer::LogExplorer;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum CurrentTab {
|
||||
Nothing,
|
||||
Blocks,
|
||||
Extrinsics,
|
||||
}
|
||||
|
||||
pub trait PartialComponent: Component {
|
||||
fn set_active(&mut self, current_tab: CurrentTab);
|
||||
}
|
||||
|
||||
pub struct Explorer {
|
||||
is_active: bool,
|
||||
current_tab: CurrentTab,
|
||||
components: Vec<Box<dyn PartialComponent>>
|
||||
components: Vec<Box<dyn PartialComponent<CurrentTab>>>
|
||||
}
|
||||
|
||||
impl Default for Explorer {
|
||||
@ -82,10 +79,15 @@ impl Explorer {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialComponent for Explorer {
|
||||
fn set_active(&mut self, _current_tab: CurrentTab) {}
|
||||
impl Activatable for Explorer {
|
||||
fn is_active(&self) -> bool { self.is_active }
|
||||
fn is_inactive(&self) -> bool { !self.is_active }
|
||||
fn set_inactive(&mut self) { self.is_active = false; }
|
||||
fn set_active(&mut self) { self.is_active = true; }
|
||||
}
|
||||
|
||||
impl PartialComponent<CurrentTab> for Explorer {}
|
||||
|
||||
impl Component for Explorer {
|
||||
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
|
||||
for component in self.components.iter_mut() {
|
||||
@ -104,31 +106,35 @@ impl Component for Explorer {
|
||||
}
|
||||
|
||||
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
|
||||
if !self.is_active { return Ok(None); }
|
||||
if self.is_inactive() { return Ok(None); }
|
||||
|
||||
match key.code {
|
||||
KeyCode::Esc => {
|
||||
self.is_active = false;
|
||||
self.set_inactive();
|
||||
self.current_tab = CurrentTab::Nothing;
|
||||
for component in self.components.iter_mut() {
|
||||
component.set_active(self.current_tab.clone());
|
||||
component.set_active_tab(self.current_tab);
|
||||
}
|
||||
return Ok(Some(Action::SetActiveScreen(Mode::Menu)));
|
||||
},
|
||||
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => {
|
||||
self.move_right();
|
||||
for component in self.components.iter_mut() {
|
||||
component.set_active(self.current_tab.clone());
|
||||
component.set_active_tab(self.current_tab);
|
||||
}
|
||||
},
|
||||
KeyCode::Char('h') | KeyCode::Left => {
|
||||
self.move_left();
|
||||
for component in self.components.iter_mut() {
|
||||
component.set_active(self.current_tab.clone());
|
||||
component.set_active_tab(self.current_tab);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
for component in self.components.iter_mut() {
|
||||
component.handle_key_event(key)?;
|
||||
let maybe_action = component.handle_key_event(key);
|
||||
if let Ok(Some(_)) = maybe_action {
|
||||
return maybe_action;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -137,10 +143,10 @@ impl Component for Explorer {
|
||||
|
||||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
if let Action::SetActiveScreen(Mode::Explorer) = action {
|
||||
self.is_active = true;
|
||||
self.set_active();
|
||||
self.current_tab = CurrentTab::Blocks;
|
||||
for component in self.components.iter_mut() {
|
||||
component.set_active(self.current_tab.clone());
|
||||
component.set_active_tab(self.current_tab);
|
||||
}
|
||||
}
|
||||
for component in self.components.iter_mut() {
|
||||
@ -150,59 +156,10 @@ impl Component for Explorer {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let screen = super::screen_layout(area);
|
||||
let screen = super::layouts::screen_layout(area);
|
||||
for component in self.components.iter_mut() {
|
||||
component.draw(frame, screen)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn explorer_layout(area: Rect) -> [Rect; 3] {
|
||||
Layout::vertical([
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(40),
|
||||
Constraint::Percentage(30),
|
||||
]).areas(area)
|
||||
}
|
||||
|
||||
pub fn explorer_header_layout(area: Rect) -> [Rect; 2] {
|
||||
let [header, _, _] = explorer_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Percentage(50),
|
||||
]).areas(header)
|
||||
}
|
||||
|
||||
pub fn explorer_info_layout(area: Rect) -> [Rect; 2] {
|
||||
let [info, _] = explorer_header_layout(area);
|
||||
Layout::vertical([
|
||||
Constraint::Percentage(100),
|
||||
Constraint::Percentage(100),
|
||||
]).areas(info)
|
||||
}
|
||||
|
||||
pub fn explorer_block_info_layout(area: Rect) -> [Rect; 3] {
|
||||
let [blocks, _] = explorer_info_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Percentage(33),
|
||||
Constraint::Percentage(33),
|
||||
Constraint::Percentage(33),
|
||||
]).flex(Flex::SpaceBetween).areas(blocks)
|
||||
}
|
||||
|
||||
pub fn explorer_era_info_layout(area: Rect) -> [Rect; 2] {
|
||||
let [_, blocks] = explorer_info_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Percentage(50),
|
||||
]).flex(Flex::SpaceBetween).areas(blocks)
|
||||
}
|
||||
|
||||
pub fn explorer_scrollbars_layout(area: Rect) -> [Rect; 2] {
|
||||
let [_, place, _] = explorer_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Percentage(50),
|
||||
]).areas(place)
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ impl Component for FpsCounter {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [_, place] = super::header_layout(area);
|
||||
let [_, place] = super::layouts::header_layout(area);
|
||||
|
||||
let message = format!(
|
||||
" {:.2} ticks/sec | {:.2} FPS",
|
||||
|
@ -1,7 +1,9 @@
|
||||
mod activatable;
|
||||
mod helpable;
|
||||
mod scrollable;
|
||||
mod partial;
|
||||
|
||||
pub use activatable::Activatable;
|
||||
pub use helpable::Helpable;
|
||||
pub use scrollable::Scrollable;
|
||||
pub use partial::PartialComponent;
|
||||
|
8
src/components/generic/partial.rs
Normal file
8
src/components/generic/partial.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::components::Component;
|
||||
|
||||
pub trait PartialComponent<TabType>: Component
|
||||
where
|
||||
TabType: std::fmt::Debug + Clone + Copy + PartialEq,
|
||||
{
|
||||
fn set_active_tab(&mut self, _current_tab: TabType) {}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
use std::{
|
||||
cmp::{PartialEq, PartialOrd}, ops::{Add, Sub}
|
||||
};
|
||||
use crossterm::event::KeyCode;
|
||||
|
||||
use color_eyre::Result;
|
||||
use crate::action::Action;
|
||||
@ -13,12 +14,15 @@ pub trait Scrollable {
|
||||
+ From<u8>
|
||||
+ Copy;
|
||||
|
||||
fn apply_next_row(&mut self, new_index: Self::IndexType) -> Result<Option<Action>>;
|
||||
fn apply_prev_row(&mut self, new_index: Self::IndexType) -> Result<Option<Action>>;
|
||||
|
||||
fn selected_index(&self) -> Option<Self::IndexType>;
|
||||
fn items_length(&self) -> Self::IndexType;
|
||||
|
||||
fn apply_next_row(&mut self, _new_index: Self::IndexType) -> Result<Option<Action>> { Ok(None) }
|
||||
fn apply_prev_row(&mut self, _new_index: Self::IndexType) -> Result<Option<Action>> { Ok(None) }
|
||||
fn apply_first_row(&mut self) -> Result<Option<Action>> { Ok(None) }
|
||||
fn apply_last_row(&mut self) -> Result<Option<Action>> { Ok(None) }
|
||||
|
||||
|
||||
fn next_row(&mut self) -> Result<Option<Action>> {
|
||||
let length = self.items_length();
|
||||
if length == 0.into() {
|
||||
@ -55,4 +59,28 @@ pub trait Scrollable {
|
||||
};
|
||||
self.apply_prev_row(new_index)
|
||||
}
|
||||
|
||||
fn first_row(&mut self) -> Result<Option<Action>> {
|
||||
if self.items_length() == 0.into() {
|
||||
return Ok(None);
|
||||
}
|
||||
self.apply_first_row()
|
||||
}
|
||||
|
||||
fn last_row(&mut self) -> Result<Option<Action>> {
|
||||
if self.items_length() == 0.into() {
|
||||
return Ok(None);
|
||||
}
|
||||
self.apply_last_row()
|
||||
}
|
||||
|
||||
fn handle_scrollable_key_codes(&mut self, key_code: KeyCode) -> Result<Option<Action>> {
|
||||
match key_code {
|
||||
KeyCode::Char('k') | KeyCode::Up => self.prev_row(),
|
||||
KeyCode::Char('j') | KeyCode::Down => self.next_row(),
|
||||
KeyCode::Char('g') => self.first_row(),
|
||||
KeyCode::Char('G') => self.last_row(),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ impl Component for Health {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [place, _] = super::header_layout(area);
|
||||
let [place, _] = super::layouts::header_layout(area);
|
||||
|
||||
let message = format!("{:^12} | tx.pool: {:^3} | peers: {:^3} | {:^9} | validators {:^4} | nominators {:^4} |",
|
||||
self.name_as_string(),
|
||||
|
37
src/components/layouts.rs
Normal file
37
src/components/layouts.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use ratatui::layout::{Constraint, Layout, Rect};
|
||||
|
||||
pub fn global_layout(area: Rect) -> [Rect; 2] {
|
||||
Layout::vertical([
|
||||
Constraint::Length(1),
|
||||
Constraint::Fill(1),
|
||||
]).areas(area)
|
||||
}
|
||||
|
||||
pub fn header_layout(area: Rect) -> [Rect; 2] {
|
||||
let [header, _] = global_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(27),
|
||||
]).areas(header)
|
||||
}
|
||||
|
||||
pub fn main_layout(area: Rect) -> [Rect; 2] {
|
||||
let [_, main] = global_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Max(30),
|
||||
Constraint::Fill(1),
|
||||
]).areas(main)
|
||||
}
|
||||
|
||||
pub fn menu_layout(area: Rect) -> [Rect; 2] {
|
||||
let [menu, _] = main_layout(area);
|
||||
Layout::vertical([
|
||||
Constraint::Min(0),
|
||||
Constraint::Length(5),
|
||||
]).areas(menu)
|
||||
}
|
||||
|
||||
pub fn screen_layout(area: Rect) -> Rect {
|
||||
let [_, screen] = main_layout(area);
|
||||
screen
|
||||
}
|
@ -118,7 +118,7 @@ impl Component for Menu {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [menu, _] = super::menu_layout(area);
|
||||
let [menu, _] = super::layouts::menu_layout(area);
|
||||
|
||||
let (color, border_type) = self.palette.create_border_style(self.is_active());
|
||||
let block = Block::bordered()
|
||||
|
@ -1,7 +1,7 @@
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{KeyEvent, MouseEvent};
|
||||
use ratatui::{
|
||||
layout::{Constraint, Layout, Rect, Size},
|
||||
layout::{Rect, Size},
|
||||
Frame,
|
||||
};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
@ -9,6 +9,7 @@ use std::sync::mpsc::Sender;
|
||||
|
||||
use crate::{palette, action::Action, config::Config, tui::Event};
|
||||
|
||||
pub mod layouts;
|
||||
pub mod fps;
|
||||
pub mod health;
|
||||
pub mod menu;
|
||||
@ -67,39 +68,3 @@ pub trait Component {
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()>;
|
||||
}
|
||||
|
||||
pub fn global_layout(area: Rect) -> [Rect; 2] {
|
||||
Layout::vertical([
|
||||
Constraint::Length(1),
|
||||
Constraint::Fill(1),
|
||||
]).areas(area)
|
||||
}
|
||||
|
||||
pub fn header_layout(area: Rect) -> [Rect; 2] {
|
||||
let [header, _] = global_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(27),
|
||||
]).areas(header)
|
||||
}
|
||||
|
||||
pub fn main_layout(area: Rect) -> [Rect; 2] {
|
||||
let [_, main] = global_layout(area);
|
||||
Layout::horizontal([
|
||||
Constraint::Max(30),
|
||||
Constraint::Fill(1),
|
||||
]).areas(main)
|
||||
}
|
||||
|
||||
pub fn menu_layout(area: Rect) -> [Rect; 2] {
|
||||
let [menu, _] = main_layout(area);
|
||||
Layout::vertical([
|
||||
Constraint::Min(0),
|
||||
Constraint::Length(5),
|
||||
]).areas(menu)
|
||||
}
|
||||
|
||||
pub fn screen_layout(area: Rect) -> Rect {
|
||||
let [_, screen] = main_layout(area);
|
||||
screen
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ impl Component for Validator {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let screen = super::screen_layout(area);
|
||||
let screen = super::layouts::screen_layout(area);
|
||||
for component in self.components.iter_mut() {
|
||||
component.draw(frame, screen)?;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ impl Component for Version {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [_, version] = super::menu_layout(area);
|
||||
let [_, version] = super::layouts::menu_layout(area);
|
||||
|
||||
let text_style = self.palette.create_basic_style(false);
|
||||
let (border_style, border_type) = self.palette.create_border_style(false);
|
||||
|
@ -239,7 +239,7 @@ impl Component for Wallet {
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let screen = super::screen_layout(area);
|
||||
let screen = super::layouts::screen_layout(area);
|
||||
for component in self.components.iter_mut() {
|
||||
component.draw(frame, screen)?;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user