Skip to content

Commit

Permalink
feat: initial rendering of the editor tab
Browse files Browse the repository at this point in the history
  • Loading branch information
wllfaria committed May 4, 2024
1 parent 67c0f70 commit 8463169
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 53 deletions.
38 changes: 33 additions & 5 deletions tui/src/components/api_explorer/api_explorer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ use reqtui::{
use std::{collections::HashMap, ops::Add};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};

use super::req_editor::{ReqEditor, ReqEditorState, ReqEditorTabs};

#[derive(Debug, PartialEq)]
pub struct ExplorerLayout {
pub sidebar: Rect,
Expand All @@ -41,6 +43,7 @@ enum PaneFocus {
Sidebar,
ReqUri,
Preview,
Editor,
}

#[derive(Debug)]
Expand All @@ -59,6 +62,8 @@ pub struct ApiExplorer<'a> {
preview_tab: ResViewerTabs,
raw_preview_scroll: usize,

editor_tab: ReqEditorTabs,

responses_map: HashMap<Request, ReqtuiResponse>,
}

Expand Down Expand Up @@ -98,6 +103,8 @@ impl<'a> ApiExplorer<'a> {
preview_tab: ResViewerTabs::Preview,
raw_preview_scroll: 0,

editor_tab: ReqEditorTabs::Request,

response_rx,
request_tx,
}
Expand Down Expand Up @@ -189,7 +196,7 @@ impl<'a> ApiExplorer<'a> {
ReqUri::new(self.colors).render(self.layout.req_uri, frame.buffer_mut(), &mut state);
}

