-
-
Notifications
You must be signed in to change notification settings - Fork 163
refac(console): generalize controls widget #427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
67d14fb
refac(console): generalize controls widget
hds d422114
Apply suggestions from code review
hds d51d5b0
ControlDisplay keys Vec to array
hds d4f90e0
Merge remote-tracking branch 'origin/main' into controls-widget-refactor
hds 43f3966
changed universal_controls to a const UNIVERSAL_CONTROLS
hds c2b9603
inline UNIVERSAL_CONTROLS where possible
hds 61a92a7
Fix docs on UNIVERSAL_CONTROLS
hds 831a035
Merge branch 'main' into controls-widget-refactor
hds File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| use crate::view::{self, bold}; | ||
|
|
||
| use ratatui::{ | ||
| layout, | ||
| text::{Span, Spans, Text}, | ||
| widgets::{Paragraph, Widget}, | ||
| }; | ||
|
|
||
| /// A list of controls which are available in all views. | ||
| const UNIVERSAL_CONTROLS: &[ControlDisplay] = &[ | ||
| ControlDisplay { | ||
| action: "toggle pause", | ||
| keys: &[KeyDisplay { | ||
| base: "space", | ||
| utf8: None, | ||
| }], | ||
| }, | ||
| ControlDisplay { | ||
| action: "quit", | ||
| keys: &[KeyDisplay { | ||
| base: "q", | ||
| utf8: None, | ||
| }], | ||
| }, | ||
| ]; | ||
|
|
||
| /// Construct a widget to display the controls available to the user in the | ||
| /// current view. | ||
| pub(crate) struct Controls { | ||
| paragraph: Paragraph<'static>, | ||
| height: u16, | ||
| } | ||
|
|
||
| impl Controls { | ||
| pub(in crate::view) fn new( | ||
| view_controls: &'static [ControlDisplay], | ||
| area: &layout::Rect, | ||
| styles: &view::Styles, | ||
| ) -> Self { | ||
| let mut spans_controls = Vec::with_capacity(view_controls.len() + UNIVERSAL_CONTROLS.len()); | ||
| spans_controls.extend(view_controls.iter().map(|c| c.to_spans(styles))); | ||
| spans_controls.extend(UNIVERSAL_CONTROLS.iter().map(|c| c.to_spans(styles))); | ||
|
|
||
| let mut lines = vec![Spans::from(vec![Span::from("controls: ")])]; | ||
| let mut current_line = lines.last_mut().expect("This vector is never empty"); | ||
| let separator = Span::from(", "); | ||
|
|
||
| let controls_count: usize = spans_controls.len(); | ||
| for (idx, spans) in spans_controls.into_iter().enumerate() { | ||
| // If this is the first item on this line - or first item on the | ||
| // first line, then always include it - even if it goes beyond the | ||
| // line width, not much we can do anyway. | ||
| if idx == 0 || current_line.width() == 0 { | ||
| current_line.0.extend(spans.0); | ||
| continue; | ||
| } | ||
|
|
||
| // Include the width of our separator in the current item if we | ||
| // aren't placing the last item. This is the separator after the | ||
| // new element. | ||
| let needed_trailing_separator_width = if idx == controls_count + 1 { | ||
| separator.width() | ||
| } else { | ||
| 0 | ||
| }; | ||
|
|
||
| let total_width = current_line.width() | ||
| + separator.width() | ||
| + spans.width() | ||
| + needed_trailing_separator_width; | ||
|
|
||
| // If the current item fits on this line, append it. | ||
| // Otherwise, append only the separator - we accounted for its | ||
| // width in the previous loop iteration - and then create a new | ||
| // line for the current item. | ||
| if total_width <= area.width as usize { | ||
| current_line.0.push(separator.clone()); | ||
| current_line.0.extend(spans.0); | ||
| } else { | ||
hds marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| current_line.0.push(separator.clone()); | ||
| lines.push(spans); | ||
| current_line = lines.last_mut().expect("This vector is never empty"); | ||
| } | ||
| } | ||
|
|
||
| let height = lines.len() as u16; | ||
| let text = Text::from(lines); | ||
|
|
||
| Self { | ||
| paragraph: Paragraph::new(text), | ||
| height, | ||
| } | ||
| } | ||
|
|
||
| pub(crate) fn height(&self) -> u16 { | ||
| self.height | ||
| } | ||
|
|
||
| pub(crate) fn into_widget(self) -> impl Widget { | ||
| self.paragraph | ||
| } | ||
| } | ||
|
|
||
| /// Construct span to display a control. | ||
| /// | ||
| /// A control is made up of an action and one or more keys that will trigger | ||
| /// that action. | ||
| #[derive(Clone)] | ||
| pub(crate) struct ControlDisplay { | ||
| pub(crate) action: &'static str, | ||
| pub(crate) keys: &'static [KeyDisplay], | ||
| } | ||
|
|
||
| /// A key or keys which will be displayed to the user as part of spans | ||
| /// constructed by `ControlDisplay`. | ||
| /// | ||
| /// The `base` description of the key should be ASCII only, more advanced | ||
| /// descriptions can be supplied for that key in the `utf8` field. This | ||
| /// allows the application to pick the best one to display at runtime | ||
| /// based on the termainal being used. | ||
| #[derive(Clone)] | ||
| pub(crate) struct KeyDisplay { | ||
| pub(crate) base: &'static str, | ||
| pub(crate) utf8: Option<&'static str>, | ||
| } | ||
|
|
||
| impl ControlDisplay { | ||
| pub(crate) fn to_spans(&self, styles: &view::Styles) -> Spans<'static> { | ||
| let mut spans = Vec::new(); | ||
|
|
||
| spans.push(Span::from(self.action)); | ||
| spans.push(Span::from(" = ")); | ||
| for (idx, key_display) in self.keys.iter().enumerate() { | ||
| if idx > 0 { | ||
| spans.push(Span::from(" or ")) | ||
| } | ||
| spans.push(bold(match key_display.utf8 { | ||
| Some(utf8) => styles.if_utf8(utf8, key_display.base), | ||
| None => key_display.base, | ||
| })); | ||
| } | ||
|
|
||
| Spans::from(spans) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.