Skip to content

Commit 9883653

Browse files
committed
add status bar component plus some status messages
1 parent 32d7d94 commit 9883653

15 files changed

Lines changed: 279 additions & 52 deletions

File tree

coman/src/app/ids.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
22
pub enum Id {
3+
StatusBar,
34
Toolbar,
45
WorkloadList,
56
WorkloadLogs,

coman/src/app/messages.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ pub enum View {
5858
Workloads,
5959
Files,
6060
}
61+
62+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, strum::Display)]
63+
pub enum StatusMsg {
64+
#[allow(dead_code)]
65+
Progress(String, usize),
66+
Info(String),
67+
#[allow(dead_code)]
68+
Warning(String),
69+
}
70+
6171
#[derive(Debug, PartialEq)]
6272
pub enum Msg {
6373
AppClose,
@@ -71,6 +81,7 @@ pub enum Msg {
7181
Info(String),
7282
Cscs(CscsMsg),
7383
Job(JobMsg),
84+
Status(StatusMsg),
7485
ChangeView(View),
7586
CreateEvent(UserEvent),
7687
None,

coman/src/app/model.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ use crate::{
1414
app::{
1515
ids::Id,
1616
messages::{
17-
CscsMsg, DownloadPopupMsg, ErrorPopupMsg, InfoPopupMsg, JobMsg, LoginPopupMsg, MenuMsg, Msg,
17+
CscsMsg, DownloadPopupMsg, ErrorPopupMsg, InfoPopupMsg, JobMsg, LoginPopupMsg, MenuMsg, Msg, StatusMsg,
1818
SystemSelectMsg, View,
1919
},
20-
user_events::{CscsEvent, UserEvent},
20+
user_events::{CscsEvent, StatusEvent, UserEvent},
2121
},
2222
components::{
2323
context_menu::ContextMenu, download_popup::DownloadTargetInput, error_popup::ErrorPopup, info_popup::InfoPopup,
@@ -104,17 +104,19 @@ where
104104
.margin(1)
105105
.constraints(
106106
[
107+
Constraint::Max(3), //Statusbar
107108
Constraint::Min(10), //content
108109
Constraint::Max(1), //Toolbar
109110
]
110111
.as_ref(),
111112
)
112113
.split(f.area());
114+
app.view(&Id::StatusBar, f, chunks[0]);
113115
match current_view {
114-
View::Workloads => Self::view_workloads(app, f, chunks[0]),
115-
View::Files => Self::view_files(app, f, chunks[0]),
116+
View::Workloads => Self::view_workloads(app, f, chunks[1]),
117+
View::Files => Self::view_files(app, f, chunks[1]),
116118
}
117-
app.view(&Id::Toolbar, f, chunks[1]);
119+
app.view(&Id::Toolbar, f, chunks[2]);
118120

119121
if app.mounted(&Id::Menu) {
120122
let popup = draw_area_in_absolute(f.area(), 10);
@@ -188,7 +190,6 @@ where
188190
.is_ok()
189191
);
190192
assert!(self.app.active(&Id::SystemSelectPopup).is_ok());
191-
trace_dbg!("mounted system select popup");
192193
None
193194
}
194195
SystemSelectMsg::Closed => {
@@ -401,9 +402,10 @@ where
401402
None
402403
}
403404
Msg::Cscs(CscsMsg::SystemSelected(system)) => {
405+
let event_tx = self.user_event_tx.clone();
404406
let error_tx = self.error_tx.clone();
405407
tokio::spawn(async move {
406-
match cscs_system_set(system, true).await {
408+
match cscs_system_set(system.clone(), true).await {
407409
Ok(_) => {}
408410
Err(e) => error_tx
409411
.send(format!(
@@ -413,6 +415,10 @@ where
413415
.await
414416
.unwrap(),
415417
};
418+
event_tx
419+
.send(UserEvent::Cscs(CscsEvent::SystemSelected(system)))
420+
.await
421+
.unwrap();
416422
});
417423
None
418424
}
@@ -434,6 +440,18 @@ where
434440
});
435441
None
436442
}
443+
Msg::Status(status) => {
444+
let event_tx = self.user_event_tx.clone();
445+
let event = match status {
446+
StatusMsg::Progress(msg, progress) => StatusEvent::Progress(msg, progress),
447+
StatusMsg::Info(msg) => StatusEvent::Info(msg),
448+
StatusMsg::Warning(msg) => StatusEvent::Warning(msg),
449+
};
450+
tokio::spawn(async move {
451+
event_tx.send(UserEvent::Status(event)).await.unwrap();
452+
});
453+
None
454+
}
437455
Msg::None => None,
438456
}
439457
} else {

coman/src/app/user_events.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub enum CscsEvent {
99
GotWorkloadData(Vec<Job>),
1010
GotJobLog(String),
1111
SelectSystemList(Vec<System>),
12+
SystemSelected(String),
1213
}
1314

1415
#[derive(Debug, Eq, Clone, PartialEq, PartialOrd, Ord)]
@@ -17,12 +18,21 @@ pub enum FileEvent {
1718
DownloadCurrentFile,
1819
DownloadSuccessful,
1920
}
21+
22+
#[derive(Debug, Eq, Clone, PartialEq, PartialOrd, Ord)]
23+
pub enum StatusEvent {
24+
Progress(String, usize),
25+
Info(String),
26+
Warning(String),
27+
}
28+
2029
#[derive(Debug, Eq, Clone, PartialOrd, Ord)]
2130
pub enum UserEvent {
2231
Cscs(CscsEvent),
2332
File(FileEvent),
2433
Error(String),
2534
Info(String),
35+
Status(StatusEvent),
2636
SwitchedToView(View),
2737
}
2838

coman/src/cli.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,6 @@ pub struct Cli {
135135
#[arg(short, long, value_name = "FLOAT", default_value_t = 4.0)]
136136
pub tick_rate: f64,
137137

138-
/// Frame rate, i.e. number of frames per second
139-
#[arg(short, long, value_name = "FLOAT", default_value_t = 60.0)]
140-
pub frame_rate: f64,
141-
142138
#[command(subcommand)]
143139
pub command: Option<CliCommands>,
144140
}

coman/src/components/global_listener.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use tuirealm::{
55
};
66

77
use crate::app::{
8-
messages::{InfoPopupMsg, MenuMsg, Msg, SystemSelectMsg, View},
8+
messages::{MenuMsg, Msg, StatusMsg, SystemSelectMsg, View},
99
user_events::{CscsEvent, FileEvent, UserEvent},
1010
};
1111

@@ -42,13 +42,15 @@ impl Component<Msg, UserEvent> for GlobalListener {
4242
}
4343
Event::User(UserEvent::Error(msg)) => Some(Msg::Error(msg)),
4444
Event::User(UserEvent::Info(msg)) => Some(Msg::Info(msg)),
45-
Event::User(UserEvent::Cscs(CscsEvent::LoggedIn)) => Some(Msg::Info("Successfully logged in".to_string())),
45+
Event::User(UserEvent::Cscs(CscsEvent::LoggedIn)) => {
46+
Some(Msg::Status(StatusMsg::Info("Successfully logged in".to_string())))
47+
}
4648
Event::User(UserEvent::Cscs(CscsEvent::SelectSystemList(systems))) => {
4749
Some(Msg::SystemSelectPopup(SystemSelectMsg::Opened(systems)))
4850
}
49-
Event::User(UserEvent::File(FileEvent::DownloadSuccessful)) => Some(Msg::InfoPopup(InfoPopupMsg::Opened(
50-
"File successfully downloaded".to_owned(),
51-
))),
51+
Event::User(UserEvent::File(FileEvent::DownloadSuccessful)) => {
52+
Some(Msg::Status(StatusMsg::Info("File successfully downloaded".to_owned())))
53+
}
5254
_ => None,
5355
}
5456
}

