Skip to content

Commit

Permalink
rename the ScreenHandler struct to Ui
Browse files Browse the repository at this point in the history
  • Loading branch information
Việt Trọng Dương authored Sep 22, 2024
1 parent 0facf0b commit 45db014
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 152 deletions.
8 changes: 4 additions & 4 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
},
tui::Tui,
ui::{
screens::ScreenHandler,
Ui,
UiRunState,
},
utils::UnboundedChannel,
Expand Down Expand Up @@ -61,8 +61,8 @@ pub struct App {
#[serde(skip)]
run_state: AppRunState,

/// Screen handler.
screen_handler: ScreenHandler,
/// UI of the app.
screen_handler: Ui,

/// [`Event`] channel. The sender of this channel is cloned for screens to
/// send their own events to the app.
Expand All @@ -74,7 +74,7 @@ impl Default for App {
let event_channel = UnboundedChannel::new();
Self::new(
AppRunState::default(),
ScreenHandler::new(event_channel.get_sender().clone()),
Ui::new(event_channel.get_sender().clone()),
event_channel,
)
}
Expand Down
137 changes: 136 additions & 1 deletion src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
//! User interface structures in Terminal Arcade.
use color_eyre::eyre::eyre;
use ratatui::Frame;
use serde::{
Deserialize,
Serialize,
};
use tokio::sync::mpsc::UnboundedSender;

use crate::events::InputEvent;
use crate::{
events::{
Event,
InputEvent,
ScreenEvent,
},
ui::screens::{
Screen,
ScreenHandle,
},
};

pub mod screens;
pub mod widgets;
Expand Down Expand Up @@ -39,3 +52,125 @@ pub trait HandleEvent {
Ok(())
}
}

/// The UI of the app. This struct handles the screens
#[derive(Debug, Serialize)]
pub struct Ui {
/// Running state.
#[serde(skip)]
run_state: UiRunState,

/// Screens that this UI handles.
/// The top most screen (last element) is named the "active" screen.
/// It gets to render and receive events.
screens: Vec<ScreenHandle>,

/// Event channel.
#[serde(skip)]
event_sender: UnboundedSender<Event>,
}

impl Ui {
/// Constructs an empty UI.
pub fn new(event_sender: UnboundedSender<Event>) -> Self {
Self {
run_state: UiRunState::Running,
screens: Vec::new(),
event_sender,
}
}

/// Renders the screens this UI is holding to the terminal.
/// If there are no active screens, nothing is rendered.
pub fn render(&mut self, frame: &mut Frame) -> crate::Result<()> {
if let Some(handle) = self.get_mut_active_screen() {
handle.render(frame)?;
}
Ok(())
}

/// Updates the UI.
///
/// This method returns the screen that was closed, if there was one.
/// Note that this method closes at most one screen every time it is called.
///
/// [running]: UiRunState::Running
/// [closing]: UiRunState::Closing
/// [finished]: UiRunState::Finished
#[allow(clippy::unwrap_used, reason = "infallible")]
pub fn update(&mut self) -> crate::Result<Option<ScreenHandle>> {
if self.is_empty() {
self.run_state = UiRunState::Finished;
return Ok(None);
}
let handler_run_state = self.run_state;
let active_screen = self.get_mut_active_screen().unwrap();
match (handler_run_state, active_screen.state.run_state) {
(UiRunState::Finished, _) => {},
(_, UiRunState::Finished) => return Ok(self.pop_active_screen()),
(_, UiRunState::Closing) => todo!(),
(UiRunState::Running, UiRunState::Running) => todo!(),
(UiRunState::Closing, UiRunState::Running) => {
self.event_sender.send(Event::Screen(ScreenEvent::Close))?;
},
}
Ok(None)
}

/// Handles an incoming [`Event`].
pub fn event(&mut self, event: &Event) -> crate::Result<()> {
if let Some(screen) = self.get_mut_active_screen() {
screen.event(event)
} else {
Err(eyre!("no screens left in stack to receive events"))
}
}

/// Sets the [run state](Self::run_state) to
/// [`Closing`](UiRunState::Closing).
pub fn close(&mut self) {
self.run_state = UiRunState::Closing;
}

/// Performs actions to quit. (clears all of its screens, etc.)
pub fn quit(&mut self) {
self.clear_screens();
self.run_state = UiRunState::Finished;
}

/// Returns the [run state](UiRunState) of the UI.
pub fn get_run_state(&self) -> UiRunState {
self.run_state
}

/// Checks if the UI doesn't have any more screens.
pub fn is_empty(&self) -> bool {
self.screens.is_empty()
}

/// Clears all screens from this UI.
pub fn clear_screens(&mut self) {
self.screens.clear();
}

/// Gets the current active screen (the final one on the
/// [stack](Self::stack)) mutably.
pub fn get_mut_active_screen(&mut self) -> Option<&mut ScreenHandle> {
self.screens.last_mut()
}

/// Creates a new screen as active and clones the UI's own sender for
/// the new screen's use.
pub fn push_active_screen<S>(&mut self, screen: S)
where
S: Screen + 'static,
{
self.screens
.push(ScreenHandle::new(screen, self.event_sender.clone()));
}

/// Pops the active screen, returning an error if there is none left.
pub fn pop_active_screen(&mut self) -> Option<ScreenHandle> {
self.screens.pop()
}
}
2 changes: 1 addition & 1 deletion src/ui/screens/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl ScreenHandle {
ScreenEvent::Close => self.state.run_state = UiRunState::Closing,
ScreenEvent::Finish => self.state.run_state = UiRunState::Finished,
ScreenEvent::UpdateTitle(title) => {
self.state.title.clone_from(title)
self.state.title.clone_from(title);
},
}
}
Expand Down
144 changes: 0 additions & 144 deletions src/ui/screens/handler.rs

This file was deleted.

2 changes: 0 additions & 2 deletions src/ui/screens/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ use crate::{
};

pub mod handle;
pub mod handler;
pub mod state;

pub use handle::ScreenHandle;
pub use handler::ScreenHandler;
pub use state::ScreenState;

// FUTURE: When `typetag` supports associated types, switch to an `Either` API
Expand Down

0 comments on commit 45db014

Please sign in to comment.