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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
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]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
|
@ -46,8 +77,11 @@ name = "kk"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"serde",
|
||||||
"termion",
|
"termion",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
|
"xdg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -174,12 +208,43 @@ dependencies = [
|
||||||
"redox_syscall",
|
"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]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
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]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -228,6 +293,26 @@ dependencies = [
|
||||||
"redox_termios",
|
"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]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.24.1"
|
version = "1.24.1"
|
||||||
|
@ -259,6 +344,15 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.5.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
@ -349,3 +443,12 @@ name = "windows_x86_64_msvc"
|
||||||
version = "0.42.0"
|
version = "0.42.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
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
|
# 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]
|
[dependencies]
|
||||||
anyhow = "1.0.68"
|
anyhow = "1.0.68"
|
||||||
# misskey = "0.2.0"
|
# misskey = "0.2.0"
|
||||||
termion = "2.0.1"
|
termion = "2.0.1"
|
||||||
|
xdg = "2.4.1"
|
||||||
|
toml = "0.5.10"
|
||||||
|
|
||||||
|
[dependencies.serde]
|
||||||
|
version = "1.0"
|
||||||
|
features = ["std", "derive"]
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
version = "1.24.1"
|
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}",
|
"{hide}{clear}{fr}{cursor}",
|
||||||
clear = clear::All,
|
clear = clear::All,
|
||||||
hide = cursor::Hide,
|
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),
|
cursor = env.frame.goto(0, 0),
|
||||||
)?;
|
)?;
|
||||||
screen.flush()?;
|
screen.flush()?;
|
||||||
|
@ -240,6 +240,9 @@ impl SigninPage {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn char(&mut self, c: char) {
|
fn char(&mut self, c: char) {
|
||||||
|
if ['\t', '\n', '\r'].contains(&c) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
match self.cursor {
|
match self.cursor {
|
||||||
SigninCursorLocation::Hostname => {
|
SigninCursorLocation::Hostname => {
|
||||||
self.hostname.push(c);
|
self.hostname.push(c);
|
||||||
|
@ -264,13 +267,7 @@ impl Page for SigninPage {
|
||||||
termion::event::Key::Char(c) => self.char(c),
|
termion::event::Key::Char(c) => self.char(c),
|
||||||
termion::event::Key::Up => self.previous(),
|
termion::event::Key::Up => self.previous(),
|
||||||
termion::event::Key::Down => self.next(),
|
termion::event::Key::Down => self.next(),
|
||||||
termion::event::Key::Backspace => {
|
termion::event::Key::Backspace => self.del(),
|
||||||
if let SigninCursorLocation::Hostname =
|
|
||||||
self.cursor
|
|
||||||
{
|
|
||||||
self.hostname.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
termion::event::Key::Delete => {}
|
termion::event::Key::Delete => {}
|
||||||
termion::event::Key::Left => {}
|
termion::event::Key::Left => {}
|
||||||
termion::event::Key::Right => {}
|
termion::event::Key::Right => {}
|
||||||
|
@ -284,10 +281,10 @@ impl Page for SigninPage {
|
||||||
screen,
|
screen,
|
||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
env.primary_frame(),
|
env.primary_frame(),
|
||||||
fr.frame_str(env.theme.colors.subwin),
|
fr.frame_str(env.theme.windows.subwin),
|
||||||
fr.make(self.draw(
|
fr.make(self.draw(
|
||||||
env.theme.colors.highlight,
|
env.theme.windows.highlight,
|
||||||
env.theme.colors.subwin
|
env.theme.windows.subwin
|
||||||
))
|
))
|
||||||
)?;
|
)?;
|
||||||
screen.flush()?;
|
screen.flush()?;
|
||||||
|
@ -301,24 +298,21 @@ impl Page for SigninPage {
|
||||||
screen: &mut RawTerminal<Stdout>,
|
screen: &mut RawTerminal<Stdout>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.frame = Some(env.frame.sub(
|
self.frame = Some(env.frame.sub(
|
||||||
env.theme.colors.subframe,
|
env.theme.frames.inner,
|
||||||
super::frame::FrameSize::ByAbsolute(70, 15),
|
super::frame::FrameSize::ByAbsolute(70, 10),
|
||||||
1,
|
|
||||||
));
|
));
|
||||||
write!(
|
write!(
|
||||||
screen,
|
screen,
|
||||||
"{theme}{clear}{frame}{subframe}{content}{hide_cursor}",
|
"{frame}{subframe}{content}{hide_cursor}",
|
||||||
theme = env.theme.frame(),
|
frame = env.frame.frame_str(env.theme.windows.primary),
|
||||||
clear = clear::All,
|
|
||||||
frame = env.frame.frame_str(env.theme.colors.primary),
|
|
||||||
subframe = self
|
subframe = self
|
||||||
.frame
|
.frame
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.frame_str(env.theme.colors.subwin),
|
.frame_str(env.theme.windows.subwin),
|
||||||
hide_cursor = cursor::Hide,
|
hide_cursor = cursor::Hide,
|
||||||
content = self.frame.unwrap().make(self.draw(
|
content = self.frame.unwrap().make(self.draw(
|
||||||
env.theme.colors.highlight,
|
env.theme.windows.highlight,
|
||||||
env.theme.colors.subwin
|
env.theme.windows.subwin
|
||||||
))
|
))
|
||||||
)?;
|
)?;
|
||||||
screen.flush()?;
|
screen.flush()?;
|
||||||
|
|
|
@ -1,17 +1,10 @@
|
||||||
use termion::cursor;
|
use termion::cursor;
|
||||||
|
|
||||||
const ESTIMATED_FRAME_BIT_SIZE: usize = 11;
|
|
||||||
|
|
||||||
use crate::display::compose::Text;
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
compose::{Component, Components},
|
compose::{Component, Components},
|
||||||
theme::ColorSet,
|
theme::{self, ColorSet},
|
||||||
};
|
};
|
||||||
|
|
||||||
const FRAME_CHAR_HORIZONTAL: char = ' ';
|
|
||||||
const FRAME_CHAR_VERTICAL: char = ' ';
|
|
||||||
|
|
||||||
pub enum FrameSize {
|
pub enum FrameSize {
|
||||||
ByAbsolute(u16, u16),
|
ByAbsolute(u16, u16),
|
||||||
ByPercent(u16, u16),
|
ByPercent(u16, u16),
|
||||||
|
@ -22,36 +15,30 @@ pub struct Frame {
|
||||||
// Both are (w, h)
|
// Both are (w, h)
|
||||||
start: (u16, u16),
|
start: (u16, u16),
|
||||||
end: (u16, u16),
|
end: (u16, u16),
|
||||||
border: u16,
|
theme: theme::Frame,
|
||||||
theme: ColorSet,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Frame {
|
impl Frame {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn sub(
|
pub fn sub(&self, theme: theme::Frame, size: FrameSize) -> Frame {
|
||||||
&self,
|
|
||||||
theme: ColorSet,
|
|
||||||
size: FrameSize,
|
|
||||||
border: u16,
|
|
||||||
) -> Frame {
|
|
||||||
let (p_width, p_height) = self.size();
|
let (p_width, p_height) = self.size();
|
||||||
let sub_size = size.abs_size((
|
let sub_size = size.abs_size((
|
||||||
p_width - self.border * 2,
|
p_width - self.theme.width * 2,
|
||||||
p_height - self.border * 2,
|
p_height - self.theme.width * 2,
|
||||||
));
|
));
|
||||||
self.child_centered(sub_size, border, theme)
|
self.child_centered(sub_size, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn goto(&self, x: u16, y: u16) -> String {
|
pub fn goto(&self, x: u16, y: u16) -> String {
|
||||||
cursor::Goto(
|
cursor::Goto(
|
||||||
self.border.max(
|
self.theme.width.max(
|
||||||
(self.border + self.start.0 + x)
|
(self.theme.width + self.start.0 + x)
|
||||||
.min(self.end.0 - self.border),
|
.min(self.end.0 - self.theme.width),
|
||||||
),
|
),
|
||||||
self.border.max(
|
self.theme.width.max(
|
||||||
(self.border + self.start.1 + y)
|
(self.theme.width + self.start.1 + y)
|
||||||
.min(self.end.1 - self.border),
|
.min(self.end.1 - self.theme.width),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
|
@ -86,14 +73,14 @@ impl Frame {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write_centered_clear(&self, s: &str) -> Component {
|
fn write_centered_clear(&self, s: &str) -> Component {
|
||||||
let width = self.size().0;
|
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 {
|
let s = if s.len() > limit as usize {
|
||||||
&s[..limit as usize]
|
&s[..limit as usize]
|
||||||
} else {
|
} else {
|
||||||
s
|
s
|
||||||
};
|
};
|
||||||
let len = s.len() as u16;
|
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(
|
Component::String(super::compose::Text::PaddedBothSides(
|
||||||
base_size + ((width - len) % 2),
|
base_size + ((width - len) % 2),
|
||||||
s.into(),
|
s.into(),
|
||||||
|
@ -102,12 +89,14 @@ impl Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write_frame_line(&self, y: u16, length: u16) -> Components {
|
fn write_frame_line(
|
||||||
vec![
|
&self,
|
||||||
self.goto_internal(0, y),
|
y: u16,
|
||||||
Component::Repeated(FRAME_CHAR_HORIZONTAL, length),
|
length: u16,
|
||||||
]
|
c: char,
|
||||||
.into()
|
) -> Components {
|
||||||
|
vec![self.goto_internal(0, y), Component::Repeated(c, length)]
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make(&self, comp: Components) -> String {
|
pub fn make(&self, comp: Components) -> String {
|
||||||
|
@ -123,31 +112,31 @@ impl Frame {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn inner_size(&self) -> (u16, u16) {
|
pub fn inner_size(&self) -> (u16, u16) {
|
||||||
(
|
(
|
||||||
self.end.0 - self.start.0 - self.border * 2,
|
self.end.0 - self.start.0 - self.theme.width * 2,
|
||||||
self.end.1 - self.start.1 - self.border * 2,
|
self.end.1 - self.start.1 - self.theme.width * 2,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_terminal_size(
|
pub fn from_terminal_size(
|
||||||
theme: ColorSet,
|
theme: theme::Frame,
|
||||||
width: u16,
|
|
||||||
) -> Result<Self, anyhow::Error> {
|
) -> Result<Self, anyhow::Error> {
|
||||||
let term = termion::terminal_size()?;
|
let term = termion::terminal_size()?;
|
||||||
Ok(Self::from_bottom_right(term, term, width, theme))
|
Ok(Self::from_bottom_right(term, term, theme))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn child_centered(
|
fn child_centered(
|
||||||
&self,
|
&self,
|
||||||
(pos_w, pos_h): (u16, u16),
|
(pos_w, pos_h): (u16, u16),
|
||||||
border: u16,
|
theme: theme::Frame,
|
||||||
theme: ColorSet,
|
|
||||||
) -> 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) = self.size();
|
||||||
let (parent_w, parent_h) =
|
let (parent_w, parent_h) = (
|
||||||
(parent_w - self.border, parent_h - self.border);
|
parent_w - self.theme.width,
|
||||||
|
parent_h - self.theme.width,
|
||||||
|
);
|
||||||
let start = (
|
let start = (
|
||||||
1.max(
|
1.max(
|
||||||
(parent_w - pos_w) / 2
|
(parent_w - pos_w) / 2
|
||||||
|
@ -163,7 +152,6 @@ impl Frame {
|
||||||
let frame = Self {
|
let frame = Self {
|
||||||
end: (start.0 + pos_w, start.1 + pos_h),
|
end: (start.0 + pos_w, start.1 + pos_h),
|
||||||
start,
|
start,
|
||||||
border,
|
|
||||||
theme,
|
theme,
|
||||||
};
|
};
|
||||||
frame
|
frame
|
||||||
|
@ -173,21 +161,19 @@ impl Frame {
|
||||||
fn from_bottom_right(
|
fn from_bottom_right(
|
||||||
(pos_w, pos_h): (u16, u16),
|
(pos_w, pos_h): (u16, u16),
|
||||||
(term_w, term_h): (u16, u16),
|
(term_w, term_h): (u16, u16),
|
||||||
border: u16,
|
theme: theme::Frame,
|
||||||
theme: ColorSet,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let start = (1.max(term_w - pos_w), 1.max(term_h - pos_h));
|
let start = (1.max(term_w - pos_w), 1.max(term_h - pos_h));
|
||||||
let frame = Self {
|
let frame = Self {
|
||||||
end: (start.0 + pos_w, start.1 + pos_h),
|
end: (start.0 + pos_w, start.1 + pos_h),
|
||||||
start,
|
start,
|
||||||
border,
|
|
||||||
theme,
|
theme,
|
||||||
};
|
};
|
||||||
if frame.start.0 > frame.end.0 || frame.start.1 > frame.end.1
|
if frame.start.0 > frame.end.0 || frame.start.1 > frame.end.1
|
||||||
{
|
{
|
||||||
eprintln!(
|
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, border
|
pos_w, pos_h, term_w, term_h, theme.width,
|
||||||
);
|
);
|
||||||
panic!(
|
panic!(
|
||||||
"start.0: {} end.0: {}, start.1: {}, end.1: {}",
|
"start.0: {} end.0: {}, start.1: {}, end.1: {}",
|
||||||
|
@ -211,49 +197,66 @@ impl Frame {
|
||||||
pub fn compose(&self, body_theme: ColorSet) -> Components {
|
pub fn compose(&self, body_theme: ColorSet) -> Components {
|
||||||
let body_theme = Component::Internal(body_theme.to_string());
|
let body_theme = Component::Internal(body_theme.to_string());
|
||||||
let (w_len, h_len) = self.size();
|
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 =
|
let body_clear =
|
||||||
Component::Internal(self.make(Into::<Components>::into(
|
Component::Internal(self.make(Into::<Components>::into(
|
||||||
vec![body_theme.clone(), Component::Padding(w_len)],
|
vec![body_theme.clone(), Component::Padding(w_len)],
|
||||||
)));
|
)));
|
||||||
|
|
||||||
let border =
|
let border_left = Component::Repeated(
|
||||||
Component::Repeated(FRAME_CHAR_VERTICAL, self.border);
|
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
|
// This seems cursed but, I assure you, it's fine
|
||||||
vec![vec![frame_theme.clone()]]
|
vec![vec![frame_theme.clone()]]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// Top horizontal borders
|
// Top horizontal borders
|
||||||
.chain(
|
.chain((0..self.theme.width).into_iter().map(|y: u16| {
|
||||||
(0..self.border)
|
self.write_frame_line(
|
||||||
.into_iter()
|
y,
|
||||||
.map(|y: u16| self.write_frame_line(y, w_len).0),
|
w_len,
|
||||||
)
|
self.theme.frame_chars.top,
|
||||||
|
)
|
||||||
|
.0
|
||||||
|
}))
|
||||||
// Frame body + vertical frame borders
|
// Frame body + vertical frame borders
|
||||||
.chain(
|
.chain(
|
||||||
(self.border..h_len - self.border).into_iter().map(
|
(self.theme.width..h_len - self.theme.width)
|
||||||
|y: u16| {
|
.into_iter()
|
||||||
|
.map(|y: u16| {
|
||||||
let left = self.goto_internal(0, y);
|
let left = self.goto_internal(0, y);
|
||||||
let right = self
|
let right = self.goto_internal(
|
||||||
.goto_internal(w_len - self.border, y);
|
w_len - self.theme.width,
|
||||||
|
y,
|
||||||
|
);
|
||||||
vec![
|
vec![
|
||||||
left.clone(),
|
left.clone(),
|
||||||
body_clear.clone(),
|
body_clear.clone(),
|
||||||
frame_theme.clone(),
|
frame_theme.clone(),
|
||||||
left,
|
left,
|
||||||
border.clone(),
|
border_left.clone(),
|
||||||
right,
|
right,
|
||||||
border.clone(),
|
border_right.clone(),
|
||||||
]
|
]
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
// Bottom horizontal border
|
// Bottom horizontal border
|
||||||
.chain(
|
.chain((h_len - self.theme.width..h_len).into_iter().map(
|
||||||
(h_len - self.border..h_len)
|
|y: u16| {
|
||||||
.into_iter()
|
self.write_frame_line(
|
||||||
.map(|y: u16| self.write_frame_line(y, w_len).0),
|
y,
|
||||||
)
|
w_len,
|
||||||
|
self.theme.frame_chars.bottom,
|
||||||
|
)
|
||||||
|
.0
|
||||||
|
},
|
||||||
|
))
|
||||||
// Theme reset + move to beginning of inside frame
|
// Theme reset + move to beginning of inside frame
|
||||||
.chain(
|
.chain(
|
||||||
vec![vec![Component::Goto(0, 0), body_theme]]
|
vec![vec![Component::Goto(0, 0), body_theme]]
|
||||||
|
|
|
@ -89,25 +89,22 @@ pub struct Environment {
|
||||||
|
|
||||||
impl Into<String> for Environment {
|
impl Into<String> for Environment {
|
||||||
fn into(self) -> String {
|
fn into(self) -> String {
|
||||||
self.frame.frame_str(self.theme.colors.primary)
|
self.frame.frame_str(self.theme.windows.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
fn initial_must(theme: Theme) -> Self {
|
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 {
|
Self {
|
||||||
theme,
|
theme,
|
||||||
frame: Frame::from_terminal_size(theme.colors.frame, w)
|
frame: Frame::from_terminal_size(theme.frames.outer)
|
||||||
.expect("could not get terminal size"),
|
.expect("could not get terminal size"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn primary_frame(&self) -> String {
|
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!(
|
write!(
|
||||||
scr,
|
scr,
|
||||||
"{color}{clear}{show_cursor}{move_to_start}",
|
"{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),
|
move_to_start = cursor::Goto(1, 1),
|
||||||
clear = clear::All,
|
clear = clear::All,
|
||||||
show_cursor = cursor::Show,
|
show_cursor = cursor::Show,
|
||||||
|
|
|
@ -1,23 +1,46 @@
|
||||||
|
use serde::{de::Visitor, Deserialize, Serialize};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use termion::color;
|
use termion::color;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
pub struct Theme {
|
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 struct ColorSet {
|
||||||
pub fg: Color,
|
pub fg: Color,
|
||||||
pub bg: 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 {
|
impl Default for ColorSet {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fg: "#ffffff".into(),
|
fg: "#ffffff".try_into().unwrap(),
|
||||||
bg: "#000000".into(),
|
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 struct Colors {
|
||||||
pub primary: ColorSet,
|
pub primary: ColorSet,
|
||||||
pub frame: ColorSet,
|
|
||||||
pub subframe: ColorSet,
|
|
||||||
pub subwin: ColorSet,
|
pub subwin: ColorSet,
|
||||||
pub highlight: ColorSet,
|
pub highlight: ColorSet,
|
||||||
}
|
}
|
||||||
impl Theme {}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Color(color::Rgb);
|
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 {
|
impl Deref for Color {
|
||||||
type Target = color::Rgb;
|
type Target = color::Rgb;
|
||||||
|
|
||||||
|
@ -56,8 +132,10 @@ impl TryFrom<String> for Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Color {
|
impl TryFrom<&str> for Color {
|
||||||
fn from(value: &str) -> Self {
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
if value.len() < 6 || value.len() > 7 {
|
if value.len() < 6 || value.len() > 7 {
|
||||||
panic!("hex code length invalid: {}", value.len());
|
panic!("hex code length invalid: {}", value.len());
|
||||||
}
|
}
|
||||||
|
@ -65,40 +143,60 @@ impl From<&str> for Color {
|
||||||
if value.starts_with('#') {
|
if value.starts_with('#') {
|
||||||
i = 1;
|
i = 1;
|
||||||
}
|
}
|
||||||
Self(color::Rgb(
|
Ok(Self(color::Rgb(
|
||||||
u8::from_str_radix(&value[i..i + 2], 16)
|
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)
|
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)
|
u8::from_str_radix(&value[i + 4..i + 6], 16)
|
||||||
.expect("blue hex"),
|
.map_err(|_| anyhow::anyhow!("blue hex"))?,
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Theme {
|
impl Default for Theme {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
colors: Colors {
|
windows: Colors {
|
||||||
primary: ColorSet {
|
primary: ColorSet {
|
||||||
fg: "#330033".into(),
|
fg: "#330033".try_into().unwrap(),
|
||||||
bg: "#a006d3".into(),
|
bg: "#a006d3".try_into().unwrap(),
|
||||||
},
|
|
||||||
frame: ColorSet {
|
|
||||||
fg: "#ffe6ff".into(),
|
|
||||||
bg: "#9005c2".into(),
|
|
||||||
},
|
|
||||||
subframe: ColorSet {
|
|
||||||
fg: "#ffe6ff".into(),
|
|
||||||
bg: "#7003a0".into(),
|
|
||||||
},
|
},
|
||||||
highlight: ColorSet {
|
highlight: ColorSet {
|
||||||
fg: "#ffffff".into(),
|
fg: "#ffffff".try_into().unwrap(),
|
||||||
bg: "#0000ff".into(),
|
bg: "#0000ff".try_into().unwrap(),
|
||||||
},
|
},
|
||||||
subwin: ColorSet {
|
subwin: ColorSet {
|
||||||
fg: "#ffffff".into(),
|
fg: "#ffffff".try_into().unwrap(),
|
||||||
bg: "#500170".into(),
|
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 {
|
impl Theme {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn primary(&self) -> String {
|
pub fn primary(&self) -> String {
|
||||||
self.colors.primary.to_string()
|
self.windows.primary.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn frame(&self) -> String {
|
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;
|
use crate::display::Screen;
|
||||||
|
|
||||||
extern crate termion;
|
extern crate termion;
|
||||||
|
mod cfg;
|
||||||
mod display;
|
mod display;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), anyhow::Error> {
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
|
let config = cfg::Config::load_or_create()?;
|
||||||
{
|
{
|
||||||
let theme = Theme::default();
|
let x = Screen::new(config.theme).unwrap();
|
||||||
let x = Screen::new(theme).unwrap();
|
|
||||||
x.start().await?;
|
x.start().await?;
|
||||||
}
|
}
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
|
|
Loading…
Reference in New Issue