mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-04-08 17:05:59 +02:00
Basic temp
This commit is contained in:
parent
18bce9f0a0
commit
a78edc88c0
33
src/app.rs
33
src/app.rs
@ -29,6 +29,7 @@ use frozen_state::FrozenState;
|
||||
use crate::{
|
||||
canvas::Painter,
|
||||
constants,
|
||||
data_conversion::ConvertedData,
|
||||
tuine::{Application, Element, Flex, Status, ViewContext},
|
||||
units::data_units::DataUnit,
|
||||
Pid,
|
||||
@ -129,7 +130,13 @@ impl Default for CurrentScreen {
|
||||
pub enum AppMessages {
|
||||
Update(Box<data_harvester::Data>),
|
||||
OpenHelp,
|
||||
KillProcess { to_kill: Vec<Pid> },
|
||||
ConfirmKillProcess {
|
||||
to_kill: Vec<Pid>,
|
||||
},
|
||||
KillProcess {
|
||||
to_kill: Vec<Pid>,
|
||||
signal: Option<i32>,
|
||||
},
|
||||
ToggleFreeze,
|
||||
Reset,
|
||||
Clean,
|
||||
@ -209,27 +216,34 @@ impl AppState {
|
||||
impl Application for AppState {
|
||||
type Message = AppMessages;
|
||||
|
||||
fn update(&mut self, message: Self::Message) {
|
||||
fn update(&mut self, message: Self::Message) -> bool {
|
||||
match message {
|
||||
AppMessages::Update(new_data) => {
|
||||
self.data_collection.eat_data(new_data);
|
||||
true
|
||||
}
|
||||
AppMessages::OpenHelp => {
|
||||
self.set_current_screen(CurrentScreen::Help);
|
||||
true
|
||||
}
|
||||
AppMessages::KillProcess { to_kill } => {}
|
||||
AppMessages::ConfirmKillProcess { to_kill } => true,
|
||||
AppMessages::KillProcess { to_kill, signal } => true,
|
||||
AppMessages::ToggleFreeze => {
|
||||
self.frozen_state.toggle(&self.data_collection);
|
||||
true
|
||||
}
|
||||
AppMessages::Clean => {
|
||||
self.data_collection
|
||||
.clean_data(constants::STALE_MAX_MILLISECONDS);
|
||||
false
|
||||
}
|
||||
AppMessages::Quit => {
|
||||
self.terminator.store(true, SeqCst);
|
||||
false
|
||||
}
|
||||
AppMessages::Reset => {
|
||||
// FIXME: Reset
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -243,10 +257,21 @@ impl Application for AppState {
|
||||
use crate::tuine::StatefulComponent;
|
||||
use crate::tuine::{TempTable, TextTable, TextTableProps};
|
||||
|
||||
let data = match &self.frozen_state {
|
||||
FrozenState::NotFrozen => &self.data_collection,
|
||||
FrozenState::Frozen(frozen_data_collection) => &frozen_data_collection,
|
||||
};
|
||||
|
||||
let mut converted_data = ConvertedData::default();
|
||||
|
||||
Flex::column()
|
||||
.with_flex_child(
|
||||
Flex::row_with_children(vec![
|
||||
FlexElement::new(TempTable::build(ctx)),
|
||||
FlexElement::new(TempTable::build(
|
||||
ctx,
|
||||
&self.painter,
|
||||
converted_data.temp_table(data, self.app_config_fields.temperature_type),
|
||||
)),
|
||||
FlexElement::new(TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["D", "E", "F"]),
|
||||
|
@ -175,6 +175,7 @@ impl Default for DataCollection {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Just rip this out, store only stringified data...?
|
||||
impl DataCollection {
|
||||
pub fn reset(&mut self) {
|
||||
self.timed_data_vec = Default::default();
|
||||
|
@ -23,7 +23,7 @@ pub struct TempHarvest {
|
||||
pub temperature: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum TemperatureType {
|
||||
Celsius,
|
||||
Kelvin,
|
||||
|
@ -7,6 +7,47 @@ use crate::{app::AxisScaling, units::data_units::DataUnit};
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Stores converted data, and caches results.
|
||||
#[derive(Default)]
|
||||
pub struct ConvertedData {
|
||||
temp_table: Option<Vec<Vec<Cow<'static, str>>>>,
|
||||
}
|
||||
|
||||
impl ConvertedData {
|
||||
pub fn temp_table(
|
||||
&mut self, data: &DataCollection, temp_type: TemperatureType,
|
||||
) -> Vec<Vec<Cow<'static, str>>> {
|
||||
match &self.temp_table {
|
||||
Some(temp_table) => temp_table.clone(),
|
||||
None => {
|
||||
let temp_table = if data.temp_harvest.is_empty() {
|
||||
vec![vec!["No Sensors Found".into(), "".into()]]
|
||||
} else {
|
||||
let unit = match temp_type {
|
||||
data_harvester::temperature::TemperatureType::Celsius => "°C",
|
||||
data_harvester::temperature::TemperatureType::Kelvin => "K",
|
||||
data_harvester::temperature::TemperatureType::Fahrenheit => "°F",
|
||||
};
|
||||
|
||||
data.temp_harvest
|
||||
.iter()
|
||||
.map(|temp_harvest| {
|
||||
let val = temp_harvest.temperature.ceil().to_string();
|
||||
vec![
|
||||
temp_harvest.name.clone().into(),
|
||||
format!("{}{}", val, unit).into(),
|
||||
]
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
self.temp_table = Some(temp_table.clone());
|
||||
temp_table
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Point is of time, data
|
||||
type Point = (f64, f64);
|
||||
|
||||
|
@ -14,8 +14,8 @@ pub type CrosstermBackend = tui::backend::CrosstermBackend<std::io::Stdout>;
|
||||
pub trait Application: Sized {
|
||||
type Message: Debug;
|
||||
|
||||
/// Determines how to handle a given message.
|
||||
fn update(&mut self, message: Self::Message);
|
||||
/// Determines how to handle a given message, and returns `true` if this update should trigger a redraw.
|
||||
fn update(&mut self, message: Self::Message) -> bool;
|
||||
|
||||
/// Returns whether to stop the application. Defaults to
|
||||
/// always returning false.
|
||||
|
@ -9,8 +9,7 @@ use crate::tuine::{
|
||||
/// A set of styles for a [`Block`].
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct StyleSheet {
|
||||
text: Style,
|
||||
border: Style,
|
||||
pub border: Style,
|
||||
}
|
||||
|
||||
/// A [`Block`] is a widget that draws a border around a child [`Component`], as well as optional
|
||||
@ -47,6 +46,11 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn style(mut self, style: StyleSheet) -> Self {
|
||||
self.style_sheet = style;
|
||||
self
|
||||
}
|
||||
|
||||
fn inner_rect(&self, original: Rect) -> Rect {
|
||||
let mut inner = original;
|
||||
|
||||
|
@ -5,12 +5,14 @@ mod table_scroll_state;
|
||||
use self::table_scroll_state::ScrollState;
|
||||
|
||||
pub mod data_row;
|
||||
use crossterm::event::KeyCode;
|
||||
pub use data_row::DataRow;
|
||||
|
||||
pub mod data_cell;
|
||||
pub use data_cell::DataCell;
|
||||
|
||||
pub mod sort_type;
|
||||
|
||||
pub use sort_type::SortType;
|
||||
|
||||
pub mod props;
|
||||
@ -35,9 +37,9 @@ use crate::{
|
||||
/// A set of styles for a [`TextTable`].
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct StyleSheet {
|
||||
text: Style,
|
||||
selected_text: Style,
|
||||
table_header: Style,
|
||||
pub text: Style,
|
||||
pub selected_text: Style,
|
||||
pub table_header: Style,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Default)]
|
||||
@ -108,6 +110,71 @@ impl<Message> TextTable<Message> {
|
||||
|
||||
self.column_widths = column_widths;
|
||||
}
|
||||
|
||||
fn update_sort_column(&mut self, state: &mut TextTableState, x: u16) -> Status {
|
||||
match state.sort {
|
||||
SortType::Unsortable => Status::Ignored,
|
||||
SortType::Ascending(column) | SortType::Descending(column) => {
|
||||
let mut cursor = 0;
|
||||
for (selected_column, width) in self.column_widths.iter().enumerate() {
|
||||
if x >= cursor && x <= cursor + width {
|
||||
match state.sort {
|
||||
SortType::Ascending(_) => {
|
||||
if selected_column == column {
|
||||
// FIXME: This should handle default sorting orders...
|
||||
state.sort = SortType::Descending(selected_column);
|
||||
} else {
|
||||
state.sort = SortType::Ascending(selected_column);
|
||||
}
|
||||
}
|
||||
SortType::Descending(_) => {
|
||||
if selected_column == column {
|
||||
// FIXME: This should handle default sorting orders...
|
||||
state.sort = SortType::Ascending(selected_column);
|
||||
} else {
|
||||
state.sort = SortType::Descending(selected_column);
|
||||
}
|
||||
}
|
||||
SortType::Unsortable => unreachable!(), // Should be impossible by above check.
|
||||
}
|
||||
|
||||
return Status::Captured;
|
||||
} else {
|
||||
cursor += width;
|
||||
}
|
||||
}
|
||||
Status::Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_page_up(&mut self, state: &mut TextTableState, rect: Rect) -> Status {
|
||||
let height = rect.height.saturating_sub(self.table_gap + 1);
|
||||
state.scroll.move_up(height.into())
|
||||
}
|
||||
|
||||
pub fn on_page_down(&mut self, state: &mut TextTableState, rect: Rect) -> Status {
|
||||
let height = rect.height.saturating_sub(self.table_gap + 1);
|
||||
state.scroll.move_down(height.into())
|
||||
}
|
||||
|
||||
pub fn scroll_down(
|
||||
&mut self, state: &mut TextTableState, messages: &mut Vec<Message>,
|
||||
) -> Status {
|
||||
let status = state.scroll.move_down(1);
|
||||
if let Some(on_select) = &self.on_select {
|
||||
messages.push(on_select(state.scroll.current_index()));
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
pub fn scroll_up(&mut self, state: &mut TextTableState, messages: &mut Vec<Message>) -> Status {
|
||||
let status = state.scroll.move_up(1);
|
||||
if let Some(on_select) = &self.on_select {
|
||||
messages.push(on_select(state.scroll.current_index()));
|
||||
}
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> StatefulComponent<Message> for TextTable<Message> {
|
||||
@ -190,9 +257,31 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
||||
};
|
||||
|
||||
// Now build up our headers...
|
||||
let header = Row::new(self.columns.iter().map(|column| column.name.clone()))
|
||||
.style(self.style_sheet.table_header)
|
||||
.bottom_margin(self.table_gap);
|
||||
let header = match state.sort {
|
||||
SortType::Unsortable => Row::new(self.columns.iter().map(|column| column.name.clone())),
|
||||
SortType::Ascending(sort_column) => {
|
||||
Row::new(self.columns.iter().enumerate().map(|(index, column)| {
|
||||
const UP_ARROW: &str = "▲";
|
||||
if index == sort_column {
|
||||
format!("{}{}", column.name, UP_ARROW).into()
|
||||
} else {
|
||||
column.name.clone()
|
||||
}
|
||||
}))
|
||||
}
|
||||
SortType::Descending(sort_column) => {
|
||||
Row::new(self.columns.iter().enumerate().map(|(index, column)| {
|
||||
const DOWN_ARROW: &str = "▼";
|
||||
if index == sort_column {
|
||||
format!("{}{}", column.name, DOWN_ARROW).into()
|
||||
} else {
|
||||
column.name.clone()
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
.style(self.style_sheet.table_header)
|
||||
.bottom_margin(self.table_gap);
|
||||
|
||||
let mut table = Table::new(data_slice)
|
||||
.header(header)
|
||||
@ -219,6 +308,10 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
||||
Event::Keyboard(key_event) => {
|
||||
if key_event.modifiers.is_empty() {
|
||||
match key_event.code {
|
||||
KeyCode::PageUp => self.on_page_up(state, rect),
|
||||
KeyCode::PageDown => self.on_page_down(state, rect),
|
||||
KeyCode::Up => self.scroll_up(state, messages),
|
||||
KeyCode::Down => self.scroll_down(state, messages),
|
||||
_ => Status::Ignored,
|
||||
}
|
||||
} else {
|
||||
@ -232,52 +325,7 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
||||
let y = mouse_event.row - rect.top();
|
||||
if y == 0 {
|
||||
let x = mouse_event.column - rect.left();
|
||||
match state.sort {
|
||||
SortType::Unsortable => Status::Ignored,
|
||||
SortType::Ascending(column) | SortType::Descending(column) => {
|
||||
let mut cursor = 0;
|
||||
for (selected_column, width) in
|
||||
self.column_widths.iter().enumerate()
|
||||
{
|
||||
let end = cursor + width;
|
||||
|
||||
if x >= cursor && x <= end {
|
||||
match state.sort {
|
||||
SortType::Ascending(_) => {
|
||||
if selected_column == column {
|
||||
// FIXME: This should handle default sorting orders...
|
||||
state.sort = SortType::Descending(
|
||||
selected_column,
|
||||
);
|
||||
} else {
|
||||
state.sort = SortType::Ascending(
|
||||
selected_column,
|
||||
);
|
||||
}
|
||||
}
|
||||
SortType::Descending(_) => {
|
||||
if selected_column == column {
|
||||
// FIXME: This should handle default sorting orders...
|
||||
state.sort = SortType::Ascending(
|
||||
selected_column,
|
||||
);
|
||||
} else {
|
||||
state.sort = SortType::Descending(
|
||||
selected_column,
|
||||
);
|
||||
}
|
||||
}
|
||||
SortType::Unsortable => unreachable!(), // Should be impossible by above check.
|
||||
}
|
||||
|
||||
return Status::Captured;
|
||||
} else {
|
||||
cursor += width;
|
||||
}
|
||||
}
|
||||
Status::Ignored
|
||||
}
|
||||
}
|
||||
self.update_sort_column(state, x)
|
||||
} else if y > self.table_gap {
|
||||
let visual_index = usize::from(y - self.table_gap);
|
||||
match state.scroll.set_visual_index(visual_index) {
|
||||
@ -297,20 +345,8 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
||||
Status::Ignored
|
||||
}
|
||||
}
|
||||
MouseEventKind::ScrollDown => {
|
||||
let status = state.scroll.move_down(1);
|
||||
if let Some(on_select) = &self.on_select {
|
||||
messages.push(on_select(state.scroll.current_index()));
|
||||
}
|
||||
status
|
||||
}
|
||||
MouseEventKind::ScrollUp => {
|
||||
let status = state.scroll.move_up(1);
|
||||
if let Some(on_select) = &self.on_select {
|
||||
messages.push(on_select(state.scroll.current_index()));
|
||||
}
|
||||
status
|
||||
}
|
||||
MouseEventKind::ScrollDown => self.scroll_down(state, messages),
|
||||
MouseEventKind::ScrollUp => self.scroll_up(state, messages),
|
||||
_ => Status::Ignored,
|
||||
}
|
||||
} else {
|
||||
|
@ -94,6 +94,12 @@ impl<Message> TextTableProps<Message> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the style for the entry.
|
||||
pub fn style(mut self, style: StyleSheet) -> Self {
|
||||
self.style_sheet = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn try_sort_data(&mut self, sort_type: SortType) {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::tuine::{State, ViewContext};
|
||||
use crate::tuine::{State, StateContext, ViewContext};
|
||||
|
||||
use super::TmpComponent;
|
||||
|
||||
|
0
src/tuine/component/widget/battery_table.rs
Normal file
0
src/tuine/component/widget/battery_table.rs
Normal file
0
src/tuine/component/widget/cpu_graph.rs
Normal file
0
src/tuine/component/widget/cpu_graph.rs
Normal file
0
src/tuine/component/widget/cpu_simple.rs
Normal file
0
src/tuine/component/widget/cpu_simple.rs
Normal file
0
src/tuine/component/widget/disk_table.rs
Normal file
0
src/tuine/component/widget/disk_table.rs
Normal file
0
src/tuine/component/widget/mem_graph.rs
Normal file
0
src/tuine/component/widget/mem_graph.rs
Normal file
0
src/tuine/component/widget/mem_simple.rs
Normal file
0
src/tuine/component/widget/mem_simple.rs
Normal file
@ -1,2 +1,32 @@
|
||||
pub mod simple_table;
|
||||
pub use simple_table::*;
|
||||
|
||||
pub mod cpu_graph;
|
||||
pub use cpu_graph::*;
|
||||
|
||||
pub mod disk_table;
|
||||
pub use disk_table::*;
|
||||
|
||||
pub mod mem_graph;
|
||||
pub use mem_graph::*;
|
||||
|
||||
pub mod net_graph;
|
||||
pub use net_graph::*;
|
||||
|
||||
pub mod process_table;
|
||||
pub use process_table::*;
|
||||
|
||||
pub mod temp_table;
|
||||
pub use temp_table::*;
|
||||
|
||||
pub mod battery_table;
|
||||
pub use battery_table::*;
|
||||
|
||||
pub mod cpu_simple;
|
||||
pub use cpu_simple::*;
|
||||
|
||||
pub mod mem_simple;
|
||||
pub use mem_simple::*;
|
||||
|
||||
pub mod net_simple;
|
||||
pub use net_simple::*;
|
||||
|
0
src/tuine/component/widget/net_graph.rs
Normal file
0
src/tuine/component/widget/net_graph.rs
Normal file
0
src/tuine/component/widget/net_simple.rs
Normal file
0
src/tuine/component/widget/net_simple.rs
Normal file
0
src/tuine/component/widget/process_table.rs
Normal file
0
src/tuine/component/widget/process_table.rs
Normal file
72
src/tuine/component/widget/simple_table.rs
Normal file
72
src/tuine/component/widget/simple_table.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use tui::style::Style;
|
||||
|
||||
use crate::tuine::{
|
||||
self, block,
|
||||
text_table::{self, DataRow, SortType, TextTableProps},
|
||||
Block, Event, Shortcut, StatefulComponent, Status, TextTable, TmpComponent, ViewContext,
|
||||
};
|
||||
|
||||
/// A set of styles for a [`SimpleTable`].
|
||||
#[derive(Default)]
|
||||
pub struct StyleSheet {
|
||||
pub text: Style,
|
||||
pub selected_text: Style,
|
||||
pub table_header: Style,
|
||||
pub border: Style,
|
||||
}
|
||||
|
||||
/// A [`SimpleTable`] is a wrapper around a [`TextTable`] with basic shortcut support already added for:
|
||||
/// - Skipping to the start/end of the table
|
||||
/// - Scrolling up/down by a page
|
||||
/// - Configurable sorting options
|
||||
pub struct SimpleTable<Message> {
|
||||
inner: Block<Message, Shortcut<Message, TextTable<Message>>>,
|
||||
}
|
||||
|
||||
impl<Message> SimpleTable<Message> {
|
||||
#[track_caller]
|
||||
pub fn build<C: Into<std::borrow::Cow<'static, str>>, R: Into<DataRow>>(
|
||||
ctx: &mut ViewContext<'_>, style: StyleSheet, columns: Vec<C>, data: Vec<R>,
|
||||
) -> Self {
|
||||
let shortcut = Shortcut::with_child(TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(columns)
|
||||
.rows(data)
|
||||
.default_sort(SortType::Ascending(1))
|
||||
.style(text_table::StyleSheet {
|
||||
text: style.text,
|
||||
selected_text: style.selected_text,
|
||||
table_header: style.table_header,
|
||||
}),
|
||||
));
|
||||
|
||||
Self {
|
||||
inner: Block::with_child(shortcut).style(block::StyleSheet {
|
||||
border: style.border,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> TmpComponent<Message> for SimpleTable<Message> {
|
||||
fn draw<Backend>(
|
||||
&mut self, state_ctx: &mut tuine::StateContext<'_>, draw_ctx: &tuine::DrawContext<'_>,
|
||||
frame: &mut tui::Frame<'_, Backend>,
|
||||
) where
|
||||
Backend: tui::backend::Backend,
|
||||
{
|
||||
self.inner.draw(state_ctx, draw_ctx, frame);
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self, state_ctx: &mut tuine::StateContext<'_>, draw_ctx: &tuine::DrawContext<'_>,
|
||||
event: tuine::Event, messages: &mut Vec<Message>,
|
||||
) -> tuine::Status {
|
||||
self.inner.on_event(state_ctx, draw_ctx, event, messages)
|
||||
}
|
||||
|
||||
fn layout(&self, bounds: tuine::Bounds, node: &mut tuine::LayoutNode) -> tuine::Size {
|
||||
self.inner.layout(bounds, node)
|
||||
}
|
||||
}
|
@ -1,35 +1,41 @@
|
||||
use crate::tuine::{
|
||||
text_table::{DataRow, SortType, TextTableProps},
|
||||
Block, Shortcut, StatefulComponent, TextTable, TmpComponent, ViewContext,
|
||||
use crate::{
|
||||
canvas::Painter,
|
||||
tuine::{
|
||||
Bounds, DataRow, DrawContext, LayoutNode, SimpleTable, Size, StateContext, Status,
|
||||
TmpComponent, ViewContext,
|
||||
},
|
||||
};
|
||||
|
||||
/// A [`TempTable`] is a text table that is meant to display temperature data.
|
||||
use super::simple_table;
|
||||
|
||||
/// A [`TempTable`] is a table displaying temperature data.
|
||||
///
|
||||
/// It wraps a [`SimpleTable`], with set columns and manages extracting data and styling.
|
||||
pub struct TempTable<Message> {
|
||||
inner: Block<Message, Shortcut<Message, TextTable<Message>>>,
|
||||
inner: SimpleTable<Message>,
|
||||
}
|
||||
|
||||
impl<Message> TempTable<Message> {
|
||||
#[track_caller]
|
||||
pub fn build(ctx: &mut ViewContext<'_>) -> Self {
|
||||
pub fn build<R: Into<DataRow>>(
|
||||
ctx: &mut ViewContext<'_>, painter: &Painter, data: Vec<R>,
|
||||
) -> Self {
|
||||
let style = simple_table::StyleSheet {
|
||||
text: painter.colours.text_style,
|
||||
selected_text: painter.colours.currently_selected_text_style,
|
||||
table_header: painter.colours.table_header_style,
|
||||
border: painter.colours.border_style,
|
||||
};
|
||||
|
||||
Self {
|
||||
inner: Block::with_child(Shortcut::with_child(TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["Sensor", "Temp"])
|
||||
.rows(vec![
|
||||
DataRow::default().cell("A").cell(2),
|
||||
DataRow::default().cell("B").cell(3),
|
||||
DataRow::default().cell("C").cell(1),
|
||||
])
|
||||
.default_sort(SortType::Ascending(1)),
|
||||
))),
|
||||
inner: SimpleTable::build(ctx, style, vec!["Sensor", "Temp"], data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> TmpComponent<Message> for TempTable<Message> {
|
||||
fn draw<Backend>(
|
||||
&mut self, state_ctx: &mut crate::tuine::StateContext<'_>,
|
||||
draw_ctx: &crate::tuine::DrawContext<'_>, frame: &mut tui::Frame<'_, Backend>,
|
||||
&mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||
frame: &mut tui::Frame<'_, Backend>,
|
||||
) where
|
||||
Backend: tui::backend::Backend,
|
||||
{
|
||||
@ -37,16 +43,13 @@ impl<Message> TmpComponent<Message> for TempTable<Message> {
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self, state_ctx: &mut crate::tuine::StateContext<'_>,
|
||||
draw_ctx: &crate::tuine::DrawContext<'_>, event: crate::tuine::Event,
|
||||
messages: &mut Vec<Message>,
|
||||
) -> crate::tuine::Status {
|
||||
&mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||
event: crate::tuine::Event, messages: &mut Vec<Message>,
|
||||
) -> Status {
|
||||
self.inner.on_event(state_ctx, draw_ctx, event, messages)
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self, bounds: crate::tuine::Bounds, node: &mut crate::tuine::LayoutNode,
|
||||
) -> crate::tuine::Size {
|
||||
fn layout(&self, bounds: Bounds, node: &mut LayoutNode) -> Size {
|
||||
self.inner.layout(bounds, node)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use tui::Frame;
|
||||
|
||||
use super::{
|
||||
Block, Bounds, Carousel, Container, DrawContext, Empty, Event, Flex, LayoutNode, Shortcut,
|
||||
Size, StateContext, Status, TempTable, TextTable, TmpComponent,
|
||||
SimpleTable, Size, StateContext, Status, TempTable, TextTable, TmpComponent,
|
||||
};
|
||||
|
||||
/// An [`Element`] is an instantiated [`Component`].
|
||||
@ -19,5 +19,6 @@ where
|
||||
Shortcut(Shortcut<Message, C>),
|
||||
TextTable(TextTable<Message>),
|
||||
Empty,
|
||||
SimpleTable(SimpleTable<Message>),
|
||||
TempTable(TempTable<Message>),
|
||||
}
|
||||
|
@ -41,27 +41,22 @@ where
|
||||
if let Ok(event) = receiver.recv() {
|
||||
match event {
|
||||
RuntimeEvent::UserInterface(event) => {
|
||||
match on_event(
|
||||
if on_event(
|
||||
&mut application,
|
||||
&mut user_interface,
|
||||
&mut app_data,
|
||||
&mut layout,
|
||||
event,
|
||||
) {
|
||||
Status::Captured => {
|
||||
// Hmm... is this really needed? Or is it fine to redraw once then do the termination check?
|
||||
if application.is_terminated() {
|
||||
break;
|
||||
}
|
||||
|
||||
user_interface = new_user_interface(&mut application, &mut app_data);
|
||||
draw(&mut user_interface, terminal, &mut app_data, &mut layout)?;
|
||||
}
|
||||
Status::Ignored => {}
|
||||
user_interface = new_user_interface(&mut application, &mut app_data);
|
||||
draw(&mut user_interface, terminal, &mut app_data, &mut layout)?;
|
||||
}
|
||||
}
|
||||
RuntimeEvent::Custom(message) => {
|
||||
application.update(message);
|
||||
if application.update(message) {
|
||||
user_interface = new_user_interface(&mut application, &mut app_data);
|
||||
draw(&mut user_interface, terminal, &mut app_data, &mut layout)?;
|
||||
}
|
||||
}
|
||||
RuntimeEvent::Resize {
|
||||
width: _,
|
||||
@ -86,7 +81,7 @@ where
|
||||
fn on_event<A>(
|
||||
application: &mut A, user_interface: &mut Element<A::Message>, app_data: &mut AppData,
|
||||
layout: &mut LayoutNode, event: Event,
|
||||
) -> Status
|
||||
) -> bool
|
||||
where
|
||||
A: Application + 'static,
|
||||
{
|
||||
@ -103,12 +98,18 @@ where
|
||||
Status::Ignored => application.global_event_handler(event, &mut messages),
|
||||
};
|
||||
|
||||
let mut should_redraw = match event_handled {
|
||||
Status::Captured => true,
|
||||
Status::Ignored => false,
|
||||
};
|
||||
|
||||
for msg in messages {
|
||||
debug!("Message: {:?}", msg); // FIXME: Remove this debug line!
|
||||
application.update(msg);
|
||||
let msg_result = application.update(msg);
|
||||
should_redraw = should_redraw || msg_result;
|
||||
}
|
||||
|
||||
event_handled
|
||||
should_redraw
|
||||
}
|
||||
|
||||
/// Creates a new [`Element`] representing the root of the user interface.
|
||||
|
Loading…
x
Reference in New Issue
Block a user