Skip to content

Commit 05230d3

Browse files
committed
Added dynamic re-centering
1 parent a0770fe commit 05230d3

File tree

6 files changed

+172
-18
lines changed

6 files changed

+172
-18
lines changed

Cargo.lock

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ license = "MIT"
77
[dependencies]
88
clap = { version = "4.5.47", features = ["derive"] }
99
console = "0.16.1"
10+
ctrlc = "3.5.0"
1011
dirs = "6.0.0"
1112
rand = "0.9.2"
1213
serde = { version = "1.0.219", features = ["derive"] }

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ Here you can customize:
7878
- `modes` - list of quote files to use (any `.toml` file in `~/.config/kotofetch/quotes/` or built-in)
7979
- `seed` - RNG seed for random quotes (`0` for random seed)
8080
- `centered` - center text (true/false)
81+
- `dynamic` - dynamic re-centering of the text (true/false)
8182

8283
Example `config.toml`:
8384
```toml
@@ -97,6 +98,7 @@ source = true
9798
modes = ["proverb", "anime"]
9899
seed = 0
99100
centered = true
101+
dynamic = false
100102
```
101103

102104
### Custom quotes

src/cli.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ pub struct Cli {
6767
// Center text
6868
#[arg(long)]
6969
pub centered: Option<bool>,
70+
71+
// Dynamic re-centering text
72+
#[arg(long)]
73+
pub dynamic: Option<bool>,
7074
}
7175

7276
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]

src/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub struct DisplayConfig {
3333
pub modes: Option<Vec<PathBuf>>,
3434
pub seed: Option<u64>,
3535
pub centered: Option<bool>,
36+
pub dynamic: Option<bool>,
3637
}
3738

3839
#[derive(Clone, Debug)]
@@ -52,6 +53,7 @@ pub struct RuntimeConfig {
5253
pub modes: Vec<PathBuf>,
5354
pub seed: u64,
5455
pub centered: bool,
56+
pub dynamic: bool,
5557
}
5658

5759
impl Default for RuntimeConfig {
@@ -74,9 +76,12 @@ impl Default for RuntimeConfig {
7476
PathBuf::from("proverb.toml"),
7577
PathBuf::from("haiku.toml"),
7678
PathBuf::from("anime.toml"),
79+
PathBuf::from("lyrics.toml"),
80+
PathBuf::from("yojijukugo.toml"),
7781
],
7882
seed: 0, // 0 = random
7983
centered: true,
84+
dynamic: false,
8085
}
8186
}
8287
}
@@ -161,6 +166,9 @@ pub fn make_runtime_config(user: Option<FileConfig>, cli: &crate::cli::Cli) -> R
161166
if let Some(c) = d.centered {
162167
r.centered = c;
163168
}
169+
if let Some(dc) = d.dynamic {
170+
r.dynamic = dc;
171+
}
164172
}
165173
}
166174

@@ -216,6 +224,9 @@ pub fn make_runtime_config(user: Option<FileConfig>, cli: &crate::cli::Cli) -> R
216224
if let Some(c) = cli.centered {
217225
r.centered = c;
218226
}
227+
if let Some(dc) = cli.dynamic {
228+
r.dynamic = dc;
229+
}
219230

220231
r
221232
}

src/display.rs

Lines changed: 118 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use crate::quotes::Quote;
44
use crate::quotes::QuotesFile;
55
use console::{Color, Style};
66
use rand::prelude::*;
7-
use std::fs;
7+
use std::sync::Arc;
8+
use std::sync::atomic::{AtomicBool, Ordering};
9+
use std::time::Duration;
10+
use std::{thread, fs, io::{self, Write}};
811
use term_size;
912
use textwrap::wrap;
1013
use unicode_width::UnicodeWidthStr;
@@ -350,6 +353,12 @@ fn print_boxed(
350353
}
351354
}
352355