coman/src/components/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub(crate) mod file_tree;
55
pub(crate) mod global_listener;
66
pub(crate) mod info_popup;
77
pub(crate) mod login_popup;
8+
pub(crate) mod status_bar;
89
pub(crate) mod system_select_popup;
910
pub(crate) mod toolbar;
1011
pub(crate) mod workload_list;

coman/src/components/status_bar.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use std::time::{Duration, Instant};
2+
3+
use ratatui::{
4+
text::{Line, Span},
5+
widgets::{LineGauge, Paragraph},
6+
};
7+
use tuirealm::{
8+
AttrValue, Attribute, Component, Event, MockComponent, Props, State,
9+
command::CmdResult,
10+
props::{BorderType, Borders, Layout},
11+
ratatui::{
12+
Frame,
13+
layout::{Constraint, Direction},
14+
prelude::Rect,
15+
style::{Color, Style},
16+
widgets::Block,
17+
},
18+
};
19+
20+
use crate::{
21+
app::{
22+
messages::Msg,
23+
user_events::{CscsEvent, StatusEvent, UserEvent},
24+
},
25+
config::Config,
26+
};
27+
28+
pub struct StatusBar {
29+
props: Props,
30+
last_updated: Instant,
31+
current_status: Option<StatusEvent>,
32+
status_clear_time: Duration,
33+
current_platform: String,
34+
current_system: String,
35+
}
36+
37+
impl StatusBar {
38+
pub fn new() -> Self {
39+
let config = Config::new().unwrap();
40+
Self {
41+
props: Props::default(),
42+
last_updated: Instant::now(),
43+
current_status: None,
44+
status_clear_time: Duration::from_secs(10),
45+
current_platform: config.cscs.current_platform.to_string(),
46+
current_system: config.cscs.current_system,
47+
}
48+
}
49+
}
50+
51+
impl MockComponent for StatusBar {
52+
fn query(&self, attr: Attribute) -> Option<AttrValue> {
53+
self.props.get(attr)
54+
}
55+
56+
fn attr(&mut self, attr: Attribute, value: AttrValue) {
57+
self.props.set(attr, value);
58+
}
59+
60+
fn state(&self) -> State {
61+
State::None
62+
}
63+
64+
fn perform(&mut self, _cmd: tuirealm::command::Cmd) -> CmdResult {
65+
CmdResult::None
66+
}
67+
fn view(&mut self, frame: &mut Frame, area: Rect) {
68+
if self.props.get_or(Attribute::Display, AttrValue::Flag(true)) == AttrValue::Flag(true) {
69+
let borders = Borders::default().modifiers(BorderType::Rounded);
70+
let div = Block::default()
71+
.borders(borders.sides)
72+
.border_style(borders.style())
73+
.border_type(borders.modifiers);
74+
let layout = Layout::default()
75+
.constraints(&[Constraint::Percentage(30), Constraint::Percentage(70)])
76+
.direction(Direction::Horizontal)
77+
.margin(1);
78+
frame.render_widget(div, area);
79+
80+
let highlight_style = Style::default().fg(Color::Yellow);
81+
let info_style = Style::default().fg(Color::Blue);
82+
let warn_style = Style::default().fg(Color::Red);
83+
let system_status = Paragraph::new(Line::from(vec![
84+
Span::styled("Platform: ", highlight_style),
85+
Span::raw(self.current_platform.clone().to_uppercase()),
86+
Span::raw(" "),
87+
Span::styled("System: ", highlight_style),
88+
Span::raw(self.current_system.clone()),
89+
]));
90+
let chunks = layout.chunks(area);
91+
frame.render_widget(system_status, chunks[0]);
92+
if let Some(status) = self.current_status.clone() {
93+
match status {
94+
StatusEvent::Progress(msg, progress) => {
95+
let gauge = LineGauge::default()
96+
.filled_style(Style::default().fg(Color::DarkGray))
97+
.label(msg)
98+
.ratio((progress as f64) / 100.0);
99+
frame.render_widget(gauge, chunks[1]);
100+
}
101+
StatusEvent::Info(info) => {
102+
let notification_status = Paragraph::new(info).style(info_style);
103+
frame.render_widget(notification_status, chunks[1]);
104+
}
105+
StatusEvent::Warning(warning) => {
106+
let notification_status = Paragraph::new(warning).style(warn_style);
107+
frame.render_widget(notification_status, chunks[1]);
108+
}
109+
}
110+
}
111+
}
112+
}
113+
}
114+
impl Component<Msg, UserEvent> for StatusBar {
115+
fn on(&mut self, ev: Event<UserEvent>) -> Option<Msg> {
116+
match ev {
117+
Event::Tick => {
118+
if self.last_updated.elapsed() > self.status_clear_time {
119+
self.current_status = None;
120+
}
121+
}
122+
Event::User(UserEvent::Status(status)) => {
123+
self.current_status = Some(status);
124+
self.last_updated = Instant::now();
125+
}
126+
Event::User(UserEvent::Cscs(CscsEvent::SystemSelected(system))) => {
127+
self.current_system = system;
128+
}
129+
_ => {}
130+
}
131+
None
132+
}
133+
}

