Skip to content

Commit

Permalink
feat: creating new requests
Browse files Browse the repository at this point in the history
  • Loading branch information
wllfaria committed Oct 6, 2024
1 parent 28e2cca commit a8e9b78
Show file tree
Hide file tree
Showing 7 changed files with 392 additions and 238 deletions.
48 changes: 36 additions & 12 deletions hac-client/src/components/blending_list.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::rc::Rc;

use ratatui::layout::{Constraint, Layout, Rect};
use ratatui::style::Stylize;
use ratatui::text::Line;
use ratatui::widgets::{Block, Padding, Paragraph};
use ratatui::Frame;

Expand All @@ -10,44 +9,69 @@ use crate::HacColors;

#[derive(Debug)]
pub struct BlendingList {
selected: usize,
pub selected: usize,
scroll: usize,
amount: usize,
page_size: usize,
total: usize,
colors: HacColors,
padding: u16,
}

impl BlendingList {
pub fn new(selected: usize, amount: usize, padding: u16, colors: HacColors) -> Self {
pub fn new(selected: usize, total: usize, page_size: usize, padding: u16, colors: HacColors) -> Self {
Self {
selected,
amount,
total,
page_size,
scroll: 0,
colors,
padding,
}
}

pub fn reset(&mut self) {
self.selected = 0;
self.scroll = 0;
}

pub fn select_up(&mut self) {
self.selected = self.selected.saturating_sub(1);
if self.selected.saturating_sub(self.scroll) == 0 {
self.scroll = self.scroll - (self.scroll - self.selected);
}
}

pub fn select_down(&mut self) {
self.selected = usize::min(self.selected + 1, self.total - 1);
if self.selected - self.scroll >= self.page_size.saturating_sub(1) {
let diff = usize::abs_diff(self.selected - self.scroll, self.page_size.saturating_sub(1));
self.scroll += diff;
}
}

pub fn draw_with<'a, T: 'a, E: Fn(&'a T) -> O, O: AsRef<str>>(
&mut self,
frame: &mut Frame,
items: impl Iterator<Item = &'a T>,
extractor: E,
area: Rect,
) {
let areas = Layout::vertical((0..self.amount).map(|_| Constraint::Length(1 + self.padding))).split(area);
let areas = Layout::vertical((0..self.page_size).map(|_| Constraint::Length(1 + self.padding))).split(area);
let blend_max = 0.2;
let blend_step = 0.8 / self.amount as f32;
let blend_step = 1.0 / self.page_size as f32;

for (i, item) in items.skip(self.scroll).take(self.amount).enumerate() {
for (i, item) in items.skip(self.scroll).take(self.page_size).enumerate() {
let diff_from_selected = usize::abs_diff(i, self.selected - self.scroll);
let blend_step = diff_from_selected as f32 * blend_step;
let name = extractor(item);

let mut color = alpha_blend_multiply(
self.colors.normal.white,
self.colors.normal.black,
f32::max(1.0 - blend_step * i as f32, blend_max),
f32::max(1.0 - blend_step, blend_max),
);

if self.selected == i {
if self.selected - self.scroll == i {
color = self.colors.normal.red;
}

Expand All @@ -57,7 +81,7 @@ impl BlendingList {
}

frame.render_widget(
Paragraph::new(name.as_ref().fg(color)).centered().block(block),
Paragraph::new(Line::from(name.as_ref().fg(color)).centered()).block(block),
areas[i],
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::rc::Rc;
use std::sync::mpsc::{channel, Sender};

use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use futures::stream::SelectNextSome;
use hac_core::command::Command;
use hac_core::net::request_manager::Response;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
Expand Down
142 changes: 105 additions & 37 deletions hac-client/src/pages/collection_viewer/sidebar/create_request_form.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
use std::rc::Rc;
use std::sync::mpsc::{channel, Sender};

use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use hac_core::command::Command;
use hac_store::collection::{Folder, ReqMethod, ReqTreeNode};
use hac_store::collection::ReqMethod;
use hac_store::slab::Key;
use ratatui::layout::{Constraint, Flex, Layout, Margin, Rect};
use ratatui::style::{Color, Style, Stylize};
use ratatui::style::{Style, Stylize};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Clear, Padding, Paragraph};
use ratatui::widgets::{Block, Borders, Clear, Paragraph};
use ratatui::Frame;

use crate::app::Routes;
use crate::ascii::LOGO_ASCII;
use crate::components::blending_list::BlendingList;
use crate::components::input::Input;
use crate::pages::overlay::make_overlay;
use crate::renderable::{Eventful, Renderable};
use crate::{HacColors, MIN_HEIGHT, MIN_WIDTH};
use crate::router::RouterMessage;
use crate::{router_drop_dialog, HacColors, MIN_HEIGHT, MIN_WIDTH};

#[derive(Debug)]
struct CreateReqFormLayout {
Expand All @@ -25,6 +28,7 @@ struct CreateReqFormLayout {
parent: Rect,
methods: Rc<[Rect]>,
parent_listing: Rect,
parent_hint: Rect,
}

#[derive(Debug, Default, PartialEq, Eq)]
Expand Down Expand Up @@ -69,6 +73,7 @@ pub struct CreateRequestForm {
parent: Option<Key>,
form_step: FormStep,
parent_listing: BlendingList,
messager: Sender<RouterMessage>,
}

impl CreateRequestForm {
Expand All @@ -80,8 +85,9 @@ impl CreateRequestForm {
focus: Default::default(),
parent: None,
form_step: FormStep::MainForm,
parent_listing: BlendingList::new(0, 6, 1, colors.clone()),
parent_listing: BlendingList::new(0, hac_store::collection::len_folders(), 13, 0, colors.clone()),
colors,
messager: channel().0,
}
}

Expand Down Expand Up @@ -118,18 +124,18 @@ impl CreateRequestForm {
" - Confirm • ".fg(self.colors.bright.black),
"Esc".fg(self.colors.bright.green).bold(),
" - Cancel • ".fg(self.colors.bright.black),
"1-5".fg(self.colors.bright.green).bold(),
" - Change Method • ".fg(self.colors.bright.black),
"Ctrl p".fg(self.colors.bright.green).bold(),
" - Parent".fg(self.colors.bright.black),
" - Parent • ".fg(self.colors.bright.black),
"Backspace".fg(self.colors.bright.green).bold(),
" - Remove parent".fg(self.colors.bright.black),
]
.into_iter(),
}
}

fn draw_main_form(&mut self, frame: &mut Frame) {
let border_style = match self.focus == FieldFocus::Name {
true => Style::new().fg(self.colors.normal.white),
true => Style::new().fg(self.colors.bright.red),
false => Style::new().fg(self.colors.bright.black),
};
let label = String::from("Request Name");
Expand All @@ -148,7 +154,7 @@ impl CreateRequestForm {
for (idx, method) in ReqMethod::iter().enumerate() {
let selected = method == self.method;
let number_color = match selected {
true => self.colors.bright.red,
true => self.colors.bright.blue,
false => self.colors.bright.black,
};
let area = self.layout.methods[idx];
Expand All @@ -159,26 +165,33 @@ impl CreateRequestForm {
let parts = vec![
(idx + 1).to_string().fg(number_color),
" ".repeat(left_pad).into(),
method.fg(self.colors.normal.white),
method.fg(number_color),
];

let mut block = Block::new()
.borders(Borders::ALL)
.border_style(Style::new().fg(self.colors.bright.black));
if let FieldFocus::Methods = self.focus {
block = block.border_style(Style::new().fg(self.colors.normal.white));
block = block.border_style(Style::new().fg(self.colors.bright.red));
}
if selected {
block = block.border_style(Style::new().fg(self.colors.bright.red));
block = block.border_style(Style::new().fg(self.colors.bright.blue));
}

frame.render_widget(Paragraph::new(Line::from(parts)).block(block), area);
}

let hint = self.make_contextual_hint();
let parent_name = "No Parent".to_string();
let mut parent_name = "No Parent".to_string();
if let Some(parent) = self.parent {
hac_store::collection::get_folder(parent, |folder, _| parent_name.clone_from(&folder.name));
};

let parent_color =
if self.focus == FieldFocus::Parent { self.colors.bright.red } else { self.colors.bright.black };

let parent = Paragraph::new(Line::from(parent_name).centered())
.block(Block::new().borders(Borders::ALL).fg(self.colors.normal.white));
.block(Block::new().borders(Borders::ALL).fg(parent_color));

frame.render_widget(name_input, self.layout.name);
frame.render_widget(logo, self.layout.logo);
Expand Down Expand Up @@ -207,16 +220,80 @@ impl CreateRequestForm {
self.parent_listing
.draw_with(frame, folders.iter(), |name| name, self.layout.parent_listing);

let hint = vec![
"Enter".fg(self.colors.bright.green).bold(),
" - Confirm • ".fg(self.colors.bright.black),
"Esc".fg(self.colors.bright.green).bold(),
" - Cancel • ".fg(self.colors.bright.black),
"j/k ↑/↓".fg(self.colors.normal.green),
" - Choose".fg(self.colors.bright.black),
];

frame.render_widget(Line::from(hint).centered(), self.layout.parent_hint);
frame.render_widget(logo, self.layout.logo);
}

fn main_form_key_event(&mut self, key_event: KeyEvent) -> anyhow::Result<Option<Command>> {
match key_event.code {
KeyCode::Char('p') if matches!(key_event.modifiers, KeyModifiers::CONTROL) => {
self.form_step = FormStep::ParentSelector;
}

KeyCode::Char(ch) if matches!(self.focus, FieldFocus::Name) => self.name.push(ch),
KeyCode::Backspace if matches!(self.focus, FieldFocus::Name) => _ = self.name.pop(),

KeyCode::Left if matches!(self.focus, FieldFocus::Methods) => self.method.set_prev(),
KeyCode::Right if matches!(self.focus, FieldFocus::Methods) => self.method.set_next(),
KeyCode::Up if matches!(self.focus, FieldFocus::Methods) => self.method.set_first(),
KeyCode::Down if matches!(self.focus, FieldFocus::Methods) => self.method.set_last(),
KeyCode::Char('h') if matches!(self.focus, FieldFocus::Methods) => self.method.set_prev(),
KeyCode::Char('j') if matches!(self.focus, FieldFocus::Methods) => self.method.set_last(),
KeyCode::Char('k') if matches!(self.focus, FieldFocus::Methods) => self.method.set_first(),
KeyCode::Char('l') if matches!(self.focus, FieldFocus::Methods) => self.method.set_next(),
KeyCode::Char(ch @ '1'..='5') if matches!(self.focus, FieldFocus::Methods) => {
self.method = ReqMethod::from(ch)
}

KeyCode::Backspace if matches!(self.focus, FieldFocus::Parent) => self.parent = None,

KeyCode::Tab => self.focus.next(),
KeyCode::BackTab => self.focus.prev(),
KeyCode::Esc => {
router_drop_dialog!(&self.messager, Routes::CreateRequest.into());
}
KeyCode::Enter => {
let request = hac_store::collection::Request::new(self.name.clone(), self.method, self.parent);
hac_store::collection::push_request(request, self.parent);
router_drop_dialog!(&self.messager, Routes::CreateRequest.into());
}
_ => {}
};

Ok(None)
}

fn parent_selector_key_event(&mut self, key_event: KeyEvent) -> anyhow::Result<Option<Command>> {
match key_event.code {
KeyCode::Char('j') | KeyCode::Down => self.parent_listing.select_down(),
KeyCode::Char('k') | KeyCode::Up => self.parent_listing.select_up(),
KeyCode::Enter => {
if hac_store::collection::has_folders() {
self.parent = Some(self.parent_listing.selected);
self.form_step = FormStep::MainForm;
self.parent_listing.reset()
}
}
_ => {}
};

Ok(None)
}
}

impl Renderable for CreateRequestForm {
type Input = ();
type Output = ();

fn data(&self, _: u8) -> Self::Output {}

fn draw(&mut self, frame: &mut Frame, _: Rect) -> anyhow::Result<()> {
make_overlay(self.colors.clone(), self.colors.normal.black, 0.15, frame);

Expand All @@ -228,6 +305,12 @@ impl Renderable for CreateRequestForm {
Ok(())
}

fn data(&self, _: u8) -> Self::Output {}

fn attach_navigator(&mut self, messager: Sender<RouterMessage>) {
self.messager = messager;
}

fn resize(&mut self, new_size: Rect) {
self.layout = build_layout(new_size);
}
Expand All @@ -241,25 +324,9 @@ impl Eventful for CreateRequestForm {
return Ok(Some(Command::Quit));
}

match key_event.code {
KeyCode::Char('p') if matches!(key_event.modifiers, KeyModifiers::CONTROL) => {
self.form_step = FormStep::ParentSelector;
}

KeyCode::Char(ch) if matches!(self.focus, FieldFocus::Name) => self.name.push(ch),
KeyCode::Backspace if matches!(self.focus, FieldFocus::Name) => _ = self.name.pop(),

KeyCode::Left if matches!(self.focus, FieldFocus::Methods) => self.method.set_prev(),
KeyCode::Right if matches!(self.focus, FieldFocus::Methods) => self.method.set_next(),
KeyCode::Up if matches!(self.focus, FieldFocus::Methods) => self.method.set_first(),
KeyCode::Down if matches!(self.focus, FieldFocus::Methods) => self.method.set_last(),
KeyCode::Char(ch @ '1'..='5') if matches!(self.focus, FieldFocus::Methods) => {
self.method = ReqMethod::from(ch)
}

KeyCode::Tab => self.focus.next(),
KeyCode::BackTab => self.focus.prev(),
_ => {}
match self.form_step {
FormStep::MainForm => self.main_form_key_event(key_event)?,
FormStep::ParentSelector => self.parent_selector_key_event(key_event)?,
};

Ok(None)
Expand Down Expand Up @@ -290,7 +357,7 @@ fn build_layout(area: Rect) -> CreateReqFormLayout {
.flex(Flex::Center)
.areas(form);

let [_, _, parent_listing, _, _] = Layout::vertical([
let [_, _, parent_listing, _, parent_hint] = Layout::vertical([
Constraint::Length(LOGO_ASCII.len() as u16),
Constraint::Length(1),
Constraint::Length(13),
Expand All @@ -309,5 +376,6 @@ fn build_layout(area: Rect) -> CreateReqFormLayout {
methods,
parent,
parent_listing,
parent_hint,
}
}
3 changes: 2 additions & 1 deletion hac-client/src/pages/collection_viewer/sidebar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ impl Renderable for Sidebar {
fn draw(&mut self, frame: &mut Frame, size: Rect) -> anyhow::Result<()> {
let layout = hac_store::collection::tree_layout();
let mut lines = vec![];

layout.nodes.into_iter().for_each(|node| match node {
ReqTreeNode::Req(key) => hac_store::collection::get_root_request(key, |req, status| {
let name = req.name.clone();
Expand Down Expand Up @@ -368,7 +369,7 @@ impl Eventful for Sidebar {
KeyCode::Char('k') | KeyCode::Up => collection::hover_prev(),
KeyCode::Char('n') => return Ok(Some(SidebarEvent::CreateRequest)),
KeyCode::Enter => {
if let Some((which, key)) = collection::get_hovered_request(|req| req).flatten() {
if let Some((which, key)) = collection::get_hovered_request(|req| req) {
match which {
WhichSlab::Requests | WhichSlab::RootRequests => collection::select_request((which, key)),
WhichSlab::Folders => collection::toggle_dir(key),
Expand Down
2 changes: 1 addition & 1 deletion hac-client/src/pages/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl<'a> Input<'a> {
self.focused = true;
}

fn build_input(&self, value: String, size: Rect) -> Paragraph<'_> {
fn build_input(&self, value: String, _: Rect) -> Paragraph<'_> {
let border_color = if self.focused {
Style::default().fg(self.colors.normal.red)
} else {
Expand Down
Loading

0 comments on commit a8e9b78

Please sign in to comment.