356+
fn clear_screen() {
357+
// ANSI escape: clear entire screen and move cursor to top-left
358+
print!("\x1B[2J\x1B[H");
359+
let _ = io::stdout().flush();
360+
}
361+
353362
pub fn render(runtime: &RuntimeConfig, cli: &crate::cli::Cli) {
354363
// seed
355364
let seed = if cli.seed.unwrap_or(0) == 0 {
@@ -443,21 +452,112 @@ pub fn render(runtime: &RuntimeConfig, cli: &crate::cli::Cli) {
443452

444453
let border_color = color_from_hex(&runtime.border_color);
445454

446-
print_boxed(
447-
jap_lines,
448-
jap_style,
449-
runtime.horizontal_padding,
450-
runtime.vertical_padding,
451-
runtime.width,
452-
runtime.border,
453-
runtime.rounded_border,
454-
border_color,
455-
translation,
456-
show_translation,
457-
translation_style,
458-
quote.source.as_deref(),
459-
show_source,
460-
source_style,
461-
runtime.centered,
462-
);
455+
if runtime.dynamic {
456+
// Dynamic recentering mode
457+
let running = Arc::new(AtomicBool::new(true));
458+
let r = running.clone();
459+
460+
// Handle Ctrl+C gracefully
461+
ctrlc::set_handler(move || {
462+
r.store(false, Ordering::SeqCst);
463+
}).expect("Error setting Ctrl-C handler");
464+
465+
// Hide cursor
466+
print!("\x1B[?25l");
467+
io::stdout().flush().unwrap();
468+
469+
let mut last_size = term_size::dimensions();
470+
471+
while running.load(Ordering::SeqCst) {
472+
clear_screen();
473+
474+
let (_, term_h) = term_size::dimensions().unwrap_or((80, 24));
475+
476+
let mut vertical = 0;
477+
let mut horizontal = 0;
478+
479+
if runtime.border {
480+
vertical = runtime.vertical_padding;
481+
horizontal = runtime.horizontal_padding;
482+
}
483+
484+
// estimate how many lines the box will take (content + borders + padding)
485+
let content_lines = {
486+
let mut count = jap_lines.len();
487+
if show_translation { count += 1; }
488+
if show_source { count += 1; }
489+
// Add vertical padding and border lines
490+
count += vertical * 2;
491+
if runtime.border { count += 2; }
492+
count
493+
};
494+
495+
// Compute top blank lines to center vertically
496+
let top_blank = if term_h > content_lines {
497+
(term_h - content_lines) / 2
498+
} else {
499+
1
500+
};
501+
502+
// Print top spacing
503+
for _ in 0..top_blank {
504+
println!();
505+
}
506+
507+
// Render centered block
508+
print_boxed(
509+
jap_lines.clone(),
510+
jap_style.clone(),
511+
horizontal,
512+
vertical,
513+
runtime.width,
514+
runtime.border,
515+
runtime.rounded_border,
516+
border_color.clone(),
517+
translation,
518+
show_translation,
519+
translation_style.clone(),
520+
quote.source.as_deref(),
521+
show_source,
522+
source_style.clone(),
523+
runtime.centered,
524+
);
525+
526+
io::stdout().flush().unwrap();
527+
528+
// Sleep before checking for resize or exit
529+
thread::sleep(Duration::from_millis(200));
530+
531+
let current_size = term_size::dimensions();
532+
if current_size != last_size {
533+
last_size = current_size;
534+
clear_screen(); // redraw on resize
535+
}
536+
}
537+
538+
// Show cursor again before exiting
539+
print!("\x1B[?25h");
540+
io::stdout().flush().unwrap();
541+
542+
clear_screen(); // clean terminal on exit
543+
} else {
544+
// Normal static render
545+
print_boxed(
546+
jap_lines,
547+
jap_style,
548+
runtime.horizontal_padding,
549+
runtime.vertical_padding,
550+
runtime.width,
551+
runtime.border,
552+
runtime.rounded_border,
553+
border_color,
554+
translation,
555+
show_translation,
556+
translation_style,
557+
quote.source.as_deref(),
558+
show_source,
559+
source_style,
560+
runtime.centered,
561+
);
562+
}
463563
}

0 commit comments

Comments
 (0)