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