mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
fix(TUI): Selected column and selected row of table are now preserved when set_items() is called (#3161)
This commit is contained in:
parent
39af7fa202
commit
5c7bc3d8cd
3 changed files with 109 additions and 9 deletions
|
@ -193,6 +193,24 @@ pub struct PeerStats {
|
||||||
pub received_bytes_per_sec: u64,
|
pub received_bytes_per_sec: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for PeerStats {
|
||||||
|
fn eq(&self, other: &PeerStats) -> bool {
|
||||||
|
*self.addr == other.addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for WorkerStats {
|
||||||
|
fn eq(&self, other: &WorkerStats) -> bool {
|
||||||
|
*self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for DiffBlock {
|
||||||
|
fn eq(&self, other: &DiffBlock) -> bool {
|
||||||
|
self.block_height == other.block_height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl StratumStats {
|
impl StratumStats {
|
||||||
/// Calculate network hashrate
|
/// Calculate network hashrate
|
||||||
pub fn network_hashrate(&self, height: u64) -> f64 {
|
pub fn network_hashrate(&self, height: u64) -> f64 {
|
||||||
|
|
|
@ -33,7 +33,7 @@ use crate::tui::table::{TableView, TableViewItem};
|
||||||
use crate::tui::types::TUIStatusListener;
|
use crate::tui::types::TUIStatusListener;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
enum PeerColumn {
|
pub enum PeerColumn {
|
||||||
Address,
|
Address,
|
||||||
State,
|
State,
|
||||||
UsedBandwidth,
|
UsedBandwidth,
|
||||||
|
|
|
@ -145,7 +145,7 @@ where
|
||||||
/// .default_column(BasicColumn::Name);
|
/// .default_column(BasicColumn::Name);
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct TableView<T: TableViewItem<H>, H: Eq + Hash + Copy + Clone + 'static> {
|
pub struct TableView<T: TableViewItem<H> + PartialEq, H: Eq + Hash + Copy + Clone + 'static> {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
scrollbase: ScrollBase,
|
scrollbase: ScrollBase,
|
||||||
last_size: Vec2,
|
last_size: Vec2,
|
||||||
|
@ -165,7 +165,7 @@ pub struct TableView<T: TableViewItem<H>, H: Eq + Hash + Copy + Clone + 'static>
|
||||||
on_select: Option<Rc<dyn Fn(&mut Cursive, usize, usize)>>,
|
on_select: Option<Rc<dyn Fn(&mut Cursive, usize, usize)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: TableViewItem<H>, H: Eq + Hash + Copy + Clone + 'static> TableView<T, H> {
|
impl<T: TableViewItem<H> + PartialEq, H: Eq + Hash + Copy + Clone + 'static> TableView<T, H> {
|
||||||
/// Creates a new empty `TableView` without any columns.
|
/// Creates a new empty `TableView` without any columns.
|
||||||
///
|
///
|
||||||
/// A TableView should be accompanied by a enum of type `H` representing
|
/// A TableView should be accompanied by a enum of type `H` representing
|
||||||
|
@ -233,8 +233,7 @@ impl<T: TableViewItem<H>, H: Eq + Hash + Copy + Clone + 'static> TableView<T, H>
|
||||||
pub fn sort_by(&mut self, column: H, order: Ordering) {
|
pub fn sort_by(&mut self, column: H, order: Ordering) {
|
||||||
if self.column_indices.contains_key(&column) {
|
if self.column_indices.contains_key(&column) {
|
||||||
for c in &mut self.columns {
|
for c in &mut self.columns {
|
||||||
c.selected = c.column == column;
|
if c.column == column {
|
||||||
if c.selected {
|
|
||||||
c.order = order;
|
c.order = order;
|
||||||
} else {
|
} else {
|
||||||
c.order = Ordering::Equal;
|
c.order = Ordering::Equal;
|
||||||
|
@ -437,8 +436,19 @@ impl<T: TableViewItem<H>, H: Eq + Hash + Copy + Clone + 'static> TableView<T, H>
|
||||||
/// Sets the contained items of the table.
|
/// Sets the contained items of the table.
|
||||||
///
|
///
|
||||||
/// The currently active sort order is preserved and will be applied to all
|
/// The currently active sort order is preserved and will be applied to all
|
||||||
/// items.
|
/// items. The selected item will also be preserved.
|
||||||
pub fn set_items(&mut self, items: Vec<T>) {
|
pub fn set_items(&mut self, items: Vec<T>) {
|
||||||
|
let mut new_location = 0;
|
||||||
|
if let Some(old_item_location) = self.item() {
|
||||||
|
let old_item = self.items.get(old_item_location).unwrap();
|
||||||
|
for (i, new_item) in items.iter().enumerate() {
|
||||||
|
if old_item == new_item {
|
||||||
|
new_location = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.items = items;
|
self.items = items;
|
||||||
self.rows_to_items = Vec::with_capacity(self.items.len());
|
self.rows_to_items = Vec::with_capacity(self.items.len());
|
||||||
|
|
||||||
|
@ -453,7 +463,7 @@ impl<T: TableViewItem<H>, H: Eq + Hash + Copy + Clone + 'static> TableView<T, H>
|
||||||
self.scrollbase
|
self.scrollbase
|
||||||
.set_heights(self.last_size.y.saturating_sub(2), self.rows_to_items.len());
|
.set_heights(self.last_size.y.saturating_sub(2), self.rows_to_items.len());
|
||||||
|
|
||||||
self.set_selected_row(0);
|
self.set_selected_item(new_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the contained items of the table.
|
/// Sets the contained items of the table.
|
||||||
|
@ -580,7 +590,7 @@ impl<T: TableViewItem<H>, H: Eq + Hash + Copy + Clone + 'static> TableView<T, H>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: TableViewItem<H>, H: Eq + Hash + Copy + Clone + 'static> TableView<T, H> {
|
impl<T: TableViewItem<H> + PartialEq, H: Eq + Hash + Copy + Clone + 'static> TableView<T, H> {
|
||||||
fn draw_columns<C: Fn(&Printer<'_, '_>, &TableColumn<H>)>(
|
fn draw_columns<C: Fn(&Printer<'_, '_>, &TableColumn<H>)>(
|
||||||
&self,
|
&self,
|
||||||
printer: &Printer<'_, '_>,
|
printer: &Printer<'_, '_>,
|
||||||
|
@ -689,7 +699,7 @@ impl<T: TableViewItem<H>, H: Eq + Hash + Copy + Clone + 'static> TableView<T, H>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: TableViewItem<H> + 'static, H: Eq + Hash + Copy + Clone + 'static> View
|
impl<T: TableViewItem<H> + PartialEq + 'static, H: Eq + Hash + Copy + Clone + 'static> View
|
||||||
for TableView<T, H>
|
for TableView<T, H>
|
||||||
{
|
{
|
||||||
fn draw(&self, printer: &Printer<'_, '_>) {
|
fn draw(&self, printer: &Printer<'_, '_>) {
|
||||||
|
@ -981,3 +991,75 @@ impl<H: Copy + Clone + 'static> TableColumn<H> {
|
||||||
printer.print((0, 0), value.as_str());
|
printer.print((0, 0), value.as_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::tui::peers::PeerColumn;
|
||||||
|
use crate::tui::table::TableView;
|
||||||
|
use chrono::Utc;
|
||||||
|
use grin_core::ser::ProtocolVersion;
|
||||||
|
use grin_servers::PeerStats;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_set_items_preserves_selected_item() {
|
||||||
|
let mut table = TableView::<PeerStats, PeerColumn>::new();
|
||||||
|
let ps1 = PeerStats {
|
||||||
|
addr: "123.0.0.1".to_string(),
|
||||||
|
..TestPeerStats::default()
|
||||||
|
};
|
||||||
|
let ps2 = PeerStats {
|
||||||
|
addr: "123.0.0.2".to_string(),
|
||||||
|
..TestPeerStats::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut items = vec![ps1, ps2];
|
||||||
|
table.set_items(items.clone());
|
||||||
|
assert_eq!(table.item().unwrap(), 0);
|
||||||
|
|
||||||
|
items.reverse();
|
||||||
|
table.set_items(items);
|
||||||
|
assert_eq!(table.item().unwrap(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_set_items_preserves_order() {
|
||||||
|
let mut table = TableView::<PeerStats, PeerColumn>::new();
|
||||||
|
let ps1 = PeerStats {
|
||||||
|
addr: "123.0.0.1".to_string(),
|
||||||
|
received_bytes_per_sec: 10,
|
||||||
|
..TestPeerStats::default()
|
||||||
|
};
|
||||||
|
let ps2 = PeerStats {
|
||||||
|
addr: "123.0.0.2".to_string(),
|
||||||
|
received_bytes_per_sec: 80,
|
||||||
|
..TestPeerStats::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let items = vec![ps1, ps2];
|
||||||
|
table.set_items(items);
|
||||||
|
assert_eq!(table.rows_to_items[0], 0);
|
||||||
|
table.sort_by(PeerColumn::UsedBandwidth, Ordering::Greater);
|
||||||
|
|
||||||
|
assert_eq!(table.rows_to_items[0], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestPeerStats(PeerStats);
|
||||||
|
|
||||||
|
impl TestPeerStats {
|
||||||
|
fn default() -> PeerStats {
|
||||||
|
PeerStats {
|
||||||
|
state: "Connected".to_string(),
|
||||||
|
addr: "127.0.0.1".to_string(),
|
||||||
|
version: ProtocolVersion::local(),
|
||||||
|
user_agent: "".to_string(),
|
||||||
|
total_difficulty: 0,
|
||||||
|
height: 0,
|
||||||
|
direction: "Outbound".to_string(),
|
||||||
|
last_seen: Utc::now(),
|
||||||
|
sent_bytes_per_sec: 0,
|
||||||
|
received_bytes_per_sec: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue