From 1151fe77c19eb4b59db47f1e30cadef35c0d78f0 Mon Sep 17 00:00:00 2001 From: wiru Date: Thu, 6 Jun 2024 21:53:19 -0300 Subject: [PATCH] fix: fixing resizing crashing when drawing hint --- .../collection_viewer/collection_viewer.rs | 13 +- .../pages/collection_viewer/request_editor.rs | 16 +- .../request_editor/headers_editor.rs | 171 ++++++++++-------- .../headers_editor_delete_prompt.rs | 46 ++--- .../request_editor/headers_editor_form.rs | 70 +++++++ 5 files changed, 206 insertions(+), 110 deletions(-) create mode 100644 hac-client/src/pages/collection_viewer/request_editor/headers_editor_form.rs diff --git a/hac-client/src/pages/collection_viewer/collection_viewer.rs b/hac-client/src/pages/collection_viewer/collection_viewer.rs index 70e98a5..3b420a1 100755 --- a/hac-client/src/pages/collection_viewer/collection_viewer.rs +++ b/hac-client/src/pages/collection_viewer/collection_viewer.rs @@ -46,6 +46,7 @@ pub enum CollectionViewerOverlay { RequestMethod, HeadersHelp, HeadersDelete, + HeadersForm(usize), } #[derive(Debug, Clone, Copy, PartialEq)] @@ -173,7 +174,6 @@ impl<'cv> CollectionViewer<'cv> { config, collection_store.clone(), layout.req_editor, - layout.hint_pane, ), response_viewer: ResponseViewer::new( colors, @@ -677,8 +677,15 @@ impl Renderable for CollectionViewer<'_> { match overlay { CollectionViewerOverlay::CreateRequest => self.draw_create_request_form(frame), CollectionViewerOverlay::RequestMethod => self.draw_request_method_form(frame), - CollectionViewerOverlay::HeadersHelp => self.request_editor.draw_overlay(frame)?, - CollectionViewerOverlay::HeadersDelete => self.request_editor.draw_overlay(frame)?, + CollectionViewerOverlay::HeadersHelp => { + self.request_editor.draw_overlay(frame, overlay)? + } + CollectionViewerOverlay::HeadersDelete => { + self.request_editor.draw_overlay(frame, overlay)? + } + CollectionViewerOverlay::HeadersForm(_) => { + self.request_editor.draw_overlay(frame, overlay)? + } CollectionViewerOverlay::None => {} } diff --git a/hac-client/src/pages/collection_viewer/request_editor.rs b/hac-client/src/pages/collection_viewer/request_editor.rs index 52bef2f..7a6a1b3 100755 --- a/hac-client/src/pages/collection_viewer/request_editor.rs +++ b/hac-client/src/pages/collection_viewer/request_editor.rs @@ -2,6 +2,7 @@ mod auth_editor; mod body_editor; mod headers_editor; mod headers_editor_delete_prompt; +mod headers_editor_form; use auth_editor::AuthEditor; use body_editor::{BodyEditor, BodyEditorEvent}; @@ -27,7 +28,7 @@ use ratatui::style::{Style, Stylize}; use ratatui::widgets::{Block, Borders, Tabs}; use ratatui::Frame; -use super::collection_viewer::PaneFocus; +use super::collection_viewer::{CollectionViewerOverlay, PaneFocus}; /// set of possible events the edtior can send to the parent #[derive(Debug)] @@ -102,7 +103,6 @@ pub struct RequestEditor<'re> { layout: ReqEditorLayout, curr_tab: ReqEditorTabs, - hint_size: Rect, } impl<'re> RequestEditor<'re> { @@ -111,7 +111,6 @@ impl<'re> RequestEditor<'re> { config: &'re hac_config::Config, collection_store: Rc>, size: Rect, - hint_size: Rect, ) -> Self { let curr_tab = collection_store .borrow() @@ -136,13 +135,11 @@ impl<'re> RequestEditor<'re> { colors, collection_store.clone(), layout.content_pane, - hint_size, ), auth_editor: AuthEditor::new(colors), layout, curr_tab, collection_store, - hint_size, } } @@ -158,6 +155,7 @@ impl<'re> RequestEditor<'re> { pub fn resize(&mut self, new_size: Rect) { self.layout = build_layout(new_size); + self.headers_editor.resize(self.layout.content_pane); self.body_editor.resize(self.layout.content_pane); } @@ -218,10 +216,14 @@ impl<'re> RequestEditor<'re> { frame.render_widget(block, size); } - pub fn draw_overlay(&mut self, frame: &mut Frame) -> anyhow::Result<()> { + pub fn draw_overlay( + &mut self, + frame: &mut Frame, + overlay: CollectionViewerOverlay, + ) -> anyhow::Result<()> { match self.curr_tab { ReqEditorTabs::Body => todo!(), - ReqEditorTabs::Headers => self.headers_editor.draw_overlay(frame), + ReqEditorTabs::Headers => self.headers_editor.draw_overlay(frame, overlay), ReqEditorTabs::Query => todo!(), ReqEditorTabs::Auth => todo!(), } diff --git a/hac-client/src/pages/collection_viewer/request_editor/headers_editor.rs b/hac-client/src/pages/collection_viewer/request_editor/headers_editor.rs index b148fbf..77ecb44 100755 --- a/hac-client/src/pages/collection_viewer/request_editor/headers_editor.rs +++ b/hac-client/src/pages/collection_viewer/request_editor/headers_editor.rs @@ -12,12 +12,13 @@ use rand::Rng; use ratatui::layout::{Constraint, Direction, Layout, Rect}; use ratatui::style::{Style, Stylize}; use ratatui::text::Line; -use ratatui::widgets::{Block, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState}; +use ratatui::widgets::{Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState}; use ratatui::Frame; use super::headers_editor_delete_prompt::{ HeadersEditorDeletePrompt, HeadersEditorDeletePromptEvent, }; +use super::headers_editor_form::HeadersEditorForm; #[derive(Debug)] pub enum HeadersEditorEvent { @@ -42,9 +43,10 @@ pub struct HeadersEditor<'he> { row_height: u16, amount_on_view: usize, layout: HeadersEditorLayout, - hint_size: Rect, - delete_prompt: HeadersEditorDeletePrompt<'he>, logo_idx: usize, + + delete_prompt: HeadersEditorDeletePrompt<'he>, + header_form: HeadersEditorForm<'he>, } impl<'he> HeadersEditor<'he> { @@ -52,12 +54,15 @@ impl<'he> HeadersEditor<'he> { colors: &'he hac_colors::colors::Colors, collection_store: Rc>, size: Rect, - hint_size: Rect, ) -> Self { let row_height = 2; let layout = build_layout(size, row_height); let logo_idx = rand::thread_rng().gen_range(0..LOGO_ASCII.len()); + HeadersEditor { + delete_prompt: HeadersEditorDeletePrompt::new(colors), + header_form: HeadersEditorForm::new(colors, collection_store.clone()), + colors, collection_store, scroll: 0, @@ -65,8 +70,6 @@ impl<'he> HeadersEditor<'he> { row_height, amount_on_view: layout.content_size.height.div(row_height).into(), layout, - hint_size, - delete_prompt: HeadersEditorDeletePrompt::new(colors), logo_idx, } } @@ -98,110 +101,117 @@ impl<'he> HeadersEditor<'he> { frame.render_widget(Paragraph::new(checkbox).fg(decor_fg).centered(), row[3]); } + fn get_hint_size(&self, frame: &mut Frame) -> Rect { + let size = frame.size(); + Rect::new(0, size.height.sub(1), size.width, 1) + } + fn draw_hint(&self, frame: &mut Frame) { - let hint = match self.hint_size.width { + let hint_size = self.get_hint_size(frame); + let hint = match hint_size.width { w if w.le(&100) => "[j/k -> move down/up] [enter -> select] [space -> enable/disable] [? -> help]", _ => "[j/k -> move down/up] [enter -> select] [space -> enable/disable] [d -> delete] [? -> help]", }; frame.render_widget( Paragraph::new(hint).fg(self.colors.bright.black).centered(), - self.hint_size, + hint_size, ); } fn draw_help_overlay(&self, frame: &mut Frame) { make_overlay(self.colors, self.colors.normal.black, 0.1, frame); - let size = frame.size(); - let logo = LOGO_ASCII[self.logo_idx]; - - let help_popup = Rect::new( - size.width.div(2).saturating_sub(20), - size.height.div(2).saturating_sub(3), - 40, - 6, - ); - - let logo_size = Rect::new( - size.width - .div(2) - .saturating_sub(logo[0].len().div(2) as u16), - size.y.add(4), - logo[0].len() as u16, - logo.len() as u16, - ); - - let logo = logo - .iter() - .map(|line| Line::from(line.fg(self.colors.normal.red))) - .collect::>(); - - let hint_size = Rect::new( - help_popup.x, - help_popup.y.add(help_popup.height).add(2), - 40, - 1, - ); - - let hint = Line::from("press any key to close this dialog") - .fg(self.colors.bright.black) - .centered(); let lines = [ [ - format!("j{}", " ".repeat(11)), - format!("- move down{}", " ".repeat(29)), + format!("j{}", " ".repeat(11)).fg(self.colors.normal.red), + format!("- move down{}", " ".repeat(29)).fg(self.colors.normal.yellow), ], [ - format!("k{}", " ".repeat(11)), - format!("- move up{}", " ".repeat(31)), + format!("k{}", " ".repeat(11)).fg(self.colors.normal.red), + format!("- move up{}", " ".repeat(31)).fg(self.colors.normal.yellow), ], [ - format!("d{}", " ".repeat(11)), - format!("- deletes header{}", " ".repeat(20)), + format!("d{}", " ".repeat(11)).fg(self.colors.normal.red), + format!("- deletes header{}", " ".repeat(20)).fg(self.colors.normal.yellow), ], [ - format!("space{}", " ".repeat(7)), - format!("- enables or disabled header{}", " ".repeat(12)), + format!("space{}", " ".repeat(7)).fg(self.colors.normal.red), + format!("- enables or disabled header{}", " ".repeat(12)) + .fg(self.colors.normal.yellow), ], [ - format!("enter{}", " ".repeat(7)), - format!("- select header for editing{}", " ".repeat(13)), + format!("enter{}", " ".repeat(7)).fg(self.colors.normal.red), + format!("- select header for editing{}", " ".repeat(13)) + .fg(self.colors.normal.yellow), ], [ - format!("?{}", " ".repeat(11)), - format!("- shows this help message{}", " ".repeat(15)), + format!("?{}", " ".repeat(11)).fg(self.colors.normal.red), + format!("- shows this help message{}", " ".repeat(15)) + .fg(self.colors.normal.yellow), ], ]; - Layout::default() - .direction(Direction::Vertical) - .constraints((0..help_popup.height).map(|_| Constraint::Length(1))) - .split(help_popup) + let lines: Vec = lines + .into_iter() + .map(|l| Line::from(l.into_iter().collect::>())) + .collect(); + + let mut logo = LOGO_ASCII[1]; + let size = frame.size(); + let logo_size = logo.len(); + // we are adding 2 spaces for the gap between the logo and the text + // 1 space for the gap between the help lines and the hint + // 1 space for the hint itself + // 1 space after the hint + let mut total_size = logo_size.add(lines.len()).add(5) as u16; + + if total_size.ge(&size.height) { + logo = &[]; + total_size = lines.len().add(2) as u16; + } + + let popup_size = Rect::new( + size.width.div(2).saturating_sub(25), + size.height.div(2).saturating_sub(total_size.div(2)), + 50, + total_size, + ); + + let components = logo .iter() - .zip(lines) - .for_each(|(size, line)| { - let sizes = Layout::default() - .constraints([Constraint::Length(12), Constraint::Fill(1)]) - .direction(Direction::Horizontal) - .split(*size); + .map(|line| Line::from(line.fg(self.colors.normal.red))) + .chain(std::iter::repeat(Line::from("")).take(2)) + .chain(lines) + .collect::>(); - let prefix = line[0].to_string().fg(self.colors.normal.red); - let description = line[1].to_string().fg(self.colors.normal.white); + let hint_size = Rect::new( + popup_size.x, + popup_size.y.add(popup_size.height).add(1), + 40, + 1, + ); - frame.render_widget(prefix, sizes[0]); - frame.render_widget(description, sizes[1]); - }); + let hint = Line::from("press any key to close this dialog") + .fg(self.colors.bright.black) + .centered(); - frame.render_widget(Paragraph::new(logo), logo_size); + frame.render_widget(Paragraph::new(components), popup_size); frame.render_widget(Paragraph::new(hint), hint_size); } - pub fn draw_overlay(&mut self, frame: &mut Frame) -> anyhow::Result<()> { - let overlay = self.collection_store.borrow().peek_overlay(); + pub fn draw_overlay( + &mut self, + frame: &mut Frame, + overlay: CollectionViewerOverlay, + ) -> anyhow::Result<()> { match overlay { CollectionViewerOverlay::HeadersHelp => self.draw_help_overlay(frame), CollectionViewerOverlay::HeadersDelete => { - self.delete_prompt.draw(frame, frame.size())? + self.delete_prompt.draw(frame, frame.size())?; + } + CollectionViewerOverlay::HeadersForm(header_idx) => { + self.header_form.update(header_idx); + self.header_form.draw(frame, frame.size())?; } _ => {} } @@ -387,6 +397,21 @@ impl Eventful for HeadersEditor<'_> { .borrow_mut() .push_overlay(CollectionViewerOverlay::HeadersDelete); } + KeyCode::Enter => { + if headers.is_empty() { + return Ok(None); + } + + if headers.get(self.selected_row).is_none() { + tracing::error!("tried to edit a non-existing header"); + anyhow::bail!("tried to edit a non-existing header"); + }; + + drop(request); + self.collection_store + .borrow_mut() + .push_overlay(CollectionViewerOverlay::HeadersForm(self.selected_row)); + } _ => {} } diff --git a/hac-client/src/pages/collection_viewer/request_editor/headers_editor_delete_prompt.rs b/hac-client/src/pages/collection_viewer/request_editor/headers_editor_delete_prompt.rs index 10ee9d3..eefbb5d 100644 --- a/hac-client/src/pages/collection_viewer/request_editor/headers_editor_delete_prompt.rs +++ b/hac-client/src/pages/collection_viewer/request_editor/headers_editor_delete_prompt.rs @@ -4,7 +4,6 @@ use crate::ascii::LOGO_ASCII; use crate::pages::{overlay::make_overlay, Eventful, Renderable}; use crossterm::event::{KeyCode, KeyEvent}; -use hac_core::collection::types::HeaderMap; use rand::Rng; use ratatui::style::Stylize; use ratatui::text::Line; @@ -39,16 +38,6 @@ impl Renderable for HeadersEditorDeletePrompt<'_> { fn draw(&mut self, frame: &mut Frame, _: Rect) -> anyhow::Result<()> { make_overlay(self.colors, self.colors.normal.black, 0.1, frame); - let logo = LOGO_ASCII[self.logo_idx]; - let size = frame.size(); - - let popup_size = Rect::new( - size.width.div(2).saturating_sub(25), - size.height.div(2).saturating_sub(1), - 50, - 3, - ); - let lines: Vec = vec![ "are you sure you want to delete this header?" .fg(self.colors.normal.yellow) @@ -59,30 +48,33 @@ impl Renderable for HeadersEditorDeletePrompt<'_> { "es ".fg(self.colors.normal.white), "[N]".fg(self.colors.normal.red), "o".fg(self.colors.normal.white), - ]), + ]) + .centered(), ]; - let logo_size = Rect::new( - size.width - .div(2) - .saturating_sub(logo[0].len().div(2) as u16), - size.y.add(4), - logo[0].len() as u16, - logo.len() as u16, + let logo = LOGO_ASCII[self.logo_idx]; + let size = frame.size(); + let logo_size = logo.len(); + // we are adding 2 spaces for the gap between the logo and the text + // 1 space for the gap between the help lines and the hint + // 1 space for the hint itself + let total_size = logo_size.add(lines.len()).add(4) as u16; + + let popup_size = Rect::new( + size.width.div(2).saturating_sub(25), + size.height.div(2).saturating_sub(total_size.div(2)), + 50, + total_size, ); - let logo = logo + let components = logo .iter() .map(|line| Line::from(line.fg(self.colors.normal.red))) + .chain(std::iter::repeat(Line::from("")).take(2)) + .chain(lines) .collect::>(); - frame.render_widget(Paragraph::new(logo), logo_size); - frame.render_widget( - Paragraph::new(lines) - .bg(self.colors.normal.black) - .centered(), - popup_size, - ); + frame.render_widget(Paragraph::new(components).centered(), popup_size); Ok(()) } diff --git a/hac-client/src/pages/collection_viewer/request_editor/headers_editor_form.rs b/hac-client/src/pages/collection_viewer/request_editor/headers_editor_form.rs new file mode 100644 index 0000000..079aea8 --- /dev/null +++ b/hac-client/src/pages/collection_viewer/request_editor/headers_editor_form.rs @@ -0,0 +1,70 @@ +use std::{cell::RefCell, rc::Rc}; + +use crate::pages::{ + collection_viewer::collection_store::CollectionStore, overlay::make_overlay, Eventful, + Renderable, +}; + +use crossterm::event::KeyEvent; +use ratatui::{layout::Rect, Frame}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HeadersEditorFormEvent { + FinishEdit, +} + +#[derive(Debug)] +pub struct HeadersEditorForm<'hef> { + colors: &'hef hac_colors::Colors, + collection_store: Rc>, + header_idx: usize, +} + +impl<'hef> HeadersEditorForm<'hef> { + pub fn new( + colors: &'hef hac_colors::Colors, + collection_store: Rc>, + ) -> HeadersEditorForm { + HeadersEditorForm { + colors, + header_idx: 0, + collection_store, + } + } + + pub fn update(&mut self, header_idx: usize) { + self.header_idx = header_idx; + } +} + +impl Renderable for HeadersEditorForm<'_> { + fn draw(&mut self, frame: &mut Frame, size: Rect) -> anyhow::Result<()> { + make_overlay(self.colors, self.colors.normal.black, 0.0, frame); + + let store = self.collection_store.borrow_mut(); + let Some(request) = store.get_selected_request() else { + tracing::error!("trying to edit a header without a selected request"); + anyhow::bail!("trying to edit a header without a selected request"); + }; + + let request = request.read().unwrap(); + let Some(ref header) = request.headers else { + tracing::error!("trying to edit a header that don't exist"); + anyhow::bail!("trying to edit a header that don't exist"); + }; + + + + //frame.render_widget(Par, area); + + Ok(()) + } +} + +impl Eventful for HeadersEditorForm<'_> { + type Result = HeadersEditorFormEvent; + + fn handle_key_event(&mut self, key_event: KeyEvent) -> anyhow::Result> { + Ok(Some(HeadersEditorFormEvent::FinishEdit)) + } +}