styling
This commit is contained in:
+4
-6
@@ -4,9 +4,7 @@ use crossterm::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::utils::{
|
use crate::utils::{clear, flush, get_term_size, hide_cursor, show_cursor, Frame};
|
||||||
clear, create_frame, flush, get_term_size, hide_cursor, render_to_frame, show_cursor,
|
|
||||||
};
|
|
||||||
use std::{ffi::c_void, ptr::null, sync::mpsc};
|
use std::{ffi::c_void, ptr::null, sync::mpsc};
|
||||||
|
|
||||||
pub struct LArgs<'a> {
|
pub struct LArgs<'a> {
|
||||||
@@ -125,10 +123,10 @@ extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void {
|
|||||||
}
|
}
|
||||||
Command::Render => {
|
Command::Render => {
|
||||||
let (width, height) = get_term_size();
|
let (width, height) = get_term_size();
|
||||||
let mut frame: Vec<String> = create_frame(width, height);
|
let mut frame = Frame::new(width, height);
|
||||||
render_to_frame(true, width, &mut frame, args.element);
|
frame.render(true, args.element);
|
||||||
clear();
|
clear();
|
||||||
print!("{}", frame.join(""));
|
print!("{}", frame.output_nnl());
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
@@ -1,6 +1,31 @@
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! __css {
|
macro_rules! __css {
|
||||||
($_style:expr,)=>{};
|
($_style:expr,)=>{};
|
||||||
|
// percentage (n%)
|
||||||
|
(
|
||||||
|
$_style:expr, $name:ident: $value:literal% $(, $($other:tt)*)?
|
||||||
|
) => {{
|
||||||
|
$_style.$name = Number::Pe($value);
|
||||||
|
$crate::__css!($_style, $($($other)*)?);
|
||||||
|
}};
|
||||||
|
(
|
||||||
|
$_style:expr, ($name:ident): $value:literal% $(, $($other:tt)*)?
|
||||||
|
) => {{
|
||||||
|
$_style.other.insert(stringify!($name).to_string(), Box::new(
|
||||||
|
Number::Pe($value)
|
||||||
|
));
|
||||||
|
$crate::__css!($_style, $($($other)*)?);
|
||||||
|
}};
|
||||||
|
|
||||||
|
// Number
|
||||||
|
(
|
||||||
|
$_style:expr, $name:ident: $value:literal $(, $($other:tt)*)?
|
||||||
|
) => {{
|
||||||
|
$_style.$name = Number::Px($value);
|
||||||
|
$crate::__css!($_style, $($($other)*)?);
|
||||||
|
}};
|
||||||
|
|
||||||
|
// Normal
|
||||||
(
|
(
|
||||||
$_style:expr, $name:ident: $value:expr $(, $($other:tt)*)?
|
$_style:expr, $name:ident: $value:expr $(, $($other:tt)*)?
|
||||||
) => {{
|
) => {{
|
||||||
|
|||||||
+16
-5
@@ -2,10 +2,21 @@ use crate::prelude::*;
|
|||||||
|
|
||||||
pub fn event_logger() -> Element {
|
pub fn event_logger() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
id: "root",
|
styling: Some(styling()),
|
||||||
text { on_event: Handler::new(|_, _, document| {
|
button { class: "txt", "Hello, World!" }
|
||||||
let root = document.get_element_by_id::<Div>("root").unwrap();
|
}
|
||||||
root.children.add_child(rsx_elem!( text { "Ok" } ));
|
}
|
||||||
}), id: "txt", "current event: None" }
|
|
||||||
|
pub fn styling() -> Css {
|
||||||
|
css! {
|
||||||
|
.txt {
|
||||||
|
x: Center,
|
||||||
|
y: Center,
|
||||||
|
color: Red,
|
||||||
|
}
|
||||||
|
|
||||||
|
.txt: clicked {
|
||||||
|
color: Blue,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -43,6 +43,7 @@ pub use app::run;
|
|||||||
/// the UI framework easier to use.
|
/// the UI framework easier to use.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::ui::Color::*;
|
pub use crate::ui::Color::*;
|
||||||
|
pub use crate::ui::Number::*;
|
||||||
pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler};
|
pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler};
|
||||||
pub use crate::{style, Command, Document, Element, Value};
|
pub use crate::{style, Command, Document, Element, Value};
|
||||||
// useful for Element making
|
// useful for Element making
|
||||||
@@ -88,6 +89,11 @@ impl<T: Copy + PartialEq + Default> Default for Value<T> {
|
|||||||
/// ElementCore Traits
|
/// ElementCore Traits
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/// ```
|
||||||
|
/// RenderResult(output, (x, y), (width, height))
|
||||||
|
/// ```
|
||||||
|
pub struct RenderResult(String, (ui::Number, ui::Number));
|
||||||
|
|
||||||
pub trait ElementCore: Send + Sync {
|
pub trait ElementCore: Send + Sync {
|
||||||
fn get_data(&self) -> (Value<usize>, usize, String);
|
fn get_data(&self) -> (Value<usize>, usize, String);
|
||||||
fn update_data(&mut self, width: usize, height: usize);
|
fn update_data(&mut self, width: usize, height: usize);
|
||||||
@@ -97,10 +103,7 @@ pub trait ElementCore: Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait ElementWidget: ElementCore + std::fmt::Debug {
|
pub trait ElementWidget: ElementCore + std::fmt::Debug {
|
||||||
fn render(&self, focused: bool) -> String {
|
fn render(&self, focused: bool) -> RenderResult;
|
||||||
_ = focused;
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn event(&mut self, event: Event, document: &Document) {
|
fn event(&mut self, event: Event, document: &Document) {
|
||||||
_ = (event, document)
|
_ = (event, document)
|
||||||
|
|||||||
+24
-18
@@ -5,7 +5,7 @@
|
|||||||
pub mod styles;
|
pub mod styles;
|
||||||
pub use styles::*;
|
pub use styles::*;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::{prelude::*, RenderResult};
|
||||||
use osui_element::{elem_fn, element};
|
use osui_element::{elem_fn, element};
|
||||||
|
|
||||||
/// The `Text` element represents a piece of static text in the terminal UI.
|
/// The `Text` element represents a piece of static text in the terminal UI.
|
||||||
@@ -31,12 +31,17 @@ pub struct Text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ElementWidget for Text<'_> {
|
impl ElementWidget for Text<'_> {
|
||||||
fn render(&self, _: bool) -> String {
|
fn render(&self, _: bool) -> RenderResult {
|
||||||
|
self.style.result(
|
||||||
|
"",
|
||||||
|
self.style.write("", &{
|
||||||
if let Children::Text(text) = &self.children {
|
if let Children::Text(text) = &self.children {
|
||||||
text.clone()
|
text.clone()
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
fn event(&mut self, event: Event, document: &Document) {
|
fn event(&mut self, event: Event, document: &Document) {
|
||||||
call!(self.on_event(event, document));
|
call!(self.on_event(event, document));
|
||||||
@@ -71,21 +76,16 @@ pub struct Div {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ElementWidget for Div<'_> {
|
impl ElementWidget for Div<'_> {
|
||||||
fn render(&self, focused: bool) -> String {
|
fn render(&self, focused: bool) -> RenderResult {
|
||||||
let mut frame = crate::utils::create_frame(self.width.get_value(), self.height.get_value());
|
let mut frame = crate::utils::Frame::new(crate::utils::get_term_size().0, 12);
|
||||||
|
|
||||||
if let Children::Children(children, child) = &self.children {
|
if let Children::Children(children, child) = &self.children {
|
||||||
for (i, elem) in children.iter().enumerate() {
|
for (i, elem) in children.iter().enumerate() {
|
||||||
crate::utils::render_to_frame(
|
frame.render(focused && i == *child, elem);
|
||||||
focused && i == *child,
|
|
||||||
self.width.get_value(),
|
|
||||||
&mut frame,
|
|
||||||
elem,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.join("\n")
|
self.style.result("", frame.output())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, event: Event, document: &Document) {
|
fn event(&mut self, event: Event, document: &Document) {
|
||||||
@@ -149,14 +149,20 @@ pub struct Button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ElementWidget for Button<'_> {
|
impl ElementWidget for Button<'_> {
|
||||||
fn render(&self, focused: bool) -> String {
|
fn render(&self, focused: bool) -> RenderResult {
|
||||||
if self.state == "clicked" {
|
if self.state != "" {
|
||||||
return self.style.write("clicked", &self.children.get_text());
|
self.style.result(
|
||||||
}
|
self.state,
|
||||||
if focused {
|
self.style.write(self.state, &self.children.get_text()),
|
||||||
self.style.write("hover", &self.children.get_text())
|
)
|
||||||
|
} else if focused {
|
||||||
|
self.style.result(
|
||||||
|
"hover",
|
||||||
|
self.style.write("hover", &self.children.get_text()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
self.style.write("", &self.children.get_text())
|
self.style
|
||||||
|
.result("", self.style.write("", &self.children.get_text()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+72
-5
@@ -6,9 +6,12 @@ use std::{collections::HashMap, fmt::Debug};
|
|||||||
|
|
||||||
use dyn_clone::{clone_trait_object, DynClone};
|
use dyn_clone::{clone_trait_object, DynClone};
|
||||||
|
|
||||||
|
use crate::RenderResult;
|
||||||
|
|
||||||
pub trait StyleCore: Debug + Send + Sync + DynClone {
|
pub trait StyleCore: Debug + Send + Sync + DynClone {
|
||||||
fn ansi(&self) -> String;
|
fn ansi(&self) -> String;
|
||||||
fn ansi_bg(&self) -> String;
|
fn ansi_bg(&self) -> String;
|
||||||
|
fn is_null(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
clone_trait_object!(StyleCore);
|
clone_trait_object!(StyleCore);
|
||||||
@@ -45,6 +48,9 @@ impl StyleCore for Font {
|
|||||||
fn ansi_bg(&self) -> String {
|
fn ansi_bg(&self) -> String {
|
||||||
self.ansi()
|
self.ansi()
|
||||||
}
|
}
|
||||||
|
fn is_null(&self) -> bool {
|
||||||
|
*self == Self::None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
@@ -104,6 +110,9 @@ impl StyleCore for Color {
|
|||||||
Color::White => "\x1b[47m",
|
Color::White => "\x1b[47m",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
fn is_null(&self) -> bool {
|
||||||
|
*self == Self::NoColor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Color {
|
impl Default for Color {
|
||||||
@@ -144,6 +153,15 @@ fn hex_to_rgb(hex: &str) -> (u8, u8, u8) {
|
|||||||
(r, g, b)
|
(r, g, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Number {
|
||||||
|
Px(usize),
|
||||||
|
Pe(usize),
|
||||||
|
Center,
|
||||||
|
Auto,
|
||||||
|
Default,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
||||||
pub enum StyleName {
|
pub enum StyleName {
|
||||||
Class(String),
|
Class(String),
|
||||||
@@ -154,6 +172,12 @@ pub enum StyleName {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StyleElement {
|
pub struct StyleElement {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
pub background: Color,
|
||||||
|
pub font: Font,
|
||||||
|
pub x: Number,
|
||||||
|
pub y: Number,
|
||||||
|
pub width: Number,
|
||||||
|
pub height: Number,
|
||||||
pub other: HashMap<String, Box<dyn StyleCore>>,
|
pub other: HashMap<String, Box<dyn StyleCore>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,11 +185,44 @@ impl Default for StyleElement {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
StyleElement {
|
StyleElement {
|
||||||
color: Color::NoColor,
|
color: Color::NoColor,
|
||||||
|
background: Color::NoColor,
|
||||||
|
font: Font::None,
|
||||||
|
x: Number::Default,
|
||||||
|
y: Number::Default,
|
||||||
|
width: Number::Default,
|
||||||
|
height: Number::Default,
|
||||||
other: HashMap::new(),
|
other: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StyleElement {
|
||||||
|
pub fn prioritize(mut self, other: &Self) -> StyleElement {
|
||||||
|
if !other.color.is_null() {
|
||||||
|
self.color = other.color.clone();
|
||||||
|
}
|
||||||
|
if !other.background.is_null() {
|
||||||
|
self.background = other.background.clone();
|
||||||
|
}
|
||||||
|
if !other.font.is_null() {
|
||||||
|
self.font = other.font.clone();
|
||||||
|
}
|
||||||
|
if other.x != Number::Default {
|
||||||
|
self.x = other.x.clone();
|
||||||
|
}
|
||||||
|
if other.y != Number::Default {
|
||||||
|
self.y = other.y.clone();
|
||||||
|
}
|
||||||
|
if other.width != Number::Default {
|
||||||
|
self.width = other.width.clone();
|
||||||
|
}
|
||||||
|
if other.height != Number::Default {
|
||||||
|
self.height = other.height.clone();
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Style(pub StyleElement, pub HashMap<String, StyleElement>);
|
pub struct Style(pub StyleElement, pub HashMap<String, StyleElement>);
|
||||||
|
|
||||||
@@ -177,20 +234,30 @@ impl Default for Style {
|
|||||||
|
|
||||||
impl Style {
|
impl Style {
|
||||||
pub fn write(&self, state: &str, s: &str) -> String {
|
pub fn write(&self, state: &str, s: &str) -> String {
|
||||||
if state == "" {
|
|
||||||
return self.0.write(s);
|
|
||||||
}
|
|
||||||
if let Some(style_element) = self.1.get(state) {
|
if let Some(style_element) = self.1.get(state) {
|
||||||
style_element.write(s)
|
style_element.write(s)
|
||||||
} else {
|
} else {
|
||||||
s.to_string()
|
return self.0.write(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn result(&self, state: &str, s: String) -> RenderResult {
|
||||||
|
if let Some(style_element) = self.1.get(state) {
|
||||||
|
let style_element = self.0.clone().prioritize(&style_element.clone());
|
||||||
|
RenderResult(s, (style_element.x.clone(), style_element.y.clone()))
|
||||||
|
} else {
|
||||||
|
RenderResult(s, (self.0.x.clone(), self.0.y.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyleElement {
|
impl StyleElement {
|
||||||
pub fn write(&self, s: &str) -> String {
|
pub fn write(&self, s: &str) -> String {
|
||||||
format!("{}{s}\x1b[0m", self.color.ansi())
|
format!(
|
||||||
|
"{}{}{}{s}\x1b[0m",
|
||||||
|
self.color.ansi(),
|
||||||
|
self.background.ansi_bg(),
|
||||||
|
self.font.ansi()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+72
-26
@@ -9,7 +9,6 @@
|
|||||||
/// let frame = create_frame(50, 10);
|
/// let frame = create_frame(50, 10);
|
||||||
/// render_to_frame(crate::State::Normal, 50, &mut frame, &text_element);
|
/// render_to_frame(crate::State::Normal, 50, &mut frame, &text_element);
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
use crate::{Element, Value};
|
use crate::{Element, Value};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@@ -49,7 +48,7 @@ pub fn compress_string(input: &str, re: &Regex) -> (String, HashMap<usize, Strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Merges a frame withe a line by x
|
/// Merges a frame withe a line by x
|
||||||
fn merge_line(frame_: &str, line_: &str, x: usize) -> String {
|
fn merge_line(frame_: &str, line_: &str, x: usize) -> (String, String) {
|
||||||
let (frame_, fm) = compress_string(frame_, &ANSI);
|
let (frame_, fm) = compress_string(frame_, &ANSI);
|
||||||
let (mut line_, lm) = compress_string(line_, &ANSI);
|
let (mut line_, lm) = compress_string(line_, &ANSI);
|
||||||
|
|
||||||
@@ -82,35 +81,86 @@ fn merge_line(frame_: &str, line_: &str, x: usize) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
(res, line_)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render to a frame
|
pub struct Frame {
|
||||||
pub fn render_to_frame(
|
frame: Vec<String>,
|
||||||
focused: bool,
|
|
||||||
width: usize,
|
width: usize,
|
||||||
frame: &mut Vec<String>,
|
used: Vec<usize>,
|
||||||
element: &Element,
|
|
||||||
) {
|
|
||||||
let data = element.get_data();
|
|
||||||
for (i, line) in element.render(focused).split('\n').enumerate() {
|
|
||||||
if (data.1 + i) < frame.len() {
|
|
||||||
let frame_line = frame.get_mut(data.1 + i).unwrap();
|
|
||||||
match data.0 {
|
|
||||||
Value::Custom(x) => {
|
|
||||||
*frame_line = merge_line(&frame_line, line, x);
|
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
for (x, c) in frame_line.chars().rev().enumerate() {
|
impl Frame {
|
||||||
if c != ' ' {
|
pub fn new(width: usize, height: usize) -> Self {
|
||||||
*frame_line = merge_line(&frame_line, line, width - x);
|
Self {
|
||||||
return;
|
frame: vec![" ".repeat(width); height],
|
||||||
|
width,
|
||||||
|
used: vec![0; height],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*frame_line = merge_line(&frame_line, line, 0);
|
pub fn render(&mut self, focused: bool, element: &Element) {
|
||||||
|
// let data = element.get_data();
|
||||||
|
let output = element.render(focused);
|
||||||
|
let x;
|
||||||
|
let mut y = 0;
|
||||||
|
match output.1 .1 {
|
||||||
|
crate::ui::Number::Px(_y) => {
|
||||||
|
y = _y;
|
||||||
|
}
|
||||||
|
crate::ui::Number::Pe(p) => y = (self.frame.len() * p) / 100,
|
||||||
|
crate::ui::Number::Center => {
|
||||||
|
y = (self.frame.len() - output.0.matches('\n').count() + 1) / 2
|
||||||
|
}
|
||||||
|
crate::ui::Number::Auto => {
|
||||||
|
for (i, n) in self.used.iter().enumerate() {
|
||||||
|
if *n == 0 {
|
||||||
|
y = i;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
crate::ui::Number::Default => {
|
||||||
|
for (i, n) in self.used.iter().enumerate() {
|
||||||
|
if *n == 0 {
|
||||||
|
y = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match output.1 .0 {
|
||||||
|
crate::ui::Number::Px(_x) => {
|
||||||
|
x = _x;
|
||||||
|
}
|
||||||
|
crate::ui::Number::Pe(p) => x = (self.width * p) / 100,
|
||||||
|
crate::ui::Number::Center => {
|
||||||
|
x = (self.width
|
||||||
|
- ANSI
|
||||||
|
.replace_all(&output.0.lines().next().unwrap_or(""), "")
|
||||||
|
.len())
|
||||||
|
/ 2;
|
||||||
|
}
|
||||||
|
crate::ui::Number::Auto => {
|
||||||
|
x = *self.used.get(y).unwrap();
|
||||||
|
}
|
||||||
|
crate::ui::Number::Default => {
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i, line) in output.0.split('\n').enumerate() {
|
||||||
|
if self.frame.len() > y + i {
|
||||||
|
let frame_line = self.frame.get_mut(y + i).unwrap();
|
||||||
|
let merged = merge_line(&frame_line, line, x);
|
||||||
|
*frame_line = merged.0;
|
||||||
|
*self.used.get_mut(y + i).unwrap() += merged.1.len() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn output(&self) -> String {
|
||||||
|
self.frame.join("\n")
|
||||||
|
}
|
||||||
|
pub fn output_nnl(&self) -> String {
|
||||||
|
self.frame.join("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,10 +230,6 @@ pub fn closest_component(
|
|||||||
.unwrap_or(current_index) // If no component is found, return the current index
|
.unwrap_or(current_index) // If no component is found, return the current index
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_frame(width: usize, height: usize) -> Vec<String> {
|
|
||||||
vec![" ".repeat(width); height]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_term_size() -> (usize, usize) {
|
pub fn get_term_size() -> (usize, usize) {
|
||||||
let (width, height) = crossterm::terminal::size().unwrap();
|
let (width, height) = crossterm::terminal::size().unwrap();
|
||||||
(width as usize, height as usize)
|
(width as usize, height as usize)
|
||||||
|
|||||||
+1
-1
@@ -6,4 +6,4 @@
|
|||||||
|
|
||||||
`x`, `y`: `n`, `n%`, `center`, `auto`;
|
`x`, `y`: `n`, `n%`, `center`, `auto`;
|
||||||
|
|
||||||
`display`: `row`, `column`, `overlap`;
|
<!-- `display`: `row`, `column`, `overlap`; -->
|
||||||
Reference in New Issue
Block a user