Skip to content

Commit 40d9edc

Browse files
committed
add status bar component plus some status messages
1 parent 0b48057 commit 40d9edc

14 files changed

Lines changed: 276 additions & 44 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
@@ -59,6 +59,16 @@ pub enum View {
5959
Workloads,
6060
Files,
6161
}
62+
63+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, strum::Display)]
64+
pub enum StatusMsg {
65+
#[allow(dead_code)]
66+
Progress(String, usize),
67+
Info(String),
68+
#[allow(dead_code)]
69+
Warning(String),
70+
}
71+
6272
#[derive(Debug, PartialEq)]
6373
pub enum Msg {
6474
AppClose,
@@ -72,6 +82,7 @@ pub enum Msg {
7282
Info(String),
7383
Cscs(CscsMsg),
7484
Job(JobMsg),
85+
Status(StatusMsg),
7586
ChangeView(View),
7687
CreateEvent(UserEvent),
7788
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 => {
@@ -408,9 +409,10 @@ where
408409
None
409410
}
410411
Msg::Cscs(CscsMsg::SystemSelected(system)) => {
412+
let event_tx = self.user_event_tx.clone();
411413
let error_tx = self.error_tx.clone();
412414
tokio::spawn(async move {
413-
match cscs_system_set(system, true).await {
415+
match cscs_system_set(system.clone(), true).await {
414416
Ok(_) => {}
415417
Err(e) => error_tx
416418
.send(format!(
@@ -420,6 +422,10 @@ where
420422
.await
421423
.unwrap(),
422424
};
425+
event_tx
426+
.send(UserEvent::Cscs(CscsEvent::SystemSelected(system)))
427+
.await
428+
.unwrap();
423429
});
424430
None
425431
}
@@ -441,6 +447,18 @@ where
441447
});
442448
None
443449
}
450+
Msg::Status(status) => {
451+
let event_tx = self.user_event_tx.clone();
452+
let event = match status {
453+
StatusMsg::Progress(msg, progress) => StatusEvent::Progress(msg, progress),
454+
StatusMsg::Info(msg) => StatusEvent::Info(msg),
455+
StatusMsg::Warning(msg) => StatusEvent::Warning(msg),
456+
};
457+
tokio::spawn(async move {
458+
event_tx.send(UserEvent::Status(event)).await.unwrap();
459+
});
460+
None
461+
}
444462
Msg::None => None,
445463
}
446464
} 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
@@ -139,10 +139,6 @@ pub struct Cli {
139139
#[arg(short, long, value_name = "FLOAT", default_value_t = 4.0)]
140140
pub tick_rate: f64,
141141

142-
/// Frame rate, i.e. number of frames per second
143-
#[arg(short, long, value_name = "FLOAT", default_value_t = 60.0)]
144-
pub frame_rate: f64,
145-
146142
#[command(subcommand)]
147143
pub command: Option<CliCommands>,
148144
}

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, tab: switch view, ?: 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/cscs/api_client.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ impl CscsApi {
388388
) -> Result<()> {
389389
let workingdir = script_path.clone();
390390
let workingdir = workingdir.parent();
391-
let result = post_compute_system_job(
391+
let _result = post_compute_system_job(
392392
&self.client,
393393
system_name,
394394
account,
@@ -399,7 +399,6 @@ impl CscsApi {
399399
envvars,
400400
)
401401
.await?;
402-
let _ = trace_dbg!(result);
403402

404403
Ok(())
405404
}
@@ -523,7 +522,6 @@ impl CscsApi {
523522
let result = get_filesystem_ops_ls(&self.client, system_name, path)
524523
.await
525524
.wrap_err("couldn't list path")?;
526-
let result = trace_dbg!(result);
527525
match result.output {
528526
Some(entries) => Ok(entries.into_iter().map(|e| e.into()).collect()),
529527
None => Ok(vec![]),

0 commit comments

Comments
 (0)