added config file
This commit is contained in:
parent
0918ffea90
commit
78f5ba7ac1
|
@ -32,6 +32,37 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
|
@ -46,8 +77,11 @@ name = "kk"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"termion",
|
||||
"tokio",
|
||||
"toml",
|
||||
"xdg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -174,12 +208,43 @@ dependencies = [
|
|||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
|
@ -228,6 +293,26 @@ dependencies = [
|
|||
"redox_termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.24.1"
|
||||
|
@ -259,6 +344,15 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
|
@ -349,3 +443,12 @@ name = "windows_x86_64_msvc"
|
|||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
[[package]]
|
||||
name = "xdg"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
]
|
||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -5,10 +5,23 @@ edition = "2021"
|
|||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
# [profile.release.build-override]
|
||||
# opt-level = 3
|
||||
# lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
# misskey = "0.2.0"
|
||||
termion = "2.0.1"
|
||||
xdg = "2.4.1"
|
||||
toml = "0.5.10"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["std", "derive"]
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.24.1"
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
use std::fs::{self, File};
|
||||
|
||||
use crate::display::theme::Theme;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub auth: Auth,
|
||||
pub theme: Theme,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct Auth {
|
||||
hostname: Option<String>,
|
||||
token: Option<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load_or_create() -> Result<Self, anyhow::Error> {
|
||||
const CONFIG_FILENAME: &str = "kk.toml";
|
||||
let xdg_dirs = xdg::BaseDirectories::with_prefix("kk")?;
|
||||
let cfg_filename =
|
||||
xdg_dirs.place_config_file(CONFIG_FILENAME)?;
|
||||
Ok(match fs::read(&cfg_filename) {
|
||||
Ok(f) => toml::from_slice(&f)?,
|
||||
Err(_) => {
|
||||
// Save default file
|
||||
let cfg = Self::default();
|
||||
let fmt_cfg = toml::to_string_pretty(&cfg)?;
|
||||
fs::write(cfg_filename, fmt_cfg)?;
|
||||
cfg
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -45,7 +45,7 @@ impl Body {
|
|||
"{hide}{clear}{fr}{cursor}",
|
||||
clear = clear::All,
|
||||
hide = cursor::Hide,
|
||||
fr = env.frame.frame_str(env.theme.colors.primary),
|
||||
fr = env.frame.frame_str(env.theme.windows.primary),
|
||||
cursor = env.frame.goto(0, 0),
|
||||
)?;
|
||||
screen.flush()?;
|
||||
|
@ -240,6 +240,9 @@ impl SigninPage {
|
|||
|
||||
#[inline]
|
||||
fn char(&mut self, c: char) {
|
||||
if ['\t', '\n', '\r'].contains(&c) {
|
||||
return;
|
||||
}
|
||||
match self.cursor {
|
||||
SigninCursorLocation::Hostname => {
|
||||
self.hostname.push(c);
|
||||
|
@ -264,13 +267,7 @@ impl Page for SigninPage {
|
|||
termion::event::Key::Char(c) => self.char(c),
|
||||
termion::event::Key::Up => self.previous(),
|
||||
termion::event::Key::Down => self.next(),
|
||||
termion::event::Key::Backspace => {
|
||||
if let SigninCursorLocation::Hostname =
|
||||
self.cursor
|
||||
{
|
||||
self.hostname.pop();
|
||||
}
|
||||
}
|
||||
termion::event::Key::Backspace => self.del(),
|
||||
termion::event::Key::Delete => {}
|
||||
termion::event::Key::Left => {}
|
||||
termion::event::Key::Right => {}
|
||||
|
@ -284,10 +281,10 @@ impl Page for SigninPage {
|
|||
screen,
|
||||
"{}{}{}",
|
||||
env.primary_frame(),
|
||||
fr.frame_str(env.theme.colors.subwin),
|
||||
fr.frame_str(env.theme.windows.subwin),
|
||||
fr.make(self.draw(
|
||||
env.theme.colors.highlight,
|
||||
env.theme.colors.subwin
|
||||
env.theme.windows.highlight,
|
||||
env.theme.windows.subwin
|
||||
))
|
||||
)?;
|
||||
screen.flush()?;
|
||||
|
@ -301,24 +298,21 @@ impl Page for SigninPage {
|
|||
screen: &mut RawTerminal<Stdout>,
|
||||
) -> Result<()> {
|
||||
self.frame = Some(env.frame.sub(
|
||||
env.theme.colors.subframe,
|
||||
super::frame::FrameSize::ByAbsolute(70, 15),
|
||||
1,
|
||||
env.theme.frames.inner,
|
||||
super::frame::FrameSize::ByAbsolute(70, 10),
|
||||
));
|
||||
write!(
|
||||
screen,
|
||||
"{theme}{clear}{frame}{subframe}{content}{hide_cursor}",
|
||||
theme = env.theme.frame(),
|
||||
clear = clear::All,
|
||||
frame = env.frame.frame_str(env.theme.colors.primary),
|
||||
"{frame}{subframe}{content}{hide_cursor}",
|
||||
frame = env.frame.frame_str(env.theme.windows.primary),
|
||||
subframe = self
|
||||
.frame
|
||||
.unwrap()
|
||||
.frame_str(env.theme.colors.subwin),
|
||||
.frame_str(env.theme.windows.subwin),
|
||||
hide_cursor = cursor::Hide,
|
||||
content = self.frame.unwrap().make(self.draw(
|
||||
env.theme.colors.highlight,
|
||||
env.theme.colors.subwin
|
||||
env.theme.windows.highlight,
|
||||
env.theme.windows.subwin
|
||||
))
|
||||
)?;
|
||||
screen.flush()?;
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
use termion::cursor;
|
||||
|
||||
const ESTIMATED_FRAME_BIT_SIZE: usize = 11;
|
||||
|
||||
use crate::display::compose::Text;
|
||||
|
||||
use super::{
|
||||
compose::{Component, Components},
|
||||
theme::ColorSet,
|
||||
theme::{self, ColorSet},
|
||||
};
|
||||
|
||||
const FRAME_CHAR_HORIZONTAL: char = ' ';
|
||||
const FRAME_CHAR_VERTICAL: char = ' ';
|
||||
|
||||
pub enum FrameSize {
|
||||
ByAbsolute(u16, u16),
|
||||
ByPercent(u16, u16),
|
||||
|
@ -22,36 +15,30 @@ pub struct Frame {
|
|||
// Both are (w, h)
|
||||
start: (u16, u16),
|
||||
end: (u16, u16),
|
||||
border: u16,
|
||||
theme: ColorSet,
|
||||
theme: theme::Frame,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
#[inline(always)]
|
||||
pub fn sub(
|
||||
&self,
|
||||
theme: ColorSet,
|
||||
size: FrameSize,
|
||||
border: u16,
|
||||
) -> Frame {
|
||||
pub fn sub(&self, theme: theme::Frame, size: FrameSize) -> Frame {
|
||||
let (p_width, p_height) = self.size();
|
||||
let sub_size = size.abs_size((
|
||||
p_width - self.border * 2,
|
||||
p_height - self.border * 2,
|
||||
p_width - self.theme.width * 2,
|
||||
p_height - self.theme.width * 2,
|
||||
));
|
||||
self.child_centered(sub_size, border, theme)
|
||||
self.child_centered(sub_size, theme)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn goto(&self, x: u16, y: u16) -> String {
|
||||
cursor::Goto(
|
||||
self.border.max(
|
||||
(self.border + self.start.0 + x)
|
||||
.min(self.end.0 - self.border),
|
||||
self.theme.width.max(
|
||||
(self.theme.width + self.start.0 + x)
|
||||
.min(self.end.0 - self.theme.width),
|
||||
),
|
||||
self.border.max(
|
||||
(self.border + self.start.1 + y)
|
||||
.min(self.end.1 - self.border),
|
||||
self.theme.width.max(
|
||||
(self.theme.width + self.start.1 + y)
|
||||
.min(self.end.1 - self.theme.width),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
|
@ -86,14 +73,14 @@ impl Frame {
|
|||
#[inline(always)]
|
||||
fn write_centered_clear(&self, s: &str) -> Component {
|
||||
let width = self.size().0;
|
||||
let limit = width - self.border * 2;
|
||||
let limit = width - self.theme.width * 2;
|
||||
let s = if s.len() > limit as usize {
|
||||
&s[..limit as usize]
|
||||
} else {
|
||||
s
|
||||
};
|
||||
let len = s.len() as u16;
|
||||
let base_size = ((width - len) / 2) - self.border;
|
||||
let base_size = ((width - len) / 2) - self.theme.width;
|
||||
Component::String(super::compose::Text::PaddedBothSides(
|
||||
base_size + ((width - len) % 2),
|
||||
s.into(),
|
||||
|
@ -102,12 +89,14 @@ impl Frame {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_frame_line(&self, y: u16, length: u16) -> Components {
|
||||
vec![
|
||||
self.goto_internal(0, y),
|
||||
Component::Repeated(FRAME_CHAR_HORIZONTAL, length),
|
||||
]
|
||||
.into()
|
||||
fn write_frame_line(
|
||||
&self,
|
||||
y: u16,
|
||||
length: u16,
|
||||
c: char,
|
||||
) -> Components {
|
||||
vec![self.goto_internal(0, y), Component::Repeated(c, length)]
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn make(&self, comp: Components) -> String {
|
||||
|
@ -123,31 +112,31 @@ impl Frame {
|
|||
#[inline(always)]
|
||||
pub fn inner_size(&self) -> (u16, u16) {
|
||||
(
|
||||
self.end.0 - self.start.0 - self.border * 2,
|
||||
self.end.1 - self.start.1 - self.border * 2,
|
||||
self.end.0 - self.start.0 - self.theme.width * 2,
|
||||
self.end.1 - self.start.1 - self.theme.width * 2,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_terminal_size(
|
||||
theme: ColorSet,
|
||||
width: u16,
|
||||
theme: theme::Frame,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let term = termion::terminal_size()?;
|
||||
Ok(Self::from_bottom_right(term, term, width, theme))
|
||||
Ok(Self::from_bottom_right(term, term, theme))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn child_centered(
|
||||
&self,
|
||||
(pos_w, pos_h): (u16, u16),
|
||||
border: u16,
|
||||
theme: ColorSet,
|
||||
theme: theme::Frame,
|
||||
) -> Frame {
|
||||
let parent_offset = self.border / 2;
|
||||
let parent_offset = self.theme.width / 2;
|
||||
let (parent_w, parent_h) = self.size();
|
||||
let (parent_w, parent_h) =
|
||||
(parent_w - self.border, parent_h - self.border);
|
||||
let (parent_w, parent_h) = (
|
||||
parent_w - self.theme.width,
|
||||
parent_h - self.theme.width,
|
||||
);
|
||||
let start = (
|
||||
1.max(
|
||||
(parent_w - pos_w) / 2
|
||||
|
@ -163,7 +152,6 @@ impl Frame {
|
|||
let frame = Self {
|
||||
end: (start.0 + pos_w, start.1 + pos_h),
|
||||
start,
|
||||
border,
|
||||
theme,
|
||||
};
|
||||
frame
|
||||
|
@ -173,21 +161,19 @@ impl Frame {
|
|||
fn from_bottom_right(
|
||||
(pos_w, pos_h): (u16, u16),
|
||||
(term_w, term_h): (u16, u16),
|
||||
border: u16,
|
||||
theme: ColorSet,
|
||||
theme: theme::Frame,
|
||||
) -> Self {
|
||||
let start = (1.max(term_w - pos_w), 1.max(term_h - pos_h));
|
||||
let frame = Self {
|
||||
end: (start.0 + pos_w, start.1 + pos_h),
|
||||
start,
|
||||
border,
|
||||
theme,
|
||||
};
|
||||
if frame.start.0 > frame.end.0 || frame.start.1 > frame.end.1
|
||||
{
|
||||
eprintln!(
|
||||
"pos_w {}, pos_h {}, term_w {}, term_h {}, border {}",
|
||||
pos_w, pos_h, term_w, term_h, border
|
||||
pos_w, pos_h, term_w, term_h, theme.width,
|
||||
);
|
||||
panic!(
|
||||
"start.0: {} end.0: {}, start.1: {}, end.1: {}",
|
||||
|
@ -211,49 +197,66 @@ impl Frame {
|
|||
pub fn compose(&self, body_theme: ColorSet) -> Components {
|
||||
let body_theme = Component::Internal(body_theme.to_string());
|
||||
let (w_len, h_len) = self.size();
|
||||
let frame_theme = Component::Internal(self.theme.to_string());
|
||||
let frame_theme =
|
||||
Component::Internal(self.theme.color.to_string());
|
||||
|
||||
let body_clear =
|
||||
Component::Internal(self.make(Into::<Components>::into(
|
||||
vec![body_theme.clone(), Component::Padding(w_len)],
|
||||
)));
|
||||
|
||||
let border =
|
||||
Component::Repeated(FRAME_CHAR_VERTICAL, self.border);
|
||||
let border_left = Component::Repeated(
|
||||
self.theme.frame_chars.left,
|
||||
self.theme.width,
|
||||
);
|
||||
let border_right = Component::Repeated(
|
||||
self.theme.frame_chars.right,
|
||||
self.theme.width,
|
||||
);
|
||||
// This seems cursed but, I assure you, it's fine
|
||||
vec![vec![frame_theme.clone()]]
|
||||
.into_iter()
|
||||
// Top horizontal borders
|
||||
.chain(
|
||||
(0..self.border)
|
||||
.into_iter()
|
||||
.map(|y: u16| self.write_frame_line(y, w_len).0),
|
||||
)
|
||||
.chain((0..self.theme.width).into_iter().map(|y: u16| {
|
||||
self.write_frame_line(
|
||||
y,
|
||||
w_len,
|
||||
self.theme.frame_chars.top,
|
||||
)
|
||||
.0
|
||||
}))
|
||||
// Frame body + vertical frame borders
|
||||
.chain(
|
||||
(self.border..h_len - self.border).into_iter().map(
|
||||
|y: u16| {
|
||||
(self.theme.width..h_len - self.theme.width)
|
||||
.into_iter()
|
||||
.map(|y: u16| {
|
||||
let left = self.goto_internal(0, y);
|
||||
let right = self
|
||||
.goto_internal(w_len - self.border, y);
|
||||
let right = self.goto_internal(
|
||||
w_len - self.theme.width,
|
||||
y,
|
||||
);
|
||||
vec![
|
||||
left.clone(),
|
||||
body_clear.clone(),
|
||||
frame_theme.clone(),
|
||||
left,
|
||||
border.clone(),
|
||||
border_left.clone(),
|
||||
right,
|
||||
border.clone(),
|
||||
border_right.clone(),
|
||||
]
|
||||
},
|
||||
),
|
||||
}),
|
||||
)
|
||||
// Bottom horizontal border
|
||||
.chain(
|
||||
(h_len - self.border..h_len)
|
||||
.into_iter()
|
||||
.map(|y: u16| self.write_frame_line(y, w_len).0),
|
||||
)
|
||||
.chain((h_len - self.theme.width..h_len).into_iter().map(
|
||||
|y: u16| {
|
||||
self.write_frame_line(
|
||||
y,
|
||||
w_len,
|
||||
self.theme.frame_chars.bottom,
|
||||
)
|
||||
.0
|
||||
},
|
||||
))
|
||||
// Theme reset + move to beginning of inside frame
|
||||
.chain(
|
||||
vec![vec![Component::Goto(0, 0), body_theme]]
|
||||
|
|
|
@ -89,25 +89,22 @@ pub struct Environment {
|
|||
|
||||
impl Into<String> for Environment {
|
||||
fn into(self) -> String {
|
||||
self.frame.frame_str(self.theme.colors.primary)
|
||||
self.frame.frame_str(self.theme.windows.primary)
|
||||
}
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
fn initial_must(theme: Theme) -> Self {
|
||||
let w: u16 = std::env::var("FRAME_WIDTH")
|
||||
.map(|f| f.parse().unwrap_or(1))
|
||||
.unwrap_or(1);
|
||||
Self {
|
||||
theme,
|
||||
frame: Frame::from_terminal_size(theme.colors.frame, w)
|
||||
frame: Frame::from_terminal_size(theme.frames.outer)
|
||||
.expect("could not get terminal size"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn primary_frame(&self) -> String {
|
||||
self.frame.frame_str(self.theme.colors.primary)
|
||||
self.frame.frame_str(self.theme.windows.primary)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +156,9 @@ impl Screen {
|
|||
write!(
|
||||
scr,
|
||||
"{color}{clear}{show_cursor}{move_to_start}",
|
||||
color = Into::<Color>::into("#000000").bg_string(),
|
||||
color = TryInto::<Color>::try_into("#000000")
|
||||
.unwrap()
|
||||
.bg_string(),
|
||||
move_to_start = cursor::Goto(1, 1),
|
||||
clear = clear::All,
|
||||
show_cursor = cursor::Show,
|
||||
|
|
|
@ -1,23 +1,46 @@
|
|||
use serde::{de::Visitor, Deserialize, Serialize};
|
||||
use std::ops::Deref;
|
||||
|
||||
use termion::color;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct Theme {
|
||||
pub colors: Colors,
|
||||
pub windows: Colors,
|
||||
pub frames: Frames,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct ColorSet {
|
||||
pub fg: Color,
|
||||
pub bg: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct Frames {
|
||||
pub outer: Frame,
|
||||
pub inner: Frame,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct FrameChars {
|
||||
pub top: char,
|
||||
pub bottom: char,
|
||||
pub left: char,
|
||||
pub right: char,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct Frame {
|
||||
pub width: u16,
|
||||
pub frame_chars: FrameChars,
|
||||
pub color: ColorSet,
|
||||
}
|
||||
|
||||
impl Default for ColorSet {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
fg: "#ffffff".into(),
|
||||
bg: "#000000".into(),
|
||||
fg: "#ffffff".try_into().unwrap(),
|
||||
bg: "#000000".try_into().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,18 +52,71 @@ impl ToString for ColorSet {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct Colors {
|
||||
pub primary: ColorSet,
|
||||
pub frame: ColorSet,
|
||||
pub subframe: ColorSet,
|
||||
pub subwin: ColorSet,
|
||||
pub highlight: ColorSet,
|
||||
}
|
||||
impl Theme {}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Color(color::Rgb);
|
||||
|
||||
impl Serialize for Color {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let rgb = self.0;
|
||||
serializer.serialize_str(&format!(
|
||||
"#{:02X}{:02X}{:02X}",
|
||||
rgb.0, rgb.1, rgb.2,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Color {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct ColorCodeVisitor;
|
||||
impl<'de> Visitor<'de> for ColorCodeVisitor {
|
||||
fn expecting(
|
||||
&self,
|
||||
formatter: &mut std::fmt::Formatter,
|
||||
) -> std::fmt::Result {
|
||||
formatter.write_str("hex color code")
|
||||
}
|
||||
type Value = Color;
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
match TryInto::<Color>::try_into(v) {
|
||||
Ok(c) => Ok(c),
|
||||
Err(e) => Err(E::custom(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_string<E>(
|
||||
self,
|
||||
v: String,
|
||||
) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
match TryInto::<Color>::try_into(v) {
|
||||
Ok(c) => Ok(c),
|
||||
Err(e) => Err(E::custom(e.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_any(ColorCodeVisitor {})
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Color {
|
||||
type Target = color::Rgb;
|
||||
|
||||
|
@ -56,8 +132,10 @@ impl TryFrom<String> for Color {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Color {
|
||||
fn from(value: &str) -> Self {
|
||||
impl TryFrom<&str> for Color {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
if value.len() < 6 || value.len() > 7 {
|
||||
panic!("hex code length invalid: {}", value.len());
|
||||
}
|
||||
|
@ -65,40 +143,60 @@ impl From<&str> for Color {
|
|||
if value.starts_with('#') {
|
||||
i = 1;
|
||||
}
|
||||
Self(color::Rgb(
|
||||
Ok(Self(color::Rgb(
|
||||
u8::from_str_radix(&value[i..i + 2], 16)
|
||||
.expect("red hex"),
|
||||
.map_err(|_| anyhow::anyhow!("red hex"))?,
|
||||
u8::from_str_radix(&value[i + 2..i + 4], 16)
|
||||
.expect("green hex"),
|
||||
.map_err(|_| anyhow::anyhow!("green hex"))?,
|
||||
u8::from_str_radix(&value[i + 4..i + 6], 16)
|
||||
.expect("blue hex"),
|
||||
))
|
||||
.map_err(|_| anyhow::anyhow!("blue hex"))?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Theme {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
colors: Colors {
|
||||
windows: Colors {
|
||||
primary: ColorSet {
|
||||
fg: "#330033".into(),
|
||||
bg: "#a006d3".into(),
|
||||
},
|
||||
frame: ColorSet {
|
||||
fg: "#ffe6ff".into(),
|
||||
bg: "#9005c2".into(),
|
||||
},
|
||||
subframe: ColorSet {
|
||||
fg: "#ffe6ff".into(),
|
||||
bg: "#7003a0".into(),
|
||||
fg: "#330033".try_into().unwrap(),
|
||||
bg: "#a006d3".try_into().unwrap(),
|
||||
},
|
||||
highlight: ColorSet {
|
||||
fg: "#ffffff".into(),
|
||||
bg: "#0000ff".into(),
|
||||
fg: "#ffffff".try_into().unwrap(),
|
||||
bg: "#0000ff".try_into().unwrap(),
|
||||
},
|
||||
subwin: ColorSet {
|
||||
fg: "#ffffff".into(),
|
||||
bg: "#500170".into(),
|
||||
fg: "#ffffff".try_into().unwrap(),
|
||||
bg: "#500170".try_into().unwrap(),
|
||||
},
|
||||
},
|
||||
frames: Frames {
|
||||
outer: Frame {
|
||||
color: ColorSet {
|
||||
fg: "#ff36ff".try_into().unwrap(),
|
||||
bg: "#9005c2".try_into().unwrap(),
|
||||
},
|
||||
frame_chars: FrameChars {
|
||||
top: ' ',
|
||||
bottom: ' ',
|
||||
left: ' ',
|
||||
right: ' ',
|
||||
},
|
||||
width: 1,
|
||||
},
|
||||
inner: Frame {
|
||||
color: ColorSet {
|
||||
fg: "#ffe6ff".try_into().unwrap(),
|
||||
bg: "#7003a0".try_into().unwrap(),
|
||||
},
|
||||
frame_chars: FrameChars {
|
||||
top: ' ',
|
||||
bottom: ' ',
|
||||
left: ' ',
|
||||
right: ' ',
|
||||
},
|
||||
width: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -108,11 +206,11 @@ impl Default for Theme {
|
|||
impl Theme {
|
||||
#[inline(always)]
|
||||
pub fn primary(&self) -> String {
|
||||
self.colors.primary.to_string()
|
||||
self.windows.primary.to_string()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn frame(&self) -> String {
|
||||
self.colors.frame.to_string()
|
||||
self.frames.outer.color.to_string()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,14 @@ use display::theme::Theme;
|
|||
use crate::display::Screen;
|
||||
|
||||
extern crate termion;
|
||||
mod cfg;
|
||||
mod display;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), anyhow::Error> {
|
||||
let config = cfg::Config::load_or_create()?;
|
||||
{
|
||||
let theme = Theme::default();
|
||||
let x = Screen::new(theme).unwrap();
|
||||
let x = Screen::new(config.theme).unwrap();
|
||||
x.start().await?;
|
||||
}
|
||||
process::exit(0);
|
||||
|
|
Loading…
Reference in New Issue