fn draw_response_viewer(&mut self, frame: &mut Frame) {
fn draw_res_viewer(&mut self, frame: &mut Frame) {
let current_response = self
.selected_request
.as_ref()
Expand All @@ -213,6 +220,25 @@ impl<'a> ApiExplorer<'a> {
)
}

fn draw_req_editor(&mut self, frame: &mut Frame) {
let current_request = self.selected_request.as_ref();

let mut state = ReqEditorState::new(
self.focused_pane.eq(&PaneFocus::Editor),
self.selected_pane
.as_ref()
.map(|sel| sel.eq(&PaneFocus::Editor))
.unwrap_or(false),
&self.editor_tab,
);

frame.render_stateful_widget(
ReqEditor::new(self.colors),
self.layout.req_editor,
&mut state,
)
}

fn drain_response_rx(&mut self) {
while let Ok(ReqtuiNetRequest::Response(res)) = self.response_rx.try_recv() {
if let Some(ref req) = self.selected_request {
Expand Down Expand Up @@ -245,9 +271,10 @@ impl Component for ApiExplorer<'_> {

self.drain_response_rx();

self.draw_response_viewer(frame);
self.draw_sidebar(frame);
self.draw_res_viewer(frame);
self.draw_req_editor(frame);
self.draw_req_uri(frame);
self.draw_sidebar(frame);

Ok(())
}
Expand All @@ -260,9 +287,9 @@ impl Component for ApiExplorer<'_> {
if let KeyCode::Tab = key_event.code {
match (&self.focused_pane, &self.selected_pane) {
(PaneFocus::Sidebar, None) => self.focused_pane = PaneFocus::ReqUri,
(PaneFocus::ReqUri, None) => self.focused_pane = PaneFocus::Preview,
(PaneFocus::ReqUri, None) => self.focused_pane = PaneFocus::Editor,
(PaneFocus::Editor, None) => self.focused_pane = PaneFocus::Preview,
(PaneFocus::Preview, None) => self.focused_pane = PaneFocus::Sidebar,

(PaneFocus::Preview, Some(_)) => {
self.handle_preview_key_event(key_event)?;
}
Expand All @@ -275,6 +302,7 @@ impl Component for ApiExplorer<'_> {
PaneFocus::Sidebar => self.handle_sidebar_key_event(key_event),
PaneFocus::ReqUri => self.handle_req_uri_key_event(key_event),
PaneFocus::Preview => self.handle_preview_key_event(key_event),
PaneFocus::Editor => todo!(),
}
}
}
Expand Down
167 changes: 119 additions & 48 deletions tui/src/components/api_explorer/req_editor.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
use crate::components::Component;

use ratatui::{
layout::Rect,
style::{Color, Style, Stylize},
symbols,
widgets::{Block, BorderType, Borders, Tabs},
Frame,
buffer::Buffer,
layout::{Constraint, Direction, Layout, Rect},
style::Style,
widgets::{Block, Borders, StatefulWidget, Tabs, Widget},
};
use std::fmt::Display;
use std::{fmt::Display, ops::Add};

#[derive(Default)]
enum ReqEditorTabs {
#[derive(Debug, Default, Clone)]
pub enum ReqEditorTabs {
#[default]
Request,
Headers,
Cookies,
Query,
Auth,
}

pub struct ReqEditorLayout {
tabs_pane: Rect,
content_pane: Rect,
}

impl From<&ReqEditorTabs> for usize {
fn from(value: &ReqEditorTabs) -> Self {
impl From<ReqEditorTabs> for usize {
fn from(value: ReqEditorTabs) -> Self {
match value {
ReqEditorTabs::Request => 0,
ReqEditorTabs::Headers => 1,
ReqEditorTabs::Cookies => 2,
ReqEditorTabs::Query => 2,
ReqEditorTabs::Auth => 3,
}
}
}
Expand All @@ -32,7 +36,8 @@ impl Display for ReqEditorTabs {
match self {
ReqEditorTabs::Request => f.write_str("Request"),
ReqEditorTabs::Headers => f.write_str("Headers"),
ReqEditorTabs::Cookies => f.write_str("Cookied"),
ReqEditorTabs::Query => f.write_str("Query"),
ReqEditorTabs::Auth => f.write_str("Auth"),
}
}
}
Expand All @@ -43,47 +48,113 @@ impl AsRef<ReqEditorTabs> for ReqEditorTabs {
}
}

pub struct ReqEditor {
curr_tab: ReqEditorTabs,
tab_selector: Tabs<'static>,
pub struct ReqEditorState<'a> {
is_focused: bool,
is_selected: bool,
curr_tab: &'a ReqEditorTabs,
}

impl Default for ReqEditor {
fn default() -> Self {
Self {
curr_tab: ReqEditorTabs::default(),
tab_selector: make_tab_selector(ReqEditorTabs::default()),
impl<'a> ReqEditorState<'a> {
pub fn new(is_focused: bool, is_selected: bool, curr_tab: &'a ReqEditorTabs) -> Self {
ReqEditorState {
is_focused,
curr_tab,
is_selected,
}
}
}

impl Component for ReqEditor {
fn draw(&mut self, frame: &mut Frame, size: Rect) -> anyhow::Result<()> {
frame.render_widget(&self.tab_selector, size);
match self.curr_tab {
// TODO: we should actually render the proper components
ReqEditorTabs::Request => (),
ReqEditorTabs::Headers => (),
ReqEditorTabs::Cookies => (),
pub struct ReqEditor<'a> {
colors: &'a colors::Colors,
}

impl<'a> ReqEditor<'a> {
pub fn new(colors: &'a colors::Colors) -> Self {
Self { colors }
}

fn draw_editor(&self, state: &mut ReqEditorState, buf: &mut Buffer, size: Rect) {}

fn draw_current_tab(&self, state: &mut ReqEditorState, buf: &mut Buffer, size: Rect) {
match state.curr_tab {
ReqEditorTabs::Request => self.draw_editor(state, buf, size),
ReqEditorTabs::Headers => {}
ReqEditorTabs::Query => {}
ReqEditorTabs::Auth => {}
}
Ok(())
}
}

fn make_tab_selector(curr_tab: ReqEditorTabs) -> Tabs<'static> {
Tabs::new([
ReqEditorTabs::Request.to_string(),
ReqEditorTabs::Headers.to_string(),
ReqEditorTabs::Cookies.to_string(),
])
.block(
Block::default()
.title(curr_tab.to_string())
fn draw_tabs(&self, buf: &mut Buffer, state: &ReqEditorState, size: Rect) {
let tabs = Tabs::new(["Request", "Headers", "Query", "Auth"])
.style(Style::default().fg(self.colors.primary.hover))
.select(state.curr_tab.clone().into())
.highlight_style(
Style::default()
.fg(self.colors.bright.magenta)
.bg(self.colors.primary.hover),
);
tabs.render(size, buf);
}

fn draw_container(&self, size: Rect, buf: &mut Buffer, state: &mut ReqEditorState) {
let block_border = match (state.is_focused, state.is_selected) {
(true, false) => Style::default().fg(self.colors.bright.magenta),
(true, true) => Style::default().fg(self.colors.bright.yellow),
(_, _) => Style::default().fg(self.colors.primary.hover),
};

let block = Block::default()
.borders(Borders::ALL)
.border_style(Style::default().gray().dim())
.border_type(BorderType::Rounded),
)
.highlight_style(Style::default().fg(Color::Rgb(255, 0, 0)))
.select(curr_tab.as_ref().into())
.divider(symbols::DOT)
.border_style(block_border);

block.render(size, buf);
}
}

impl<'a> StatefulWidget for ReqEditor<'a> {
type State = ReqEditorState<'a>;

fn render(self, size: Rect, buf: &mut Buffer, state: &mut Self::State) {
let layout = build_layout(size);

self.draw_container(size, buf, state);
self.draw_tabs(buf, state, layout.tabs_pane);
self.draw_current_tab(state, buf, layout.content_pane);
}
}

fn build_layout(size: Rect) -> ReqEditorLayout {
let size = Rect::new(
size.x.add(1),
size.y.add(1),
size.width.saturating_sub(2),
size.height.saturating_sub(2),
);

let [tabs_pane, _, content_pane] = Layout::default()
.constraints([
Constraint::Length(1),
Constraint::Length(1),
Constraint::Fill(1),
])
.direction(Direction::Vertical)
.areas(size);

ReqEditorLayout {
tabs_pane,
content_pane,
}
}

fn build_preview_layout(size: Rect) -> [Rect; 2] {
let [request_pane, _, scrollbar_pane] = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Fill(1),
Constraint::Length(1),
Constraint::Length(1),
])
.areas(size);

[request_pane, scrollbar_pane]
}

0 comments on commit 8463169

Please sign in to comment.