scrollbar

This commit is contained in:
MeexReay 2025-02-12 00:30:05 +03:00
parent 6868f0b3eb
commit 4518896f45
2 changed files with 86 additions and 11 deletions

View File

@ -1,10 +1,10 @@
use std::{cmp::{max, min}, error::Error, io::{stdout, Write}, sync::{atomic::{AtomicUsize, Ordering}, Arc, RwLock}, thread, time::{Duration, SystemTime}}; use std::{cmp::{max, min}, error::Error, io::{stdout, Write}, sync::{atomic::{AtomicUsize, Ordering}, Arc, RwLock}, thread, time::{Duration, SystemTime}};
use colored::{Color, Colorize}; use colored::{Color, Colorize};
use crossterm::{cursor::{MoveLeft, MoveRight}, event::{self, Event, KeyCode, KeyModifiers, MouseEventKind}, terminal::{disable_raw_mode, enable_raw_mode}}; use crossterm::{cursor::{MoveLeft, MoveRight}, event::{self, Event, KeyCode, KeyModifiers, MouseEventKind}, terminal::{self, disable_raw_mode, enable_raw_mode}};
use rand::random; use rand::random;
use crate::IP_REGEX; use crate::{util::string_chunks, IP_REGEX};
use super::{proto::read_messages, util::sanitize_text, COLORED_USERNAMES, DATE_REGEX, config::Context, proto::send_message}; use super::{proto::read_messages, util::sanitize_text, COLORED_USERNAMES, DATE_REGEX, config::Context, proto::send_message};
@ -88,11 +88,30 @@ Press enter to close")?;
} }
pub fn print_console(context: Arc<Context>, messages: Vec<String>, input: &str) -> Result<(), Box<dyn Error>> { pub fn print_console(ctx: Arc<Context>, messages: Vec<String>, input: &str) -> Result<(), Box<dyn Error>> {
let (width, height) = terminal::size()?;
let (width, height) = (width as usize, height as usize);
let scroll = ctx.scroll.load(Ordering::SeqCst);
let scroll = (1f64 - scroll as f64 / messages.len() as f64) * (height - 1) as f64;
let scroll = scroll as usize;
let text = format!( let text = format!(
"{}{}\r\n> {}", "{}\r\n> {}",
"\r\n".repeat(context.max_messages - messages.len()), messages[messages.len()-height-1..].into_iter()
messages.join("\r\n"), .flat_map(|o| string_chunks(&o, width as usize - 1))
.enumerate()
.map(|(i, (s, l))| {
format!("{}{}{}",
s,
" ".repeat(width - 1 - l),
if i == scroll {
"#"
} else {
"|"
}
)
}).collect::<Vec<String>>().join("\r\n"),
input input
); );
@ -333,6 +352,13 @@ fn poll_events(ctx: Arc<Context>) -> Result<(), Box<dyn Error>> {
write!(stdout(), "{}", &data).unwrap(); write!(stdout(), "{}", &data).unwrap();
stdout().lock().flush().unwrap(); stdout().lock().flush().unwrap();
}, },
Event::Resize(_, _) => {
print_console(
ctx.clone(),
messages.messages(),
&input.read().unwrap()
)?;
},
Event::Mouse(data) => { Event::Mouse(data) => {
match data.kind { match data.kind {
MouseEventKind::ScrollUp => { MouseEventKind::ScrollUp => {

View File

@ -1,13 +1,62 @@
use std::io::{stdin, stdout, BufRead, Write}; use std::{collections::HashSet, io::{stdin, stdout, BufRead, Write}, ops::Range};
use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
lazy_static! {
static ref ANSI_REGEX: Regex = Regex::new(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])").unwrap();
static ref CONTROL_CHARS_REGEX: Regex = Regex::new(r"[\x00-\x1F\x7F]").unwrap();
}
fn get_matches(regex: &Regex, text: &str) -> Vec<Range<usize>> {
regex.find_iter(text).map(|mat| mat.range()).collect()
}
pub fn string_chunks(text: &str, width: usize) -> Vec<(String, usize)> {
let mut norm: Vec<bool> = vec![true; text.chars().count()];
for range in get_matches(&ANSI_REGEX, text) {
for i in range {
if let Some(index) = text.char_indices().position(|x| x.0 == i) {
norm[index] = false;
}
}
}
for range in get_matches(&CONTROL_CHARS_REGEX, text) {
for i in range {
if let Some(index) = text.char_indices().position(|x| x.0 == i) {
norm[index] = false;
}
}
}
let mut now_chunk = String::new();
let mut chunks = Vec::new();
let mut length = 0;
for (i, b) in norm.iter().enumerate() {
if *b {
length += 1;
}
now_chunk.push(text.chars().skip(i).next().unwrap());
if length == width {
chunks.push((now_chunk.clone(), length));
now_chunk.clear();
length = 0;
}
}
if !now_chunk.is_empty() {
chunks.push((now_chunk.clone(), length));
}
chunks
}
pub fn sanitize_text(input: &str) -> String { pub fn sanitize_text(input: &str) -> String {
let ansi_regex = Regex::new(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])").unwrap(); let without_ansi = ANSI_REGEX.replace_all(input, "");
let control_chars_regex = Regex::new(r"[\x00-\x1F\x7F]").unwrap(); let cleaned_text = CONTROL_CHARS_REGEX.replace_all(&without_ansi, "");
let without_ansi = ansi_regex.replace_all(input, "");
let cleaned_text = control_chars_regex.replace_all(&without_ansi, "");
cleaned_text.into_owned() cleaned_text.into_owned()
} }