coman/src/components/toolbar.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
use tui_realm_stdlib::Label;
22
use tuirealm::{AttrValue, Attribute, Component, Event, MockComponent};
33

4-
use crate::{
5-
app::{
6-
messages::{Msg, View},
7-
user_events::UserEvent,
8-
},
9-
trace_dbg,
4+
use crate::app::{
5+
messages::{Msg, View},
6+
user_events::UserEvent,
107
};
118
const WORKLOAD_TOOLTIP: &str = "q: quit, Esc: close/back, l: logs, f: File view, x: menu, ?: help";
129
const FILETREE_TOOLTIP: &str = "q: quit, ↑↓: navigate,←→: collapse/expand, x: menu, ?: help";
@@ -30,7 +27,6 @@ impl Component<Msg, UserEvent> for Toolbar {
3027
fn on(&mut self, ev: tuirealm::Event<UserEvent>) -> Option<Msg> {
3128
match ev {
3229
Event::User(UserEvent::SwitchedToView(view)) => {
33-
let view = trace_dbg!(view);
3430
self.current_view = view;
3531
match self.current_view {
3632
View::Workloads => self.attr(Attribute::Text, AttrValue::String(WORKLOAD_TOOLTIP.to_owned())),

coman/src/components/workload_log.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,9 @@ use tuirealm::{
66
props::{Alignment, BorderType, Borders, Color, PropPayload, PropValue, TextSpan},
77
};
88

9-
use crate::{
10-
app::{
11-
messages::{JobMsg, Msg},
12-
user_events::{CscsEvent, UserEvent},
13-
},
14-
trace_dbg,
9+
use crate::app::{
10+
messages::{JobMsg, Msg},
11+
user_events::{CscsEvent, UserEvent},
1512
};
1613

1714
#[derive(MockComponent)]
@@ -33,8 +30,6 @@ impl Component<Msg, UserEvent> for WorkloadLog {
3330
fn on(&mut self, ev: tuirealm::Event<UserEvent>) -> Option<Msg> {
3431
let _ = match ev {
3532
Event::User(UserEvent::Cscs(CscsEvent::GotJobLog(log))) => {
36-
let _ = trace_dbg!("got log component");
37-
let log = trace_dbg!(log);
3833
self.attr(
3934
Attribute::Text,
4035
AttrValue::Payload(PropPayload::Vec(

0 commit comments

Comments
 (0)