Skip to content

Commit

Permalink
Merge pull request #137 from afonsofrancof/refactor_float
Browse files Browse the repository at this point in the history
Big refactor. Made floats generic, to show anything
  • Loading branch information
ChrisTitusTech authored Aug 8, 2024
2 parents 1ef5c85 + 0ee4412 commit b82d0cd
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 289 deletions.
138 changes: 85 additions & 53 deletions src/float.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,91 @@
use ratatui::layout::{Constraint, Direction, Layout, Rect};

/// This function just makes a given area smaller by 20 % in each direction, creating a kind of
/// "floating window". And you don't actually need all the constraints, and layouts to do that, its
/// very easy to calculate it directly, but I chose to use the ratatui API
pub fn floating_window(size: Rect) -> Rect {
// If the terminal window is small enough, just take up all the space for the command
if size.width < 85 || size.height < 25 {
return size;
}
let hor_float = Layout::default()
.constraints([
Constraint::Percentage(20),
Constraint::Percentage(60),
Constraint::Percentage(20),
])
.direction(Direction::Horizontal)
.split(size)[1];
Layout::default()
.constraints([
Constraint::Percentage(20),
Constraint::Percentage(60),
Constraint::Percentage(20),
])
.direction(Direction::Vertical)
.split(hor_float)[1]
use crossterm::event::{KeyCode, KeyEvent};
use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
Frame,
};

pub trait FloatContent {
fn draw(&mut self, frame: &mut Frame, area: Rect);
fn handle_key_event(&mut self, key: &KeyEvent) -> bool;
fn is_finished(&self) -> bool;
}

pub struct Float<T: FloatContent> {
content: Option<T>,
width_percent: u16,
height_percent: u16,
}

/// Here is how would a purely math based function look like:
/// But it might break on smaller numbers
fn _unused_manual_floating_window(size: Rect) -> Rect {
// If the terminal window is small enough, just take up all the space for the command
if size.width < 85 || size.height < 25 {
return size;
impl<T: FloatContent> Float<T> {
pub fn new(width_percent: u16, height_percent: u16) -> Self {
Self {
content: None,
width_percent,
height_percent,
}
}
let new_width = size.width * 60 / 100;
let new_height = size.height * 60 / 100;
let new_x = size.x + size.width * 20 / 100;
let new_y = size.y + size.height * 20 / 100;
Rect {
width: new_width,
height: new_height,
x: new_x,
y: new_y,

fn floating_window(&self, size: Rect) -> Rect {
let hor_float = Layout::default()
.constraints([
Constraint::Percentage((100 - self.width_percent) / 2),
Constraint::Percentage(self.width_percent),
Constraint::Percentage((100 - self.width_percent) / 2),
])
.direction(Direction::Horizontal)
.split(size)[1];

Layout::default()
.constraints([
Constraint::Percentage((100 - self.height_percent) / 2),
Constraint::Percentage(self.height_percent),
Constraint::Percentage((100 - self.height_percent) / 2),
])
.direction(Direction::Vertical)
.split(hor_float)[1]
}
}

#[test]
fn test_floating() {
let rect = Rect {
x: 10,
y: 2,
width: 100,
height: 200,
};
let res1 = floating_window(rect);
let res2 = floating_window(rect);
assert_eq!(res1, res2);
pub fn draw(&mut self, frame: &mut Frame, parent_area: Rect) {
let popup_area = self.floating_window(parent_area);

if let Some(content) = &mut self.content {
let content_area = Rect {
x: popup_area.x,
y: popup_area.y,
width: popup_area.width,
height: popup_area.height,
};

content.draw(frame, content_area);
}
}

// Returns true if the key was processed by this Float.
pub fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
if let Some(content) = &mut self.content {
match key.code {
KeyCode::Enter | KeyCode::Char('p') | KeyCode::Esc | KeyCode::Char('q') => {
if content.is_finished() {
self.content = None;
} else {
content.handle_key_event(key);
}
}
_ => {
content.handle_key_event(key);
}
}
true
} else {
false
}
}

pub fn get_content(&self) -> &Option<T> {
&self.content
}

pub fn set_content(&mut self, content: Option<T>) {
self.content = content;
}
}
82 changes: 82 additions & 0 deletions src/floating_text.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use crate::float::FloatContent;
use crossterm::event::{KeyCode, KeyEvent};
use ratatui::{
layout::Rect,
style::{Style, Stylize},
text::Line,
widgets::{Block, Borders, List},
Frame,
};

pub struct FloatingText {
text: Vec<String>,
scroll: usize,
}

impl FloatingText {
pub fn new(text: Vec<String>) -> Self {
Self { text, scroll: 0 }
}

fn scroll_down(&mut self) {
if self.scroll + 1 < self.text.len() {
self.scroll += 1;
}
}

fn scroll_up(&mut self) {
if self.scroll > 0 {
self.scroll -= 1;
}
}
}

impl FloatContent for FloatingText {
fn draw(&mut self, frame: &mut Frame, area: Rect) {
// Define the Block with a border and background color
let block = Block::default()
.borders(Borders::ALL)
.style(Style::default());

// Draw the Block first
frame.render_widget(block.clone(), area);

// Calculate the inner area to ensure text is not drawn over the border
let inner_area = block.inner(area);

// Create the list of lines to be displayed
let lines: Vec<Line> = self
.text
.iter()
.skip(self.scroll)
.take(inner_area.height as usize)
.map(|line| Line::from(line.as_str()))
.collect();

// Create list widget
let list = List::new(lines)
.block(Block::default())
.highlight_style(Style::default().reversed());

// Render the list inside the bordered area
frame.render_widget(list, inner_area);
}

fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
match key.code {
KeyCode::Down | KeyCode::Char('j') => {
self.scroll_down();
true
}
KeyCode::Up | KeyCode::Char('k') => {
self.scroll_up();
true
}
_ => false,
}
}

fn is_finished(&self) -> bool {
true
}
}
Loading

0 comments on commit b82d0cd

Please sign in to comment.