Skip to content

Commit

Permalink
Add scrolling text window for requests/responses
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasPickering committed Nov 2, 2023
1 parent 8b6b667 commit f13af9f
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 66 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Added

- Request and response panes can now be fullscreened
- Request and response panes can now be fullscreened and scrolled [#14](https://github.com/LucasPickering/slumber/issues/14)

### Removed

Expand All @@ -16,7 +16,7 @@

### Fixed

- Initially selected recipe loads most recent response (https://github.com/LucasPickering/slumber/issues/13)
- Initially selected recipe loads most recent response [#13](https://github.com/LucasPickering/slumber/issues/13)

## 0.3.1 - 2023-10-22

Expand Down
15 changes: 9 additions & 6 deletions src/tui/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ impl InputEngine {
},
InputBinding::new(KeyCode::Char('r'), Action::ReloadCollection),
InputBinding::new(KeyCode::F(11), Action::Fullscreen),
InputBinding::new(KeyCode::BackTab, Action::FocusPrevious),
InputBinding::new(KeyCode::Tab, Action::FocusNext),
InputBinding::new(KeyCode::BackTab, Action::PreviousPane),
InputBinding::new(KeyCode::Tab, Action::NextPane),
InputBinding::new(KeyCode::Up, Action::Up),
InputBinding::new(KeyCode::Down, Action::Down),
InputBinding::new(KeyCode::Left, Action::Left),
Expand Down Expand Up @@ -94,16 +94,19 @@ pub enum Action {
/// Reload the request collection from the same file as the initial load
#[display(fmt = "Reload Collection")]
ReloadCollection,
/// Focus the next pane
#[display(fmt = "Next Pane")]
FocusNext,

/// Focus the previous pane
#[display(fmt = "Prev Pane")]
FocusPrevious,
PreviousPane,
/// Focus the next pane
#[display(fmt = "Next Pane")]
NextPane,

Up,
Down,
Left,
Right,

/// Do a thing. E.g. select an item in a list
Submit,
/// Embiggen a pane
Expand Down
2 changes: 1 addition & 1 deletion src/tui/view/component/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ impl Draw<HelpTextProps> for HelpText {

match props.fullscreen_mode {
None => {
actions.extend([Action::FocusNext, Action::FocusPrevious]);
actions.extend([Action::NextPane, Action::PreviousPane]);
// Pane-specific actions
actions.extend(match props.selected_pane {
PrimaryPane::ProfileList => [].as_slice(),
Expand Down
7 changes: 7 additions & 0 deletions src/tui/view/component/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod request;
mod response;
mod root;
mod tabs;
mod text_window;

pub use modal::{IntoModal, Modal, ModalPriority};
pub use root::Root;
Expand Down Expand Up @@ -150,6 +151,12 @@ pub enum Event {
/// to implement custom close triggers.
CloseModal,

/// Propagated from downstream when the user changes changes in a tab
/// selection. Allows parents to react to the tab change. This does not
/// include the new tab value because that would require generices. You can
/// grab the value from the child though.
TabChanged,

/// Tell the user something informational
Notify(Notification),
}
Expand Down
4 changes: 2 additions & 2 deletions src/tui/view/component/primary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,14 @@ impl Component for PrimaryView {

// Input messages
Event::Input {
action: Some(Action::FocusPrevious),
action: Some(Action::PreviousPane),
..
} => {
self.selected_pane.previous();
UpdateOutcome::Consumed
}
Event::Input {
action: Some(Action::FocusNext),
action: Some(Action::NextPane),
..
} => {
self.selected_pane.next();
Expand Down
48 changes: 26 additions & 22 deletions src/tui/view/component/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use crate::{
input::Action,
view::{
component::{
primary::PrimaryPane, root::FullscreenMode, tabs::Tabs,
primary::PrimaryPane,
root::FullscreenMode,
tabs::Tabs,
text_window::{TextWindow, TextWindowProps},
Component, Draw, Event, UpdateContext, UpdateOutcome,
},
state::FixedSelect,
Expand All @@ -16,6 +19,7 @@ use crate::{
use derive_more::Display;
use ratatui::{
prelude::{Constraint, Direction, Rect},
text::Text,
widgets::Paragraph,
};
use strum::EnumIter;
Expand All @@ -25,6 +29,7 @@ use strum::EnumIter;
#[display(fmt = "RequestPane")]
pub struct RequestPane {
tabs: Tabs<Tab>,
text_window: TextWindow,
}

pub struct RequestPaneProps<'a> {
Expand All @@ -51,18 +56,26 @@ impl Component for RequestPane {
event: Event,
) -> UpdateOutcome {
match event {
// Toggle fullscreen
Event::Input {
action: Some(Action::Fullscreen),
..
} => UpdateOutcome::Propagate(Event::ToggleFullscreen(
FullscreenMode::Request,
)),

// Reset content state when tab changes
Event::TabChanged => {
self.text_window.reset();
UpdateOutcome::Consumed
}

_ => UpdateOutcome::Propagate(event),
}
}

fn focused_child(&mut self) -> Option<&mut dyn Component> {
Some(&mut self.tabs)
fn children(&mut self) -> Vec<&mut dyn Component> {
vec![&mut self.tabs, &mut self.text_window]
}
}

Expand Down Expand Up @@ -105,27 +118,18 @@ impl<'a> Draw<RequestPaneProps<'a>> for RequestPane {
self.tabs.draw(context, (), tabs_chunk);

// Request content
match self.tabs.selected() {
let text: Text = match self.tabs.selected() {
Tab::Body => {
if let Some(body) = recipe.body.as_deref() {
context
.frame
.render_widget(Paragraph::new(body), content_chunk);
}
}
Tab::Query => {
context.frame.render_widget(
Paragraph::new(recipe.query.to_tui(context)),
content_chunk,
);
recipe.body.as_deref().map(Text::from).unwrap_or_default()
}
Tab::Headers => {
context.frame.render_widget(
Paragraph::new(recipe.headers.to_tui(context)),
content_chunk,
);
}
}
Tab::Query => recipe.query.to_tui(context),
Tab::Headers => recipe.headers.to_tui(context),
};
self.text_window.draw(
context,
TextWindowProps { text },
content_chunk,
);
}
}
}
53 changes: 28 additions & 25 deletions src/tui/view/component/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ use crate::tui::{
input::Action,
view::{
component::{
primary::PrimaryPane, root::FullscreenMode, tabs::Tabs, Component,
Draw, Event, UpdateContext, UpdateOutcome,
primary::PrimaryPane,
root::FullscreenMode,
tabs::Tabs,
text_window::{TextWindow, TextWindowProps},
Component, Draw, Event, UpdateContext, UpdateOutcome,
},
state::{FixedSelect, RequestState},
util::{layout, BlockBrick, ToTui},
Expand All @@ -13,7 +16,7 @@ use crate::tui::{
use derive_more::Display;
use ratatui::{
prelude::{Alignment, Constraint, Direction, Rect},
text::Line,
text::{Line, Text},
widgets::{Paragraph, Wrap},
};
use strum::EnumIter;
Expand All @@ -24,6 +27,7 @@ use strum::EnumIter;
#[display(fmt = "ResponsePane")]
pub struct ResponsePane {
tabs: Tabs<Tab>,
text_window: TextWindow,
}

pub struct ResponsePaneProps<'a> {
Expand All @@ -49,19 +53,26 @@ impl Component for ResponsePane {
event: Event,
) -> UpdateOutcome {
match event {
// Enter fullscreen
// Toggle fullscreen
Event::Input {
action: Some(Action::Fullscreen),
..
} => UpdateOutcome::Propagate(Event::ToggleFullscreen(
FullscreenMode::Response,
)),

// Reset content state when tab changes
Event::TabChanged => {
self.text_window.reset();
UpdateOutcome::Consumed
}

_ => UpdateOutcome::Propagate(event),
}
}

fn focused_child(&mut self) -> Option<&mut dyn Component> {
Some(&mut self.tabs)
fn children(&mut self) -> Vec<&mut dyn Component> {
vec![&mut self.tabs, &mut self.text_window]
}
}

Expand All @@ -70,7 +81,6 @@ impl<'a> Draw<ResponsePaneProps<'a>> for ResponsePane {
&self,
context: &mut DrawContext,
props: ResponsePaneProps<'a>,

chunk: Rect,
) {
// Render outermost block
Expand Down Expand Up @@ -158,25 +168,18 @@ impl<'a> Draw<ResponsePaneProps<'a>> for ResponsePane {
self.tabs.draw(context, (), tabs_chunk);

// Main content for the response
match self.tabs.selected() {
Tab::Body => {
let body = pretty_body
.as_deref()
.unwrap_or(response.body.text());
context.frame.render_widget(
Paragraph::new(body),
content_chunk,
);
}
Tab::Headers => {
context.frame.render_widget(
Paragraph::new(
response.headers.to_tui(context),
),
content_chunk,
);
}
let text: Text = match self.tabs.selected() {
Tab::Body => pretty_body
.as_deref()
.unwrap_or(response.body.text())
.into(),
Tab::Headers => response.headers.to_tui(context),
};
self.text_window.draw(
context,
TextWindowProps { text },
content_chunk,
);
}

// Sadge
Expand Down
15 changes: 11 additions & 4 deletions src/tui/view/component/tabs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,20 @@ impl<T: Debug + FixedSelect> Component for Tabs<T> {
action: Some(action),
..
} => match action {
// Propagate TabChanged event if appropriate
Action::Left => {
self.tabs.previous();
UpdateOutcome::Consumed
if self.tabs.previous() {
UpdateOutcome::Propagate(Event::TabChanged)
} else {
UpdateOutcome::Consumed
}
}
Action::Right => {
self.tabs.next();
UpdateOutcome::Consumed
if self.tabs.next() {
UpdateOutcome::Propagate(Event::TabChanged)
} else {
UpdateOutcome::Consumed
}
}

_ => UpdateOutcome::Propagate(event),
Expand Down
Loading

0 comments on commit f13af9f

Please sign in to comment.