feat: update adtools
This commit is contained in:
parent
812cc22990
commit
32370295f7
|
@ -21,4 +21,4 @@
|
|||
# Copyright (C) 2021- donkey <anjingyu_ws@foxmail.com>
|
||||
|
||||
__author__ = ['"donkey" <anjingyu_ws@foxmail.com>']
|
||||
__version__ = "0.5.45"
|
||||
__version__ = "0.5.46"
|
||||
|
|
|
@ -123,7 +123,7 @@ class Android(OSBase):
|
|||
def run_make(self, args):
|
||||
return AdUtil.run_command(
|
||||
"cmake --build . --config {} -- -j{}".format(
|
||||
"Release" if args.release else "Debug", AdUtil.get_cpu_number()
|
||||
"Release" if args.release else "Debug", AdUtil.get_recommand_cpu_number()
|
||||
)
|
||||
)[0]
|
||||
|
||||
|
|
|
@ -50,13 +50,15 @@ class Linux(OSBase):
|
|||
return '-G "Unix Makefiles"'
|
||||
|
||||
def update_argument(self, parser):
|
||||
default_job_count = Adutil.get_recommand_cpu_number()
|
||||
|
||||
parser.add_argument(
|
||||
"-j",
|
||||
"--jobs",
|
||||
default=AdUtil.get_cpu_number(),
|
||||
default=default_job_count,
|
||||
type=int,
|
||||
help="Specify the count of cocurrent jobs, default: <core count>, {}".format(
|
||||
AdUtil.get_cpu_number()
|
||||
default_job_count
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -105,6 +105,6 @@ please make sure the environment variable *QNX_ROOT* is defined to point to the
|
|||
else:
|
||||
qnx_make = os.path.join(qnx_host_bin, "make")
|
||||
|
||||
return AdUtil.run_command("{} -j{}".format(qnx_make, AdUtil.get_cpu_number()))[
|
||||
return AdUtil.run_command("{} -j{}".format(qnx_make, Adutil.get_recommand_cpu_number()))[
|
||||
0
|
||||
]
|
||||
|
|
|
@ -105,6 +105,6 @@ please make sure the environment variable *QNX7_ROOT* is defined to point to the
|
|||
else:
|
||||
qnx_make = os.path.join(qnx_host_bin, "make")
|
||||
|
||||
return AdUtil.run_command("{} -j{}".format(qnx_make, AdUtil.get_cpu_number()))[
|
||||
return AdUtil.run_command("{} -j{}".format(qnx_make, Adutil.get_recommand_cpu_number()))[
|
||||
0
|
||||
]
|
||||
|
|
|
@ -396,9 +396,23 @@ class AdUtil:
|
|||
return ""
|
||||
|
||||
@classmethod
|
||||
def get_cpu_number(cls):
|
||||
def get_cpu_number(cls) -> int:
|
||||
return multiprocessing.cpu_count()
|
||||
|
||||
@classmethod
|
||||
def get_recommand_cpu_number(cls) -> int:
|
||||
'''Get the recommended CPU core count for tasks,
|
||||
so that the tasks will never freeze the entire system'''
|
||||
default_job_count = AdUtil.get_cpu_number()
|
||||
|
||||
if default_job_count > 8:
|
||||
default_job_count = 8
|
||||
elif default_job_count > 2:
|
||||
default_job_count = default_job_count - 2
|
||||
else:
|
||||
default_job_count = 1
|
||||
|
||||
return default_job_count
|
||||
|
||||
class NexusHelper:
|
||||
def __init__(self, host: str = "", auth_info: tuple = ()):
|
||||
|
|
|
@ -6,6 +6,14 @@ edition = "2021"
|
|||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
clap = { version = "4.5.4", features = ["derive"] }
|
||||
color-eyre = "0.6.3"
|
||||
crossterm = "0.27.0"
|
||||
directories = "5.0.1"
|
||||
futures = "0.3.30"
|
||||
ratatui = "0.26.3"
|
||||
serde = { version = "1.0.202", features = ["derive"] }
|
||||
serde_derive = "1.0.203"
|
||||
thiserror = "1.0.61"
|
||||
tokio = "1.38.0"
|
||||
tokio-util = "0.7.11"
|
||||
toml = "0.8.13"
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# INS App TUI
|
||||
|
||||
``` shell
|
||||
# Append dependences
|
||||
# Required
|
||||
cargo add ratatui crossterm tokio tokio_util futures
|
||||
# Optional
|
||||
cargo add color-eyre serde serde_derive directories
|
||||
```
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
use std::io::{self, stdout};
|
||||
|
||||
use color_eyre::{config::HookBuilder, Result};
|
||||
use ratatui::{
|
||||
backend::{Backend, CrosstermBackend},
|
||||
buffer::Buffer,
|
||||
crossterm::{
|
||||
event::{self, Event, KeyCode, KeyEventKind},
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
ExecutableCommand,
|
||||
},
|
||||
layout::{
|
||||
Alignment,
|
||||
Constraint::{self, Fill, Length, Max, Min, Percentage, Ratio},
|
||||
Flex, Layout, Rect,
|
||||
},
|
||||
style::{palette::tailwind, Color, Modifier, Style, Stylize},
|
||||
symbols::{self, line},
|
||||
terminal::Terminal,
|
||||
text::{Line, Text},
|
||||
widgets::{
|
||||
block::Title, Block, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState,
|
||||
StatefulWidget, Tabs, Widget,
|
||||
},
|
||||
};
|
||||
use strum::{Display, EnumIter, FromRepr, IntoEnumIterator};
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
struct App {
|
||||
selected_tab: SelectedTab,
|
||||
scroll_offset: u16,
|
||||
spacing: u16,
|
||||
state: AppState,
|
||||
}
|
||||
|
|
@ -6,9 +6,32 @@ use std::{
|
|||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use directories::ProjectDirs;
|
||||
use serde::Deserialize;
|
||||
use toml::Value;
|
||||
|
||||
pub(crate) fn get_data_dir() -> Result<PathBuf> {
|
||||
let directory = if let Ok(s) = std::env::var("INSAPP_DATA") {
|
||||
PathBuf::from(s)
|
||||
} else if let Some(proj_dirs) = ProjectDirs::from("com", "donkey", "insapp") {
|
||||
proj_dirs.data_local_dir().to_path_buf()
|
||||
} else {
|
||||
return Err(anyhow!("Unable to find data directory for INS App"));
|
||||
};
|
||||
Ok(directory)
|
||||
}
|
||||
|
||||
pub(crate) fn get_config_dir() -> Result<PathBuf> {
|
||||
let directory = if let Ok(s) = std::env::var("INSAPP_CONFIG") {
|
||||
PathBuf::from(s)
|
||||
} else if let Some(proj_dirs) = ProjectDirs::from("com", "donkey", "insapp") {
|
||||
proj_dirs.config_local_dir().to_path_buf()
|
||||
} else {
|
||||
return Err(anyhow!("Unable to find config directory for INS App"));
|
||||
};
|
||||
Ok(directory)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) struct InsSerialConfig {
|
||||
parser: String, // "gsof"
|
||||
|
|
|
@ -1,7 +1,43 @@
|
|||
use anyhow::Result;
|
||||
use insapp::config::Config;
|
||||
|
||||
use clap::{arg, ArgMatches, Command};
|
||||
|
||||
mod tui;
|
||||
|
||||
impl App {
|
||||
async fn run(&mut self) -> Result<()> {
|
||||
let mut tui = tui::Tui::new()?
|
||||
.tick_rate(4.0) // 4 ticks per second
|
||||
.frame_rate(30.0); // 30 frames per second
|
||||
|
||||
tui.enter()?; // Starts event handler, enters raw mode, enters alternate screen
|
||||
|
||||
loop {
|
||||
tui.draw(|f| {
|
||||
// Deref allows calling `tui.terminal.draw`
|
||||
self.ui(f);
|
||||
})?;
|
||||
|
||||
if let Some(evt) = tui.next().await {
|
||||
// `tui.next().await` blocks till next event
|
||||
let mut maybe_action = self.handle_event(evt);
|
||||
while let Some(action) = maybe_action {
|
||||
maybe_action = self.update(action);
|
||||
}
|
||||
};
|
||||
|
||||
if self.should_quit {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tui.exit()?; // stops event handler, exits raw mode, exits alternate screen
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct ArgParser {
|
||||
config_file: String,
|
||||
}
|
||||
|
@ -31,7 +67,7 @@ impl ArgParser {
|
|||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<()> {
|
||||
let arg_parser = ArgParser::new();
|
||||
let matches = arg_parser.get_matches();
|
||||
|
||||
|
@ -42,5 +78,9 @@ fn main() {
|
|||
|
||||
let config = Config::load_from_file(config_file_path).unwrap();
|
||||
|
||||
println!("config: {:#?}", config);
|
||||
init_error_hooks()?;
|
||||
let terminal = init_terminal()?;
|
||||
App::default().run(terminal)?;
|
||||
restore_terminal()?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
use std::{
|
||||
ops::{Deref, DerefMut},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
use crossterm::{
|
||||
cursor,
|
||||
event::{
|
||||
DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture,
|
||||
Event as CrosstermEvent, KeyEvent, KeyEventKind, MouseEvent,
|
||||
},
|
||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ratatui::backend::CrosstermBackend as Backend;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::{
|
||||
sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
|
||||
task::JoinHandle,
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum Event {
|
||||
Init,
|
||||
Quit,
|
||||
Error,
|
||||
Closed,
|
||||
Tick,
|
||||
Render,
|
||||
FocusGained,
|
||||
FocusLost,
|
||||
Paste(String),
|
||||
Key(KeyEvent),
|
||||
Mouse(MouseEvent),
|
||||
Resize(u16, u16),
|
||||
}
|
||||
|
||||
pub struct Tui {
|
||||
pub terminal: ratatui::Terminal<Backend<std::io::Stderr>>,
|
||||
pub task: JoinHandle<()>,
|
||||
pub cancellation_token: CancellationToken,
|
||||
pub event_rx: UnboundedReceiver<Event>,
|
||||
pub event_tx: UnboundedSender<Event>,
|
||||
pub frame_rate: f64,
|
||||
pub tick_rate: f64,
|
||||
pub mouse: bool,
|
||||
pub paste: bool,
|
||||
}
|
||||
|
||||
impl Tui {
|
||||
pub fn new() -> Result<Self> {
|
||||
let tick_rate = 4.0;
|
||||
let frame_rate = 60.0;
|
||||
let terminal = ratatui::Terminal::new(Backend::new(std::io::stderr()))?;
|
||||
let (event_tx, event_rx) = mpsc::unbounded_channel();
|
||||
let cancellation_token = CancellationToken::new();
|
||||
let task = tokio::spawn(async {});
|
||||
let mouse = false;
|
||||
let paste = false;
|
||||
Ok(Self {
|
||||
terminal,
|
||||
task,
|
||||
cancellation_token,
|
||||
event_rx,
|
||||
event_tx,
|
||||
frame_rate,
|
||||
tick_rate,
|
||||
mouse,
|
||||
paste,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tick_rate(mut self, tick_rate: f64) -> Self {
|
||||
self.tick_rate = tick_rate;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn frame_rate(mut self, frame_rate: f64) -> Self {
|
||||
self.frame_rate = frame_rate;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn mouse(mut self, mouse: bool) -> Self {
|
||||
self.mouse = mouse;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn paste(mut self, paste: bool) -> Self {
|
||||
self.paste = paste;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn start(&mut self) {
|
||||
let tick_delay = std::time::Duration::from_secs_f64(1.0 / self.tick_rate);
|
||||
let render_delay = std::time::Duration::from_secs_f64(1.0 / self.frame_rate);
|
||||
self.cancel();
|
||||
self.cancellation_token = CancellationToken::new();
|
||||
let _cancellation_token = self.cancellation_token.clone();
|
||||
let _event_tx = self.event_tx.clone();
|
||||
self.task = tokio::spawn(async move {
|
||||
let mut reader = crossterm::event::EventStream::new();
|
||||
let mut tick_interval = tokio::time::interval(tick_delay);
|
||||
let mut render_interval = tokio::time::interval(render_delay);
|
||||
_event_tx.send(Event::Init).unwrap();
|
||||
loop {
|
||||
let tick_delay = tick_interval.tick();
|
||||
let render_delay = render_interval.tick();
|
||||
let crossterm_event = reader.next().fuse();
|
||||
tokio::select! {
|
||||
_ = _cancellation_token.cancelled() => {
|
||||
break;
|
||||
}
|
||||
maybe_event = crossterm_event => {
|
||||
match maybe_event {
|
||||
Some(Ok(evt)) => {
|
||||
match evt {
|
||||
CrosstermEvent::Key(key) => {
|
||||
if key.kind == KeyEventKind::Press {
|
||||
_event_tx.send(Event::Key(key)).unwrap();
|
||||
}
|
||||
},
|
||||
CrosstermEvent::Mouse(mouse) => {
|
||||
_event_tx.send(Event::Mouse(mouse)).unwrap();
|
||||
},
|
||||
CrosstermEvent::Resize(x, y) => {
|
||||
_event_tx.send(Event::Resize(x, y)).unwrap();
|
||||
},
|
||||
CrosstermEvent::FocusLost => {
|
||||
_event_tx.send(Event::FocusLost).unwrap();
|
||||
},
|
||||
CrosstermEvent::FocusGained => {
|
||||
_event_tx.send(Event::FocusGained).unwrap();
|
||||
},
|
||||
CrosstermEvent::Paste(s) => {
|
||||
_event_tx.send(Event::Paste(s)).unwrap();
|
||||
},
|
||||
}
|
||||
}
|
||||
Some(Err(_)) => {
|
||||
_event_tx.send(Event::Error).unwrap();
|
||||
}
|
||||
None => {},
|
||||
}
|
||||
},
|
||||
_ = tick_delay => {
|
||||
_event_tx.send(Event::Tick).unwrap();
|
||||
},
|
||||
_ = render_delay => {
|
||||
_event_tx.send(Event::Render).unwrap();
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn stop(&self) -> Result<()> {
|
||||
self.cancel();
|
||||
let mut counter = 0;
|
||||
while !self.task.is_finished() {
|
||||
std::thread::sleep(Duration::from_millis(1));
|
||||
counter += 1;
|
||||
if counter > 50 {
|
||||
self.task.abort();
|
||||
}
|
||||
if counter > 100 {
|
||||
log::error!("Failed to abort task in 100 milliseconds for unknown reason");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enter(&mut self) -> Result<()> {
|
||||
crossterm::terminal::enable_raw_mode()?;
|
||||
crossterm::execute!(std::io::stderr(), EnterAlternateScreen, cursor::Hide)?;
|
||||
if self.mouse {
|
||||
crossterm::execute!(std::io::stderr(), EnableMouseCapture)?;
|
||||
}
|
||||
if self.paste {
|
||||
crossterm::execute!(std::io::stderr(), EnableBracketedPaste)?;
|
||||
}
|
||||
self.start();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn exit(&mut self) -> Result<()> {
|
||||
self.stop()?;
|
||||
if crossterm::terminal::is_raw_mode_enabled()? {
|
||||
self.flush()?;
|
||||
if self.paste {
|
||||
crossterm::execute!(std::io::stderr(), DisableBracketedPaste)?;
|
||||
}
|
||||
if self.mouse {
|
||||
crossterm::execute!(std::io::stderr(), DisableMouseCapture)?;
|
||||
}
|
||||
crossterm::execute!(std::io::stderr(), LeaveAlternateScreen, cursor::Show)?;
|
||||
crossterm::terminal::disable_raw_mode()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cancel(&self) {
|
||||
self.cancellation_token.cancel();
|
||||
}
|
||||
|
||||
pub fn suspend(&mut self) -> Result<()> {
|
||||
self.exit()?;
|
||||
#[cfg(not(windows))]
|
||||
signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn resume(&mut self) -> Result<()> {
|
||||
self.enter()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn next(&mut self) -> Option<Event> {
|
||||
self.event_rx.recv().await
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Tui {
|
||||
type Target = ratatui::Terminal<Backend<std::io::Stderr>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.terminal
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Tui {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.terminal
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Tui {
|
||||
fn drop(&mut self) {
|
||||
self.exit().unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
use crate::config::Config;
|
||||
|
||||
pub(crate) mod status_line;
|
||||
pub(crate) mod theme;
|
||||
|
||||
const BOTTOM_CONTEXT_LINES: usize = 2;
|
|
@ -0,0 +1,95 @@
|
|||
use ratatui::{
|
||||
prelude::*,
|
||||
style::Style,
|
||||
widgets::{Paragraph, Widget},
|
||||
};
|
||||
|
||||
use super::theme::{DARK_PURPLE, LIGHT_GRAY, LIGHT_PURPLE};
|
||||
|
||||
/// An optional status line
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StatusLine {
|
||||
/// Displays the current editor mode in the status line.
|
||||
mode: String,
|
||||
/// The current search buffer. Shown only in search mode.
|
||||
command: Option<String>,
|
||||
/// The style for the content of the sidebar
|
||||
style_text: Style,
|
||||
/// The style for the line itself
|
||||
style_line: Style,
|
||||
}
|
||||
|
||||
impl Default for StatusLine {
|
||||
/// Creates a new instance of [`StatusLine`].
|
||||
///
|
||||
/// This constructor initializes with default style.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mode: String::new(),
|
||||
command: None,
|
||||
style_text: Style::default().fg(LIGHT_GRAY).bg(LIGHT_PURPLE).bold(),
|
||||
style_line: Style::default().fg(LIGHT_GRAY).bg(DARK_PURPLE),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusLine {
|
||||
/// Overwrite the style for the status lines content.
|
||||
///
|
||||
/// This method allows you to customize the appearance of the
|
||||
/// status lines content.
|
||||
#[must_use]
|
||||
pub fn style_text(mut self, style: Style) -> Self {
|
||||
self.style_text = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Overwrite the style for the status lines.
|
||||
///
|
||||
/// This method allows you to customize the appearance of the
|
||||
/// status line.
|
||||
#[must_use]
|
||||
pub fn style_line(mut self, style: Style) -> Self {
|
||||
self.style_line = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Overwrite the mode content for the status line.
|
||||
///
|
||||
/// This method is used internally to dynamically set the editors mode.
|
||||
#[must_use]
|
||||
pub fn mode<S: Into<String>>(mut self, mode: S) -> Self {
|
||||
self.mode = mode.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Overwrite the search content for the status line.
|
||||
///
|
||||
/// This method is used internally to dynamically set the editors mode.
|
||||
#[must_use]
|
||||
pub fn command<S: Into<String>>(mut self, search: Option<S>) -> Self {
|
||||
self.command = command.map(Into::into);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for StatusLine {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
// Split the layout horizontally.
|
||||
let constraints = [Constraint::Length(10), Constraint::Min(0)];
|
||||
let [left, right] = Layout::horizontal(constraints).areas(area);
|
||||
|
||||
// Build the content and block widgets
|
||||
let mode_paragraph = Paragraph::new(Line::from(Span::from(self.mode)))
|
||||
.alignment(Alignment::Center)
|
||||
.style(self.style_text);
|
||||
let command_text = self.command.map_or(String::new(), |s| format!(":{s}"));
|
||||
let command_paragraph = Paragraph::new(Line::from(Span::from(command_text)))
|
||||
.alignment(Alignment::Left)
|
||||
.style(self.style_line);
|
||||
|
||||
// Determine the alignment position
|
||||
mode_paragraph.render(left, buf);
|
||||
command_paragraph.render(right, buf);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
use ratatui::{
|
||||
prelude::*,
|
||||
style::Style,
|
||||
widgets::{Paragraph, Widget},
|
||||
};
|
||||
|
||||
use super::theme::{DARK_PURPLE, LIGHT_GRAY, LIGHT_PURPLE};
|
||||
|
||||
/// An optional status line
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TabLine {
|
||||
/// Displays the current editor mode in the status line.
|
||||
mode: String,
|
||||
/// The current search buffer. Shown only in search mode.
|
||||
search: Option<String>,
|
||||
/// The style for the content of the sidebar
|
||||
style_text: Style,
|
||||
/// The style for the line itself
|
||||
style_line: Style,
|
||||
// Whether to align content to the left (true) or the right (false)
|
||||
align_left: bool,
|
||||
}
|
||||
|
||||
impl Default for StatusLine {
|
||||
/// Creates a new instance of [`StatusLine`].
|
||||
///
|
||||
/// This constructor initializes with default style.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mode: String::new(),
|
||||
search: None,
|
||||
style_text: Style::default().fg(LIGHT_GRAY).bg(LIGHT_PURPLE).bold(),
|
||||
style_line: Style::default().fg(LIGHT_GRAY).bg(DARK_PURPLE),
|
||||
align_left: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusLine {
|
||||
/// Overwrite the style for the status lines content.
|
||||
///
|
||||
/// This method allows you to customize the appearance of the
|
||||
/// status lines content.
|
||||
#[must_use]
|
||||
pub fn style_text(mut self, style: Style) -> Self {
|
||||
self.style_text = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Overwrite the style for the status lines.
|
||||
///
|
||||
/// This method allows you to customize the appearance of the
|
||||
/// status line.
|
||||
#[must_use]
|
||||
pub fn style_line(mut self, style: Style) -> Self {
|
||||
self.style_line = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Overwrite the mode content for the status line.
|
||||
///
|
||||
/// This method is used internally to dynamically set the editors mode.
|
||||
#[must_use]
|
||||
pub fn mode<S: Into<String>>(mut self, mode: S) -> Self {
|
||||
self.mode = mode.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Overwrite the search content for the status line.
|
||||
///
|
||||
/// This method is used internally to dynamically set the editors mode.
|
||||
#[must_use]
|
||||
pub fn search<S: Into<String>>(mut self, search: Option<S>) -> Self {
|
||||
self.search = search.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the alignment for the status line content.
|
||||
///
|
||||
/// Set to true to align content to the left, false to align to the right.
|
||||
#[must_use]
|
||||
pub fn align_left(mut self, align_left: bool) -> Self {
|
||||
self.align_left = align_left;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for StatusLine {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
// Split the layout horizontally.
|
||||
let constraints = if self.align_left {
|
||||
[Constraint::Length(10), Constraint::Min(0)]
|
||||
} else {
|
||||
[Constraint::Min(0), Constraint::Length(10)]
|
||||
};
|
||||
let [left, right] = Layout::horizontal(constraints).areas(area);
|
||||
|
||||
// Build the content and block widgets
|
||||
let mode_paragraph = Paragraph::new(Line::from(Span::from(self.mode)))
|
||||
.alignment(Alignment::Center)
|
||||
.style(self.style_text);
|
||||
let search_text = self.search.map_or(String::new(), |s| format!("/{s}"));
|
||||
let search_paragraph = Paragraph::new(Line::from(Span::from(search_text)))
|
||||
.alignment(Alignment::Left)
|
||||
.style(self.style_line);
|
||||
|
||||
// Determine the alignment position
|
||||
if self.align_left {
|
||||
mode_paragraph.render(left, buf);
|
||||
search_paragraph.render(right, buf);
|
||||
} else {
|
||||
search_paragraph.render(left, buf);
|
||||
mode_paragraph.render(right, buf);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
use super::StatusLine;
|
||||
use ratatui::style::{Color, Style};
|
||||
|
||||
// Tailwind slate c100
|
||||
pub(crate) const LIGHT_GRAY: Color = Color::Rgb(248, 250, 252);
|
||||
|
||||
// Tailwind slate c50
|
||||
pub(crate) const WHITE: Color = Color::Rgb(248, 250, 252);
|
||||
|
||||
// Tailwind slate c900
|
||||
pub(crate) const DARK_BLUE: Color = Color::Rgb(15, 23, 42);
|
||||
|
||||
// Tailwind purple c700 & c900
|
||||
pub(crate) const LIGHT_PURPLE: Color = Color::Rgb(126, 34, 206);
|
||||
pub(crate) const DARK_PURPLE: Color = Color::Rgb(88, 28, 135);
|
||||
|
||||
// Tailwind yellow c400
|
||||
pub(crate) const YELLOW: Color = Color::Rgb(250, 204, 21);
|
Loading…
Reference in New Issue