mirror of
https://github.com/talwat/lowfi
synced 2025-12-19 05:03:34 +00:00
style: split off titlebar into seperate file
This commit is contained in:
parent
4b2fac3529
commit
287a61ca80
@ -1,6 +1,6 @@
|
||||
/// Handles communication between different parts of the program.
|
||||
#[allow(dead_code, reason = "this code may not be dead depending on features")]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum Message {
|
||||
/// Deliberate user request to go to the next song, also sent when the
|
||||
/// song is over by the waiter.
|
||||
|
||||
@ -65,12 +65,12 @@ mod window {
|
||||
#[test]
|
||||
fn new_border_strings() {
|
||||
let w = Window::new(10, false);
|
||||
assert!(w.borders[0].starts_with('┌'));
|
||||
assert!(w.borders[1].starts_with('└'));
|
||||
assert!(w.titlebar.content.starts_with('┌'));
|
||||
assert!(w.statusbar.starts_with('└'));
|
||||
|
||||
let w2 = Window::new(5, true);
|
||||
assert!(w2.borders[0].is_empty());
|
||||
assert!(w2.borders[1].is_empty());
|
||||
assert!(w2.titlebar.content.is_empty());
|
||||
assert!(w2.statusbar.is_empty());
|
||||
}
|
||||
|
||||
fn sided(text: &str) -> String {
|
||||
@ -114,7 +114,7 @@ mod window {
|
||||
#[test]
|
||||
fn zero_width_window() {
|
||||
let w = Window::new(0, false);
|
||||
assert!(!w.borders[0].is_empty());
|
||||
assert!(!w.titlebar.content.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ pub enum Kind {
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("{kind} (track: {track:?})")]
|
||||
#[error("{kind}{}", self.track.as_ref().map_or(String::new(), |t| format!(" (track: {t:?}) ")))]
|
||||
pub struct Error {
|
||||
pub track: Option<String>,
|
||||
pub kind: Kind,
|
||||
|
||||
@ -6,9 +6,11 @@ use std::{env, time::Duration};
|
||||
|
||||
pub mod clock;
|
||||
pub mod components;
|
||||
pub mod titlebar;
|
||||
pub mod window;
|
||||
|
||||
pub use clock::Clock;
|
||||
pub use titlebar::TitleBar;
|
||||
pub use window::Window;
|
||||
|
||||
/// UI-specific parameters and options.
|
||||
@ -76,12 +78,12 @@ impl TryFrom<&Args> for Params {
|
||||
/// All of the state related to the interface itself,
|
||||
/// which is displayed each frame to the standard output.
|
||||
pub struct Interface {
|
||||
/// The [`Window`] to render to.
|
||||
pub(crate) window: Window,
|
||||
|
||||
/// The interval to wait between frames.
|
||||
interval: tokio::time::Interval,
|
||||
|
||||
/// The [`Window`] to render to.
|
||||
window: Window,
|
||||
|
||||
/// The visual clock, which is [`None`] if it has
|
||||
/// been disabled by the [`Params`].
|
||||
clock: Option<Clock>,
|
||||
|
||||
@ -19,15 +19,14 @@ impl Clock {
|
||||
/// is somewhat expensive because of timezones.
|
||||
pub fn update(&mut self, window: &mut Window) {
|
||||
if self.0.elapsed().as_millis() >= 200 {
|
||||
window.display(Self::now(), 8);
|
||||
window.titlebar.display(Self::now());
|
||||
self.0 = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
/// Simply creates a new clock, and renders it's initial state to the top of the window.
|
||||
pub fn new(window: &mut Window) -> Self {
|
||||
window.display(Self::now(), 8);
|
||||
|
||||
window.titlebar.display(Self::now());
|
||||
Self(Instant::now())
|
||||
}
|
||||
}
|
||||
|
||||
62
src/ui/interface/titlebar.rs
Normal file
62
src/ui/interface/titlebar.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use std::fmt::Display;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
/// The titlebar, which is essentially the entire top of the window.
|
||||
///
|
||||
/// The struct offers a basic API for displaying messages to it.
|
||||
pub struct TitleBar {
|
||||
/// The actual content of the titlebar.
|
||||
pub(crate) content: String,
|
||||
|
||||
/// The width of the titlebar, identical to the width of the parent window.
|
||||
width: usize,
|
||||
|
||||
/// Whether to render a bordered or borderless titlebar.
|
||||
borderless: bool,
|
||||
}
|
||||
|
||||
impl TitleBar {
|
||||
/// Returns a blank default titlebar string for use elsewhere.
|
||||
fn blank_content(width: usize, borderless: bool) -> String {
|
||||
if borderless {
|
||||
String::new()
|
||||
} else {
|
||||
let middle = "─".repeat(width + 2);
|
||||
format!("┌{middle}┐")
|
||||
}
|
||||
}
|
||||
|
||||
/// Empties the contents of the titlebar.
|
||||
pub fn empty(&mut self) {
|
||||
self.content = Self::blank_content(self.width, self.borderless);
|
||||
}
|
||||
|
||||
/// Adds text to the top of the titlebar.
|
||||
pub fn display(&mut self, display: impl Display) {
|
||||
let mut display = display.to_string();
|
||||
let graphemes = display.graphemes(true);
|
||||
let mut len = graphemes.clone().count();
|
||||
let inner = self.width - 2;
|
||||
|
||||
if len > inner {
|
||||
display = format!("{}...", graphemes.take(inner - 3).collect::<String>());
|
||||
len = inner;
|
||||
}
|
||||
|
||||
let (prefix, middle, suffix) = if self.borderless {
|
||||
(" ", " ", " ")
|
||||
} else {
|
||||
("┌─", "─", "─┐")
|
||||
};
|
||||
|
||||
self.content = format!("{prefix} {display} {}{suffix}", middle.repeat(inner - len));
|
||||
}
|
||||
|
||||
pub fn new(width: usize, borderless: bool) -> Self {
|
||||
Self {
|
||||
content: Self::blank_content(width, borderless),
|
||||
width,
|
||||
borderless,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,6 @@
|
||||
use std::{
|
||||
fmt::Display,
|
||||
io::{stdout, Stdout},
|
||||
};
|
||||
use std::io::{stdout, Stdout};
|
||||
|
||||
use crate::ui::{self, interface::TitleBar};
|
||||
use crossterm::{
|
||||
cursor::{MoveToColumn, MoveUp},
|
||||
style::{Print, Stylize as _},
|
||||
@ -11,8 +9,6 @@ use crossterm::{
|
||||
use std::fmt::Write as _;
|
||||
use unicode_segmentation::UnicodeSegmentation as _;
|
||||
|
||||
use crate::ui;
|
||||
|
||||
/// Represents an abstraction for drawing the actual lowfi window itself.
|
||||
///
|
||||
/// The main purpose of this struct is just to add the fancy border,
|
||||
@ -21,11 +17,11 @@ pub struct Window {
|
||||
/// Whether or not to include borders in the output.
|
||||
borderless: bool,
|
||||
|
||||
/// The top & bottom borders, which are here since they can be
|
||||
/// prerendered, as they don't change every single draw.
|
||||
///
|
||||
/// If the option to not include borders is set, these will just be empty [String]s.
|
||||
pub(crate) borders: [String; 2],
|
||||
/// The titlebar of this window.
|
||||
pub titlebar: TitleBar,
|
||||
|
||||
/// The status (bottom) bar of the window, which for now shouldn't change since initialization.
|
||||
pub(crate) statusbar: String,
|
||||
|
||||
/// The inner width of the window.
|
||||
width: usize,
|
||||
@ -40,28 +36,22 @@ impl Window {
|
||||
/// * `width` - Inner width of the window.
|
||||
/// * `borderless` - Whether to include borders in the window, or not.
|
||||
pub fn new(width: usize, borderless: bool) -> Self {
|
||||
let borders = if borderless {
|
||||
[String::new(), String::new()]
|
||||
let statusbar = if borderless {
|
||||
String::new()
|
||||
} else {
|
||||
let middle = "─".repeat(width + 2);
|
||||
|
||||
[format!("┌{middle}┐"), format!("└{middle}┘")]
|
||||
format!("└{middle}┘")
|
||||
};
|
||||
|
||||
Self {
|
||||
borders,
|
||||
statusbar,
|
||||
borderless,
|
||||
width,
|
||||
titlebar: TitleBar::new(width, borderless),
|
||||
out: stdout(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds text to the top of the window.
|
||||
pub fn display(&mut self, display: impl Display, len: usize) {
|
||||
let new = format!("┌─ {} {}─┐", display, "─".repeat(self.width - len - 2));
|
||||
self.borders[0] = new;
|
||||
}
|
||||
|
||||
/// Renders the window itself, but doesn't actually draw it.
|
||||
///
|
||||
/// `testing` just determines whether to add special features
|
||||
@ -105,7 +95,7 @@ impl Window {
|
||||
Ok((
|
||||
format!(
|
||||
"{}{linefeed}{menu}{}{suffix}",
|
||||
self.borders[0], self.borders[1]
|
||||
self.titlebar.content, self.statusbar,
|
||||
),
|
||||
height,
|
||||
))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user