mirror of
https://github.com/lucaspalomodevelop/binbreak.git
synced 2026-03-13 00:07:28 +00:00
cleanup
This commit is contained in:
parent
e2fd52d6b3
commit
6c7f3e0729
253
src/app.rs
Normal file
253
src/app.rs
Normal file
@ -0,0 +1,253 @@
|
||||
use crate::binary_numbers::{BinaryNumbersGame, Bits};
|
||||
use crate::main_screen_widget::MainScreenWidget;
|
||||
use crate::utils::{AsciiArtWidget, AsciiCells};
|
||||
use crossterm::event;
|
||||
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::prelude::{Color, Modifier, Span, StatefulWidget, Style, Widget};
|
||||
use ratatui::widgets::{List, ListItem, ListState};
|
||||
use std::collections::HashMap;
|
||||
use std::thread;
|
||||
use std::time::Instant;
|
||||
|
||||
enum AppState {
|
||||
Start(StartMenuState),
|
||||
Playing(BinaryNumbersGame),
|
||||
Exit,
|
||||
}
|
||||
|
||||
fn handle_start_input(state: &mut StartMenuState, key: KeyEvent) -> Option<AppState> {
|
||||
match key.code {
|
||||
KeyCode::Up => state.select_previous(),
|
||||
KeyCode::Down => state.select_next(),
|
||||
KeyCode::Enter => {
|
||||
let bits = state.selected_bits();
|
||||
return Some(AppState::Playing(BinaryNumbersGame::new(bits)));
|
||||
}
|
||||
KeyCode::Esc => return Some(AppState::Exit),
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn render_start_screen(state: &mut StartMenuState, area: Rect, buf: &mut Buffer) {
|
||||
// Build ASCII art to obtain real dimensions
|
||||
let cells = ascii_art_cells();
|
||||
let ascii_width = cells.get_width();
|
||||
let ascii_height = cells.get_height();
|
||||
let ascii_widget = AsciiArtWidget::new(cells);
|
||||
|
||||
let selected = state.selected_index();
|
||||
let upper_labels: Vec<String> = state.items.iter().map(|(l, _)| l.to_uppercase()).collect();
|
||||
let max_len = upper_labels
|
||||
.iter()
|
||||
.map(|s| s.len() as u16)
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
let list_width = 2 + max_len; // marker + space + label
|
||||
let list_height = upper_labels.len() as u16;
|
||||
|
||||
// Vertical spacing between ASCII art and list
|
||||
let spacing: u16 = 3;
|
||||
let total_height = ascii_height + spacing + list_height;
|
||||
|
||||
// Center vertically & horizontally
|
||||
let start_y = area.y + area.height.saturating_sub(total_height) / 2;
|
||||
let ascii_x = area.x + area.width.saturating_sub(ascii_width) / 2;
|
||||
let list_x = area.x + area.width.saturating_sub(list_width) / 2;
|
||||
let ascii_y = start_y;
|
||||
let list_y = ascii_y + ascii_height + spacing;
|
||||
|
||||
// Define rects (clamp to area)
|
||||
let ascii_area = Rect::new(
|
||||
ascii_x,
|
||||
ascii_y,
|
||||
ascii_width.min(area.width),
|
||||
ascii_height.min(area.height),
|
||||
);
|
||||
let list_area = Rect::new(
|
||||
list_x,
|
||||
list_y,
|
||||
list_width.min(area.width),
|
||||
list_height.min(area.height.saturating_sub(list_y - area.y)),
|
||||
);
|
||||
|
||||
// Render ASCII art
|
||||
ascii_widget.render(ascii_area, buf);
|
||||
|
||||
// Palette for menu flair
|
||||
let palette = [
|
||||
Color::LightGreen,
|
||||
Color::LightCyan,
|
||||
Color::LightBlue,
|
||||
Color::LightMagenta,
|
||||
Color::LightYellow,
|
||||
Color::LightRed,
|
||||
];
|
||||
|
||||
let items: Vec<ListItem> = upper_labels
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, label)| {
|
||||
let marker = if i == selected { '»' } else { ' ' };
|
||||
let padded = format!("{:<width$}", label, width = max_len as usize);
|
||||
let line = format!("{} {}", marker, padded);
|
||||
let style = Style::default()
|
||||
.fg(palette[i % palette.len()])
|
||||
.add_modifier(Modifier::BOLD);
|
||||
ListItem::new(Span::styled(line, style))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let list = List::new(items);
|
||||
ratatui::widgets::StatefulWidget::render(list, list_area, buf, &mut state.list_state);
|
||||
}
|
||||
|
||||
pub fn run_app(terminal: &mut ratatui::DefaultTerminal) -> color_eyre::Result<()> {
|
||||
let mut app_state = AppState::Start(StartMenuState::new());
|
||||
let mut last_frame_time = Instant::now();
|
||||
let target_frame_duration = std::time::Duration::from_millis(33); // ~30 FPS
|
||||
|
||||
while !matches!(app_state, AppState::Exit) {
|
||||
let now = Instant::now();
|
||||
let dt = now - last_frame_time;
|
||||
last_frame_time = now;
|
||||
|
||||
terminal.draw(|f| match &mut app_state {
|
||||
AppState::Start(menu) => render_start_screen(menu, f.area(), f.buffer_mut()),
|
||||
AppState::Playing(game) => f.render_widget(&mut *game, f.area()),
|
||||
AppState::Exit => {}
|
||||
})?;
|
||||
|
||||
// Advance game if playing
|
||||
if let AppState::Playing(game) = &mut app_state {
|
||||
game.run(dt.as_secs_f64());
|
||||
if game.is_exit_intended() {
|
||||
app_state = AppState::Start(StartMenuState::new());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// handle input
|
||||
let poll_timeout = std::cmp::min(dt, target_frame_duration);
|
||||
if event::poll(poll_timeout)? {
|
||||
if let Event::Key(key) = event::read()? {
|
||||
if key.kind == KeyEventKind::Press {
|
||||
match key.code {
|
||||
// global exit via Ctrl+C
|
||||
KeyCode::Char('c') | KeyCode::Char('C')
|
||||
if key.modifiers == KeyModifiers::CONTROL =>
|
||||
{
|
||||
app_state = AppState::Exit;
|
||||
}
|
||||
|
||||
// state-specific input handling
|
||||
_ => {
|
||||
app_state = match app_state {
|
||||
AppState::Start(mut menu) => handle_start_input(&mut menu, key)
|
||||
.unwrap_or(AppState::Start(menu)),
|
||||
AppState::Playing(mut game) => {
|
||||
game.handle_game_input(key);
|
||||
AppState::Playing(game)
|
||||
}
|
||||
AppState::Exit => AppState::Exit,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cap frame rate
|
||||
let frame_duration = last_frame_time.elapsed();
|
||||
if frame_duration < target_frame_duration {
|
||||
thread::sleep(target_frame_duration - frame_duration);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ascii_art_cells() -> AsciiCells {
|
||||
let art = r#"
|
||||
,, ,, ,,
|
||||
*MM db *MM `7MM
|
||||
MM MM MM
|
||||
MM,dMMb.`7MM `7MMpMMMb. MM,dMMb.`7Mb,od8 .gP"Ya ,6"Yb. MM ,MP'
|
||||
MM `Mb MM MM MM MM `Mb MM' "',M' Yb 8) MM MM ;Y
|
||||
MM M8 MM MM MM MM M8 MM 8M"""""" ,pm9MM MM;Mm
|
||||
MM. ,M9 MM MM MM MM. ,M9 MM YM. , 8M MM MM `Mb.
|
||||
P^YbmdP'.JMML..JMML JMML.P^YbmdP'.JMML. `Mbmmd' `Moo9^Yo..JMML. YA.
|
||||
"#;
|
||||
|
||||
let colors = r#"
|
||||
,, ,, ,,
|
||||
*MM db *MM `7MM
|
||||
MM MM MM
|
||||
MM,dMMb.`7MM `7MMpMMMb. MM,dMMb.`7Mb,od8 .gP"Ya ,6"Yb. MM ,MP'
|
||||
MM `Mb MM MM MM MM `Mb MM' "',M' Yb 8) MM MM ;Y
|
||||
MM M8 MM MM MM MM M8 MM 8M"""""" ,pm9MM MM;Mm
|
||||
MM. ,M9 MM MM MM MM. ,M9 MM YM. , 8M MM MM `Mb.
|
||||
P^YbmdP'.JMML..JMML JMML.P^YbmdP'.JMML. `Mbmmd' `Moo9^Yo..JMML. YA.
|
||||
"#;
|
||||
|
||||
let color_map = HashMap::from([
|
||||
('M', Color::White),
|
||||
('b', Color::LightYellow),
|
||||
('d', Color::LightCyan),
|
||||
('Y', Color::LightGreen),
|
||||
('8', Color::LightMagenta),
|
||||
('*', Color::Magenta),
|
||||
('`', Color::Cyan),
|
||||
('6', Color::Green),
|
||||
('9', Color::Red),
|
||||
('(', Color::Blue),
|
||||
(')', Color::Blue),
|
||||
(' ', Color::Black),
|
||||
]);
|
||||
|
||||
let default_color = Color::LightBlue;
|
||||
AsciiCells::from(
|
||||
art.to_string(),
|
||||
colors.to_string(),
|
||||
&color_map,
|
||||
default_color,
|
||||
)
|
||||
}
|
||||
|
||||
// Start menu state
|
||||
struct StartMenuState {
|
||||
items: Vec<(String, Bits)>,
|
||||
list_state: ListState,
|
||||
}
|
||||
|
||||
impl StartMenuState {
|
||||
fn new() -> Self {
|
||||
let items = vec![
|
||||
("easy (4 bits)".to_string(), Bits::Four),
|
||||
("easy+16 (4 bits*16)".to_string(), Bits::FourShift4),
|
||||
("easy+256 (4 bits*256)".to_string(), Bits::FourShift8),
|
||||
("easy+4096 (4 bits*4096)".to_string(), Bits::FourShift12),
|
||||
("normal (8 bits)".to_string(), Bits::Eight),
|
||||
("master (12 bits)".to_string(), Bits::Twelve),
|
||||
("insane (16 bits)".to_string(), Bits::Sixteen),
|
||||
];
|
||||
Self {
|
||||
items,
|
||||
list_state: ListState::default().with_selected(Some(4)),
|
||||
} // default to normal (8 bits)
|
||||
}
|
||||
fn selected_index(&self) -> usize {
|
||||
self.list_state.selected().unwrap_or(0)
|
||||
}
|
||||
fn selected_bits(&self) -> Bits {
|
||||
self.items[self.selected_index()].1.clone()
|
||||
}
|
||||
fn select_next(&mut self) {
|
||||
self.list_state.select_next();
|
||||
}
|
||||
fn select_previous(&mut self) {
|
||||
self.list_state.select_previous();
|
||||
}
|
||||
}
|
||||
@ -657,6 +657,3 @@ impl HighScores {
|
||||
self.scores.insert(bits, score);
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: public helper for external modules (e.g., start screen) to read current high score for a bits mode
|
||||
pub fn get_high_score(bits: Bits) -> u32 { HighScores::load().get(bits.high_score_key()) }
|
||||
|
||||
229
src/main.rs
229
src/main.rs
@ -1,235 +1,12 @@
|
||||
mod app;
|
||||
mod binary_numbers;
|
||||
mod utils;
|
||||
mod main_screen_widget;
|
||||
|
||||
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
||||
use binary_numbers::{BinaryNumbersGame, Bits};
|
||||
use crate::main_screen_widget::MainScreenWidget;
|
||||
use utils::{AsciiArtWidget, AsciiCells};
|
||||
use ratatui::prelude::*;
|
||||
use ratatui::widgets::{List, ListItem, ListState};
|
||||
use std::collections::HashMap;
|
||||
use std::thread;
|
||||
use std::time::Instant;
|
||||
mod utils;
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
let mut terminal = ratatui::init();
|
||||
let result = run_app(&mut terminal);
|
||||
let result = app::run_app(&mut terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
// Start menu state
|
||||
struct StartMenuState {
|
||||
items: Vec<(String, Bits)>,
|
||||
list_state: ListState,
|
||||
}
|
||||
|
||||
impl StartMenuState {
|
||||
fn new() -> Self {
|
||||
let items = vec![
|
||||
("easy (4 bits)".to_string(), Bits::Four),
|
||||
("easy+16 (4 bits*16)".to_string(), Bits::FourShift4),
|
||||
("easy+256 (4 bits*256)".to_string(), Bits::FourShift8),
|
||||
("easy+4096 (4 bits*4096)".to_string(), Bits::FourShift12),
|
||||
("normal (8 bits)".to_string(), Bits::Eight),
|
||||
("master (12 bits)".to_string(), Bits::Twelve),
|
||||
("insane (16 bits)".to_string(), Bits::Sixteen),
|
||||
];
|
||||
Self { items, list_state: ListState::default().with_selected(Some(4)) } // default to normal (8 bits)
|
||||
}
|
||||
fn selected_index(&self) -> usize {
|
||||
self.list_state.selected().unwrap_or(0)
|
||||
}
|
||||
fn selected_bits(&self) -> Bits {
|
||||
self.items[self.selected_index()].1.clone()
|
||||
}
|
||||
fn select_next(&mut self) {
|
||||
self.list_state.select_next();
|
||||
}
|
||||
fn select_previous(&mut self) {
|
||||
self.list_state.select_previous();
|
||||
}
|
||||
}
|
||||
|
||||
enum AppState {
|
||||
Start(StartMenuState),
|
||||
Playing(BinaryNumbersGame),
|
||||
Exit,
|
||||
}
|
||||
|
||||
fn handle_start_input(state: &mut StartMenuState, key: KeyEvent) -> Option<AppState> {
|
||||
match key.code {
|
||||
KeyCode::Up => state.select_previous(),
|
||||
KeyCode::Down => state.select_next(),
|
||||
KeyCode::Enter => {
|
||||
let bits = state.selected_bits();
|
||||
return Some(AppState::Playing(BinaryNumbersGame::new(bits)));
|
||||
}
|
||||
KeyCode::Esc => return Some(AppState::Exit),
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn render_start_screen(state: &mut StartMenuState, area: Rect, buf: &mut Buffer) {
|
||||
// Build ASCII art to obtain real dimensions
|
||||
let cells = ascii_art_cells();
|
||||
let ascii_width = cells.get_width();
|
||||
let ascii_height = cells.get_height();
|
||||
let ascii_widget = AsciiArtWidget::new(cells);
|
||||
|
||||
let selected = state.selected_index();
|
||||
let upper_labels: Vec<String> = state.items.iter().map(|(l, _)| l.to_uppercase()).collect();
|
||||
let max_len = upper_labels.iter().map(|s| s.len() as u16).max().unwrap_or(0);
|
||||
|
||||
let list_width = 2 + max_len; // marker + space + label
|
||||
let list_height = upper_labels.len() as u16;
|
||||
|
||||
// Vertical spacing between ASCII art and list
|
||||
let spacing: u16 = 3;
|
||||
let total_height = ascii_height + spacing + list_height;
|
||||
|
||||
// Center vertically & horizontally
|
||||
let start_y = area.y + area.height.saturating_sub(total_height) / 2;
|
||||
let ascii_x = area.x + area.width.saturating_sub(ascii_width) / 2;
|
||||
let list_x = area.x + area.width.saturating_sub(list_width) / 2;
|
||||
let ascii_y = start_y;
|
||||
let list_y = ascii_y + ascii_height + spacing;
|
||||
|
||||
// Define rects (clamp to area)
|
||||
let ascii_area = Rect::new(ascii_x, ascii_y, ascii_width.min(area.width), ascii_height.min(area.height));
|
||||
let list_area = Rect::new(list_x, list_y, list_width.min(area.width), list_height.min(area.height.saturating_sub(list_y - area.y)));
|
||||
|
||||
// Render ASCII art
|
||||
ascii_widget.render(ascii_area, buf);
|
||||
|
||||
// Palette for menu flair
|
||||
let palette = [
|
||||
Color::LightGreen,
|
||||
Color::LightCyan,
|
||||
Color::LightBlue,
|
||||
Color::LightMagenta,
|
||||
Color::LightYellow,
|
||||
Color::LightRed,
|
||||
];
|
||||
|
||||
let items: Vec<ListItem> = upper_labels
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, label)| {
|
||||
let marker = if i == selected { '»' } else { ' ' };
|
||||
let padded = format!("{:<width$}", label, width = max_len as usize);
|
||||
let line = format!("{} {}", marker, padded);
|
||||
let style = Style::default().fg(palette[i % palette.len()]).add_modifier(Modifier::BOLD);
|
||||
ListItem::new(Span::styled(line, style))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let list = List::new(items);
|
||||
ratatui::widgets::StatefulWidget::render(list, list_area, buf, &mut state.list_state);
|
||||
}
|
||||
|
||||
fn run_app(terminal: &mut ratatui::DefaultTerminal) -> color_eyre::Result<()> {
|
||||
let mut app_state = AppState::Start(StartMenuState::new());
|
||||
let mut last_frame_time = Instant::now();
|
||||
let target_frame_duration = std::time::Duration::from_millis(33); // ~30 FPS
|
||||
|
||||
while !matches!(app_state, AppState::Exit) {
|
||||
let now = Instant::now();
|
||||
let dt = now - last_frame_time;
|
||||
last_frame_time = now;
|
||||
|
||||
terminal.draw(|f| match &mut app_state {
|
||||
AppState::Start(menu) => render_start_screen(menu, f.area(), f.buffer_mut()),
|
||||
AppState::Playing(game) => f.render_widget(&mut *game, f.area()),
|
||||
AppState::Exit => {}
|
||||
})?;
|
||||
|
||||
// Advance game if playing
|
||||
if let AppState::Playing(game) = &mut app_state {
|
||||
game.run(dt.as_secs_f64());
|
||||
if game.is_exit_intended() {
|
||||
app_state = AppState::Start(StartMenuState::new());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// handle input
|
||||
let poll_timeout = std::cmp::min(dt, target_frame_duration);
|
||||
if event::poll(poll_timeout)? {
|
||||
if let Event::Key(key) = event::read()? {
|
||||
if key.kind == KeyEventKind::Press {
|
||||
match key.code {
|
||||
// global exit via Ctrl+C
|
||||
KeyCode::Char('c') | KeyCode::Char('C') if key.modifiers == KeyModifiers::CONTROL => {
|
||||
app_state = AppState::Exit;
|
||||
}
|
||||
|
||||
// state-specific input handling
|
||||
_ => app_state = match app_state {
|
||||
AppState::Start(mut menu) => {
|
||||
handle_start_input(&mut menu, key).unwrap_or(AppState::Start(menu))
|
||||
}
|
||||
AppState::Playing(mut game) => {
|
||||
game.handle_game_input(key);
|
||||
AppState::Playing(game)
|
||||
}
|
||||
AppState::Exit => AppState::Exit,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cap frame rate
|
||||
let frame_duration = last_frame_time.elapsed();
|
||||
if frame_duration < target_frame_duration {
|
||||
thread::sleep(target_frame_duration - frame_duration);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ascii_art_cells() -> AsciiCells {
|
||||
let art = r#"
|
||||
,, ,, ,,
|
||||
*MM db *MM `7MM
|
||||
MM MM MM
|
||||
MM,dMMb.`7MM `7MMpMMMb. MM,dMMb.`7Mb,od8 .gP"Ya ,6"Yb. MM ,MP'
|
||||
MM `Mb MM MM MM MM `Mb MM' "',M' Yb 8) MM MM ;Y
|
||||
MM M8 MM MM MM MM M8 MM 8M"""""" ,pm9MM MM;Mm
|
||||
MM. ,M9 MM MM MM MM. ,M9 MM YM. , 8M MM MM `Mb.
|
||||
P^YbmdP'.JMML..JMML JMML.P^YbmdP'.JMML. `Mbmmd' `Moo9^Yo..JMML. YA.
|
||||
"#;
|
||||
|
||||
let colors = r#"
|
||||
,, ,, ,,
|
||||
*MM db *MM `7MM
|
||||
MM MM MM
|
||||
MM,dMMb.`7MM `7MMpMMMb. MM,dMMb.`7Mb,od8 .gP"Ya ,6"Yb. MM ,MP'
|
||||
MM `Mb MM MM MM MM `Mb MM' "',M' Yb 8) MM MM ;Y
|
||||
MM M8 MM MM MM MM M8 MM 8M"""""" ,pm9MM MM;Mm
|
||||
MM. ,M9 MM MM MM MM. ,M9 MM YM. , 8M MM MM `Mb.
|
||||
P^YbmdP'.JMML..JMML JMML.P^YbmdP'.JMML. `Mbmmd' `Moo9^Yo..JMML. YA.
|
||||
"#;
|
||||
|
||||
let color_map = HashMap::from([
|
||||
('M', Color::White),
|
||||
('b', Color::LightYellow),
|
||||
('d', Color::LightCyan),
|
||||
('Y', Color::LightGreen),
|
||||
('8', Color::LightMagenta),
|
||||
('*', Color::Magenta),
|
||||
('`', Color::Cyan),
|
||||
('6', Color::Green),
|
||||
('9', Color::Red),
|
||||
('(', Color::Blue),
|
||||
(')', Color::Blue),
|
||||
(' ', Color::Black),
|
||||
]);
|
||||
|
||||
let default_color = Color::LightBlue;
|
||||
AsciiCells::from(art.to_string(), colors.to_string(), &color_map, default_color)
|
||||
}
|
||||
@ -8,15 +8,6 @@ pub trait WidgetRef {
|
||||
|
||||
pub trait MainScreenWidget: WidgetRef {
|
||||
fn run(&mut self, dt: f64) -> ();
|
||||
fn handle_input(&mut self, input: KeyEvent) -> ();
|
||||
fn handle_input(&mut self, input: KeyEvent);
|
||||
fn is_exit_intended(&self) -> bool;
|
||||
|
||||
fn get_name(&self) -> String {
|
||||
let type_name = std::any::type_name::<Self>();
|
||||
type_name.split("::").last().unwrap_or("Unknown").to_string()
|
||||
}
|
||||
|
||||
fn get_overview(&self) -> String {
|
||||
format!("You are here: {}. The overview is not implemented.", self.get_name())
|
||||
}
|
||||
}
|
||||
36
src/utils.rs
36
src/utils.rs
@ -2,18 +2,6 @@ use ratatui::layout::Flex;
|
||||
use ratatui::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub trait ToDuration {
|
||||
/// Convert a number to a [`std::time::Duration`].
|
||||
fn milliseconds(&self) -> std::time::Duration;
|
||||
}
|
||||
|
||||
impl ToDuration for u64 {
|
||||
/// Convert a number to a [`std::time::Duration`].
|
||||
fn milliseconds(&self) -> std::time::Duration {
|
||||
std::time::Duration::from_millis(*self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AsciiCell {
|
||||
pub ch: char,
|
||||
pub x: u16,
|
||||
@ -56,10 +44,6 @@ pub struct AsciiCells {
|
||||
}
|
||||
|
||||
impl AsciiCells {
|
||||
pub fn new(cells: Vec<AsciiCell>) -> Self {
|
||||
Self { cells }
|
||||
}
|
||||
|
||||
pub fn from(
|
||||
art: String,
|
||||
color_map_str: String,
|
||||
@ -76,15 +60,6 @@ impl AsciiCells {
|
||||
pub fn get_height(&self) -> u16 {
|
||||
self.cells.iter().map(|cell| cell.y).max().unwrap_or(0) + 1
|
||||
}
|
||||
|
||||
pub fn get_centered_area(&self, area: Rect) -> Rect {
|
||||
let width = self.get_width();
|
||||
let height = self.get_height();
|
||||
let x_offset = (area.width.saturating_sub(width)) / 2;
|
||||
let y_offset = (area.height.saturating_sub(height)) / 2;
|
||||
|
||||
Rect::new(area.x + x_offset, area.y + y_offset, width, height)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AsciiArtWidget {
|
||||
@ -112,17 +87,6 @@ impl Widget for AsciiArtWidget {
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer_to_string(buf: &Buffer) -> String {
|
||||
(0..buf.area.height)
|
||||
.map(|y| {
|
||||
(0..buf.area.width)
|
||||
.map(|x| buf[(x, y)].symbol())
|
||||
.collect::<String>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
pub fn center(area: Rect, horizontal: Constraint) -> Rect {
|
||||
let [area] = Layout::horizontal([horizontal]).flex(Flex::Center).areas(area);
|
||||
let area = vertically_center(area);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user