mirror of
https://github.com/gitui-org/gitui
synced 2026-05-23 17:08:21 +00:00
Bump tui: 0.9 -> 0.12 (#289)
This commit is contained in:
parent
8b903ac4a7
commit
93168639fe
19 changed files with 349 additions and 381 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -1282,15 +1282,13 @@ checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed"
|
|||
|
||||
[[package]]
|
||||
name = "tui"
|
||||
version = "0.9.5"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9533d39bef0ae8f510e8a99d78702e68d1bbf0b98a78ec9740509d287010ae1e"
|
||||
checksum = "c2eaeee894a1e9b90f80aa466fe59154fdb471980b5e104d8836fcea309ae17e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cassowary",
|
||||
"crossterm",
|
||||
"either",
|
||||
"itertools",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ scopetime = { path = "./scopetime", version = "0.1" }
|
|||
asyncgit = { path = "./asyncgit", version = "0.10" }
|
||||
crossterm = { version = "0.17", features = [ "serde" ] }
|
||||
clap = { version = "2.33", default-features = false }
|
||||
tui = { version = "0.9", default-features = false, features = ['crossterm'] }
|
||||
tui = { version = "0.12", default-features = false, features = ['crossterm'] }
|
||||
bytesize = { version = "1.0.1", default-features = false}
|
||||
itertools = "0.9"
|
||||
rayon-core = "1.8"
|
||||
|
|
|
|||
22
src/app.rs
22
src/app.rs
|
|
@ -27,6 +27,7 @@ use std::{
|
|||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Constraint, Direction, Layout, Margin, Rect},
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, Borders, Tabs},
|
||||
Frame,
|
||||
};
|
||||
|
|
@ -599,24 +600,27 @@ impl App {
|
|||
horizontal: 1,
|
||||
});
|
||||
|
||||
let tabs = &[
|
||||
strings::tab_status(&self.key_config),
|
||||
strings::tab_log(&self.key_config),
|
||||
strings::tab_stashing(&self.key_config),
|
||||
strings::tab_stashes(&self.key_config),
|
||||
];
|
||||
let tabs = [
|
||||
Span::raw(strings::tab_status(&self.key_config)),
|
||||
Span::raw(strings::tab_log(&self.key_config)),
|
||||
Span::raw(strings::tab_stashing(&self.key_config)),
|
||||
Span::raw(strings::tab_stashes(&self.key_config)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Spans::from)
|
||||
.collect();
|
||||
|
||||
f.render_widget(
|
||||
Tabs::default()
|
||||
Tabs::new(tabs)
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::BOTTOM)
|
||||
.border_style(self.theme.block(false)),
|
||||
)
|
||||
.titles(tabs)
|
||||
.style(self.theme.tab(false))
|
||||
.highlight_style(self.theme.tab(true))
|
||||
.divider(&strings::tab_divider(&self.key_config))
|
||||
.divider(strings::tab_divider(&self.key_config))
|
||||
.select(self.tab),
|
||||
r,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ use std::borrow::Cow;
|
|||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Alignment, Rect},
|
||||
widgets::{Paragraph, Text},
|
||||
text::{Span, Spans},
|
||||
widgets::Paragraph,
|
||||
Frame,
|
||||
};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
|
@ -142,28 +143,44 @@ impl CommandBar {
|
|||
if r.width < MORE_WIDTH {
|
||||
return;
|
||||
}
|
||||
|
||||
let splitter = Text::Raw(Cow::from(strings::cmd_splitter(
|
||||
let splitter = Span::raw(Cow::from(strings::cmd_splitter(
|
||||
&self.key_config,
|
||||
)));
|
||||
|
||||
let texts = self
|
||||
.draw_list
|
||||
.iter()
|
||||
.map(|c| match c {
|
||||
DrawListEntry::Command(c) => Text::Styled(
|
||||
Cow::from(c.txt.as_str()),
|
||||
self.theme.commandbar(c.enabled, c.line),
|
||||
),
|
||||
DrawListEntry::LineBreak => {
|
||||
Text::Raw(Cow::from("\n"))
|
||||
}
|
||||
DrawListEntry::Splitter => splitter.clone(),
|
||||
.split(|c| match c {
|
||||
DrawListEntry::LineBreak => true,
|
||||
_ => false,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.map(|c_arr| {
|
||||
Spans::from(
|
||||
c_arr
|
||||
.iter()
|
||||
.map(|c| match c {
|
||||
DrawListEntry::Command(c) => {
|
||||
Span::styled(
|
||||
Cow::from(c.txt.as_str()),
|
||||
self.theme.commandbar(
|
||||
c.enabled, c.line,
|
||||
),
|
||||
)
|
||||
}
|
||||
DrawListEntry::LineBreak => {
|
||||
// Doesn't exist in split array
|
||||
Span::raw("")
|
||||
}
|
||||
DrawListEntry::Splitter => {
|
||||
splitter.clone()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Span>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Spans>>();
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(texts.iter()).alignment(Alignment::Left),
|
||||
Paragraph::new(texts).alignment(Alignment::Left),
|
||||
r,
|
||||
);
|
||||
|
||||
|
|
@ -176,14 +193,13 @@ impl CommandBar {
|
|||
);
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(
|
||||
vec![Text::Raw(Cow::from(if self.expanded {
|
||||
Paragraph::new(Spans::from(vec![Span::raw(
|
||||
Cow::from(if self.expanded {
|
||||
"less [.]"
|
||||
} else {
|
||||
"more [.]"
|
||||
}))]
|
||||
.iter(),
|
||||
)
|
||||
}),
|
||||
)]))
|
||||
.alignment(Alignment::Right),
|
||||
r,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -78,11 +78,6 @@ impl CommandInfo {
|
|||
res
|
||||
}
|
||||
|
||||
///
|
||||
pub fn print(&self, out: &mut String) {
|
||||
out.push_str(&self.text.name);
|
||||
}
|
||||
|
||||
///
|
||||
pub const fn show_in_quickbar(&self) -> bool {
|
||||
self.quick_bar && self.available
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@ use asyncgit::{
|
|||
};
|
||||
use crossterm::event::Event;
|
||||
use itertools::Itertools;
|
||||
use std::clone::Clone;
|
||||
use std::{borrow::Cow, cell::Cell};
|
||||
use sync::CommitTags;
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
style::{Modifier, Style},
|
||||
widgets::Text,
|
||||
text::{Span, Spans, Text},
|
||||
Frame,
|
||||
};
|
||||
|
||||
enum Detail {
|
||||
Author,
|
||||
Date,
|
||||
|
|
@ -122,7 +122,7 @@ impl DetailsComponent {
|
|||
|
||||
fn get_theme_for_line(&self, bold: bool) -> Style {
|
||||
if bold {
|
||||
self.theme.text(true, false).modifier(Modifier::BOLD)
|
||||
self.theme.text(true, false).add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
self.theme.text(true, false)
|
||||
}
|
||||
|
|
@ -132,12 +132,7 @@ impl DetailsComponent {
|
|||
&self,
|
||||
width: usize,
|
||||
height: usize,
|
||||
) -> Vec<Text> {
|
||||
let newline = Text::Styled(
|
||||
String::from("\n").into(),
|
||||
self.theme.text(true, false),
|
||||
);
|
||||
|
||||
) -> Vec<Spans> {
|
||||
let (wrapped_title, wrapped_message) =
|
||||
self.get_wrapped_lines(width);
|
||||
|
||||
|
|
@ -148,36 +143,35 @@ impl DetailsComponent {
|
|||
.skip(self.scroll_top.get())
|
||||
.take(height)
|
||||
.map(|(i, line)| {
|
||||
Text::Styled(
|
||||
Spans::from(vec![Span::styled(
|
||||
line.clone(),
|
||||
self.get_theme_for_line(i < wrapped_title.len()),
|
||||
)
|
||||
)])
|
||||
})
|
||||
.intersperse(newline)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn style_detail(&self, field: &Detail) -> Text {
|
||||
fn style_detail(&self, field: &Detail) -> Span {
|
||||
match field {
|
||||
Detail::Author => Text::Styled(
|
||||
Detail::Author => Span::styled(
|
||||
Cow::from(strings::commit::details_author(
|
||||
&self.key_config,
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
Detail::Date => Text::Styled(
|
||||
Detail::Date => Span::styled(
|
||||
Cow::from(strings::commit::details_date(
|
||||
&self.key_config,
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
Detail::Commiter => Text::Styled(
|
||||
Detail::Commiter => Span::styled(
|
||||
Cow::from(strings::commit::details_committer(
|
||||
&self.key_config,
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
Detail::Sha => Text::Styled(
|
||||
Detail::Sha => Span::styled(
|
||||
Cow::from(strings::commit::details_tags(
|
||||
&self.key_config,
|
||||
)),
|
||||
|
|
@ -186,84 +180,88 @@ impl DetailsComponent {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_text_info(&self) -> Vec<Text> {
|
||||
let new_line = Text::Raw(Cow::from("\n"));
|
||||
|
||||
fn get_text_info(&self) -> Vec<Spans> {
|
||||
if let Some(ref data) = self.data {
|
||||
let mut res = vec![
|
||||
self.style_detail(&Detail::Author),
|
||||
Text::Styled(
|
||||
Cow::from(format!(
|
||||
"{} <{}>",
|
||||
data.author.name, data.author.email
|
||||
)),
|
||||
self.theme.text(true, false),
|
||||
),
|
||||
new_line.clone(),
|
||||
self.style_detail(&Detail::Date),
|
||||
Text::Styled(
|
||||
Cow::from(time_to_string(
|
||||
data.author.time,
|
||||
false,
|
||||
)),
|
||||
self.theme.text(true, false),
|
||||
),
|
||||
new_line.clone(),
|
||||
];
|
||||
|
||||
if let Some(ref committer) = data.committer {
|
||||
res.extend(vec![
|
||||
self.style_detail(&Detail::Commiter),
|
||||
Text::Styled(
|
||||
Spans::from(vec![
|
||||
self.style_detail(&Detail::Author),
|
||||
Span::styled(
|
||||
Cow::from(format!(
|
||||
"{} <{}>",
|
||||
committer.name, committer.email
|
||||
data.author.name, data.author.email
|
||||
)),
|
||||
self.theme.text(true, false),
|
||||
),
|
||||
new_line.clone(),
|
||||
]),
|
||||
Spans::from(vec![
|
||||
self.style_detail(&Detail::Date),
|
||||
Text::Styled(
|
||||
Span::styled(
|
||||
Cow::from(time_to_string(
|
||||
committer.time,
|
||||
data.author.time,
|
||||
false,
|
||||
)),
|
||||
self.theme.text(true, false),
|
||||
),
|
||||
new_line.clone(),
|
||||
]),
|
||||
];
|
||||
|
||||
if let Some(ref committer) = data.committer {
|
||||
res.extend(vec![
|
||||
Spans::from(vec![
|
||||
self.style_detail(&Detail::Commiter),
|
||||
Span::styled(
|
||||
Cow::from(format!(
|
||||
"{} <{}>",
|
||||
committer.name, committer.email
|
||||
)),
|
||||
self.theme.text(true, false),
|
||||
),
|
||||
]),
|
||||
Spans::from(vec![
|
||||
self.style_detail(&Detail::Date),
|
||||
Span::styled(
|
||||
Cow::from(time_to_string(
|
||||
committer.time,
|
||||
false,
|
||||
)),
|
||||
self.theme.text(true, false),
|
||||
),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
res.extend(vec![
|
||||
Text::Styled(
|
||||
res.push(Spans::from(vec![
|
||||
Span::styled(
|
||||
Cow::from(strings::commit::details_sha(
|
||||
&self.key_config,
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
Text::Styled(
|
||||
Span::styled(
|
||||
Cow::from(data.hash.clone()),
|
||||
self.theme.text(true, false),
|
||||
),
|
||||
new_line.clone(),
|
||||
]);
|
||||
]));
|
||||
|
||||
if !self.tags.is_empty() {
|
||||
res.push(self.style_detail(&Detail::Sha));
|
||||
res.extend(
|
||||
res.push(Spans::from(
|
||||
self.style_detail(&Detail::Sha),
|
||||
));
|
||||
res.push(Spans::from(
|
||||
self.tags
|
||||
.iter()
|
||||
.map(|tag| {
|
||||
Text::Styled(
|
||||
Span::styled(
|
||||
Cow::from(tag),
|
||||
self.theme.text(true, false),
|
||||
)
|
||||
})
|
||||
.intersperse(Text::Styled(
|
||||
.intersperse(Span::styled(
|
||||
Cow::from(","),
|
||||
self.theme.text(true, false),
|
||||
)),
|
||||
);
|
||||
))
|
||||
.collect::<Vec<Span>>(),
|
||||
));
|
||||
}
|
||||
|
||||
res
|
||||
|
|
@ -323,7 +321,7 @@ impl DrawableComponent for DetailsComponent {
|
|||
&strings::commit::details_info_title(
|
||||
&self.key_config,
|
||||
),
|
||||
self.get_text_info().iter(),
|
||||
Text::from(self.get_text_info()),
|
||||
&self.theme,
|
||||
false,
|
||||
),
|
||||
|
|
@ -349,7 +347,7 @@ impl DrawableComponent for DetailsComponent {
|
|||
&strings::commit::details_message_title(
|
||||
&self.key_config,
|
||||
),
|
||||
wrapped_lines.iter(),
|
||||
Text::from(wrapped_lines),
|
||||
&self.theme,
|
||||
self.focused,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ use std::{
|
|||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Alignment, Rect},
|
||||
widgets::{Block, Borders, Paragraph, Text},
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, Borders, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
|
@ -178,22 +179,22 @@ impl CommitList {
|
|||
self.scroll_state.1 = speed.min(SCROLL_SPEED_MAX);
|
||||
}
|
||||
|
||||
fn add_entry<'b>(
|
||||
e: &'b LogEntry,
|
||||
fn get_entry_to_add<'a>(
|
||||
e: &'a LogEntry,
|
||||
selected: bool,
|
||||
txt: &mut Vec<Text<'b>>,
|
||||
tags: Option<String>,
|
||||
theme: &Theme,
|
||||
width: usize,
|
||||
) {
|
||||
) -> Spans<'a> {
|
||||
let mut txt: Vec<Span> = Vec::new();
|
||||
txt.reserve(ELEMENTS_PER_LINE);
|
||||
|
||||
let splitter_txt = Cow::from(" ");
|
||||
let splitter =
|
||||
Text::Styled(splitter_txt, theme.text(true, selected));
|
||||
Span::styled(splitter_txt, theme.text(true, selected));
|
||||
|
||||
// commit hash
|
||||
txt.push(Text::Styled(
|
||||
txt.push(Span::styled(
|
||||
Cow::from(e.hash_short.as_str()),
|
||||
theme.commit_hash(selected),
|
||||
));
|
||||
|
|
@ -201,7 +202,7 @@ impl CommitList {
|
|||
txt.push(splitter.clone());
|
||||
|
||||
// commit timestamp
|
||||
txt.push(Text::Styled(
|
||||
txt.push(Span::styled(
|
||||
Cow::from(e.time.as_str()),
|
||||
theme.commit_time(selected),
|
||||
));
|
||||
|
|
@ -213,15 +214,15 @@ impl CommitList {
|
|||
let author = string_width_align(&e.author, author_width);
|
||||
|
||||
// commit author
|
||||
txt.push(Text::Styled(
|
||||
author.into(),
|
||||
txt.push(Span::styled::<String>(
|
||||
author,
|
||||
theme.commit_author(selected),
|
||||
));
|
||||
|
||||
txt.push(splitter.clone());
|
||||
|
||||
// commit tags
|
||||
txt.push(Text::Styled(
|
||||
txt.push(Span::styled(
|
||||
Cow::from(if let Some(tags) = tags {
|
||||
format!(" {}", tags)
|
||||
} else {
|
||||
|
|
@ -233,17 +234,17 @@ impl CommitList {
|
|||
txt.push(splitter);
|
||||
|
||||
// commit msg
|
||||
txt.push(Text::Styled(
|
||||
txt.push(Span::styled(
|
||||
Cow::from(e.msg.as_str()),
|
||||
theme.text(true, selected),
|
||||
));
|
||||
txt.push(Text::Raw(Cow::from("\n")));
|
||||
Spans::from(txt)
|
||||
}
|
||||
|
||||
fn get_text(&self, height: usize, width: usize) -> Vec<Text> {
|
||||
fn get_text(&self, height: usize, width: usize) -> Vec<Spans> {
|
||||
let selection = self.relative_selection();
|
||||
|
||||
let mut txt = Vec::with_capacity(height * ELEMENTS_PER_LINE);
|
||||
let mut txt: Vec<Spans> = Vec::with_capacity(height);
|
||||
|
||||
for (idx, e) in self
|
||||
.items
|
||||
|
|
@ -257,15 +258,13 @@ impl CommitList {
|
|||
.as_ref()
|
||||
.and_then(|t| t.get(&e.id))
|
||||
.map(|tags| tags.join(" "));
|
||||
|
||||
Self::add_entry(
|
||||
txt.push(Self::get_entry_to_add(
|
||||
e,
|
||||
idx + self.scroll_top.get() == selection,
|
||||
&mut txt,
|
||||
tags,
|
||||
&self.theme,
|
||||
width,
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
txt
|
||||
|
|
@ -314,15 +313,16 @@ impl DrawableComponent for CommitList {
|
|||
self.get_text(
|
||||
height_in_lines,
|
||||
current_size.0 as usize,
|
||||
)
|
||||
.iter(),
|
||||
),
|
||||
)
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title(title.as_str())
|
||||
.border_style(self.theme.block(true))
|
||||
.title_style(self.theme.title(true)),
|
||||
.title(Span::styled(
|
||||
title.as_str(),
|
||||
self.theme.title(true),
|
||||
))
|
||||
.border_style(self.theme.block(true)),
|
||||
)
|
||||
.alignment(Alignment::Left),
|
||||
area,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ use tui::{
|
|||
backend::Backend,
|
||||
layout::Rect,
|
||||
symbols,
|
||||
widgets::{Block, Borders, Paragraph, Text},
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, Borders, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
|
||||
|
|
@ -299,33 +300,37 @@ impl DiffComponent {
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
fn get_text(&self, width: u16, height: u16) -> Result<Vec<Text>> {
|
||||
let mut res = Vec::new();
|
||||
fn get_text(
|
||||
&self,
|
||||
width: u16,
|
||||
height: u16,
|
||||
) -> Result<Vec<Spans>> {
|
||||
let mut res: Vec<Spans> = Vec::new();
|
||||
if let Some(diff) = &self.diff {
|
||||
if diff.hunks.is_empty() {
|
||||
let is_positive = diff.size_delta >= 0;
|
||||
let delta_byte_size =
|
||||
ByteSize::b(diff.size_delta.abs() as u64);
|
||||
let sign = if is_positive { "+" } else { "-" };
|
||||
res.extend(vec![
|
||||
Text::Raw(Cow::from("size: ")),
|
||||
Text::Styled(
|
||||
res.extend(vec![Spans::from(vec![
|
||||
Span::raw(Cow::from("size: ")),
|
||||
Span::styled(
|
||||
Cow::from(format!(
|
||||
"{}",
|
||||
ByteSize::b(diff.sizes.0)
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
Text::Raw(Cow::from(" -> ")),
|
||||
Text::Styled(
|
||||
Span::raw(Cow::from(" -> ")),
|
||||
Span::styled(
|
||||
Cow::from(format!(
|
||||
"{}",
|
||||
ByteSize::b(diff.sizes.1)
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
Text::Raw(Cow::from(" (")),
|
||||
Text::Styled(
|
||||
Span::raw(Cow::from(" (")),
|
||||
Span::styled(
|
||||
Cow::from(format!(
|
||||
"{}{:}",
|
||||
sign, delta_byte_size
|
||||
|
|
@ -339,8 +344,8 @@ impl DiffComponent {
|
|||
false,
|
||||
),
|
||||
),
|
||||
Text::Raw(Cow::from(")")),
|
||||
]);
|
||||
Span::raw(Cow::from(")")),
|
||||
])]);
|
||||
} else {
|
||||
let min = self.scroll_top.get();
|
||||
let max = min + height as usize;
|
||||
|
|
@ -370,8 +375,7 @@ impl DiffComponent {
|
|||
if line_cursor >= min
|
||||
&& line_cursor <= max
|
||||
{
|
||||
Self::add_line(
|
||||
&mut res,
|
||||
res.push(Self::get_line_to_add(
|
||||
width,
|
||||
line,
|
||||
self.focused()
|
||||
|
|
@ -381,7 +385,7 @@ impl DiffComponent {
|
|||
hunk_selected,
|
||||
i == hunk_len as usize - 1,
|
||||
&self.theme,
|
||||
);
|
||||
));
|
||||
lines_added += 1;
|
||||
}
|
||||
|
||||
|
|
@ -396,36 +400,30 @@ impl DiffComponent {
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
fn add_line(
|
||||
text: &mut Vec<Text>,
|
||||
fn get_line_to_add<'a>(
|
||||
width: u16,
|
||||
line: &DiffLine,
|
||||
line: &'a DiffLine,
|
||||
selected: bool,
|
||||
selected_hunk: bool,
|
||||
end_of_hunk: bool,
|
||||
theme: &SharedTheme,
|
||||
) {
|
||||
{
|
||||
let style = theme.diff_hunk_marker(selected_hunk);
|
||||
) -> Spans<'a> {
|
||||
let style = theme.diff_hunk_marker(selected_hunk);
|
||||
|
||||
if end_of_hunk {
|
||||
text.push(Text::Styled(
|
||||
Cow::from(symbols::line::BOTTOM_LEFT),
|
||||
let left_side_of_line = if end_of_hunk {
|
||||
Span::styled(Cow::from(symbols::line::BOTTOM_LEFT), style)
|
||||
} else {
|
||||
match line.line_type {
|
||||
DiffLineType::Header => Span::styled(
|
||||
Cow::from(symbols::line::TOP_LEFT),
|
||||
style,
|
||||
));
|
||||
} else {
|
||||
text.push(match line.line_type {
|
||||
DiffLineType::Header => Text::Styled(
|
||||
Cow::from(symbols::line::TOP_LEFT),
|
||||
style,
|
||||
),
|
||||
_ => Text::Styled(
|
||||
Cow::from(symbols::line::VERTICAL),
|
||||
style,
|
||||
),
|
||||
});
|
||||
),
|
||||
_ => Span::styled(
|
||||
Cow::from(symbols::line::VERTICAL),
|
||||
style,
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let trimmed =
|
||||
line.content.trim_matches(|c| c == '\n' || c == '\r');
|
||||
|
|
@ -440,10 +438,13 @@ impl DiffComponent {
|
|||
//TODO: allow customize tabsize
|
||||
let content = Cow::from(filled.replace("\t", " "));
|
||||
|
||||
text.push(Text::Styled(
|
||||
content,
|
||||
theme.diff_line(line.line_type, selected),
|
||||
));
|
||||
Spans::from(vec![
|
||||
left_side_of_line,
|
||||
Span::styled(
|
||||
content,
|
||||
theme.diff_line(line.line_type, selected),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
const fn hunk_visible(
|
||||
|
|
@ -564,21 +565,23 @@ impl DrawableComponent for DiffComponent {
|
|||
);
|
||||
|
||||
let txt = if self.pending {
|
||||
vec![Text::Styled(
|
||||
vec![Spans::from(vec![Span::styled(
|
||||
Cow::from(strings::loading_text(&self.key_config)),
|
||||
self.theme.text(false, false),
|
||||
)]
|
||||
)])]
|
||||
} else {
|
||||
self.get_text(r.width, self.current_size.get().1)?
|
||||
};
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(txt.iter()).block(
|
||||
Paragraph::new(txt).block(
|
||||
Block::default()
|
||||
.title(title.as_str())
|
||||
.title(Span::styled(
|
||||
title.as_str(),
|
||||
self.theme.title(self.focused),
|
||||
))
|
||||
.borders(Borders::ALL)
|
||||
.border_style(self.theme.block(self.focused))
|
||||
.title_style(self.theme.title(self.focused)),
|
||||
.border_style(self.theme.block(self.focused)),
|
||||
),
|
||||
r,
|
||||
);
|
||||
|
|
@ -715,33 +718,3 @@ impl Component for DiffComponent {
|
|||
self.focused = focus
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_lineendings() {
|
||||
let mut text = Vec::new();
|
||||
DiffComponent::add_line(
|
||||
&mut text,
|
||||
10,
|
||||
&DiffLine {
|
||||
content: String::from("line 1\r\n"),
|
||||
line_type: DiffLineType::None,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
&SharedTheme::default(),
|
||||
);
|
||||
|
||||
assert_eq!(text.len(), 2);
|
||||
|
||||
if let Text::Styled(c, _) = &text[1] {
|
||||
assert_eq!(c, "line 1\n");
|
||||
} else {
|
||||
panic!("err")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ use std::{env, io, path::Path, process::Command};
|
|||
use tui::{
|
||||
backend::Backend,
|
||||
layout::Rect,
|
||||
widgets::{Block, BorderType, Borders, Clear, Paragraph, Text},
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, BorderType, Borders, Clear, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
|
||||
|
|
@ -99,19 +100,23 @@ impl DrawableComponent for ExternalEditorComponent {
|
|||
_rect: Rect,
|
||||
) -> Result<()> {
|
||||
if self.visible {
|
||||
let txt = vec![Text::Raw(
|
||||
strings::msg_opening_editor(&self.key_config).into(),
|
||||
)];
|
||||
let txt = Spans::from(
|
||||
strings::msg_opening_editor(&self.key_config)
|
||||
.split('\n')
|
||||
.map(|string| {
|
||||
Span::raw::<String>(string.to_string())
|
||||
})
|
||||
.collect::<Vec<Span>>(),
|
||||
);
|
||||
|
||||
let area = ui::centered_rect_absolute(25, 3, f.size());
|
||||
f.render_widget(Clear, area);
|
||||
f.render_widget(
|
||||
Paragraph::new(txt.iter())
|
||||
Paragraph::new(txt)
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Thick)
|
||||
.title_style(self.theme.title(true))
|
||||
.border_style(self.theme.block(true)),
|
||||
)
|
||||
.style(self.theme.text_danger()),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use anyhow::Result;
|
|||
use asyncgit::{hash, StatusItem, StatusItemType};
|
||||
use crossterm::event::Event;
|
||||
use std::{borrow::Cow, cell::Cell, convert::From, path::Path};
|
||||
use tui::{backend::Backend, layout::Rect, widgets::Text, Frame};
|
||||
use tui::{backend::Backend, layout::Rect, text::Span, Frame};
|
||||
|
||||
///
|
||||
pub struct FileTreeComponent {
|
||||
|
|
@ -153,7 +153,7 @@ impl FileTreeComponent {
|
|||
width: u16,
|
||||
selected: bool,
|
||||
theme: &'b SharedTheme,
|
||||
) -> Option<Text<'b>> {
|
||||
) -> Option<Span<'b>> {
|
||||
let indent_str = if indent == 0 {
|
||||
String::from("")
|
||||
} else {
|
||||
|
|
@ -185,7 +185,7 @@ impl FileTreeComponent {
|
|||
format!("{} {}{}", status_char, indent_str, file)
|
||||
};
|
||||
|
||||
Some(Text::Styled(
|
||||
Some(Span::styled(
|
||||
Cow::from(txt),
|
||||
theme.item(status_item.status, selected),
|
||||
))
|
||||
|
|
@ -210,7 +210,7 @@ impl FileTreeComponent {
|
|||
)
|
||||
};
|
||||
|
||||
Some(Text::Styled(
|
||||
Some(Span::styled(
|
||||
Cow::from(txt),
|
||||
theme.text(true, selected),
|
||||
))
|
||||
|
|
@ -312,7 +312,7 @@ impl DrawableComponent for FileTreeComponent {
|
|||
r: Rect,
|
||||
) -> Result<()> {
|
||||
if self.pending {
|
||||
let items = vec![Text::Styled(
|
||||
let items = vec![Span::styled(
|
||||
Cow::from(strings::loading_text(&self.key_config)),
|
||||
self.theme.text(false, false),
|
||||
)];
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ use tui::{
|
|||
backend::Backend,
|
||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||
style::{Modifier, Style},
|
||||
widgets::{Block, BorderType, Borders, Clear, Paragraph, Text},
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, BorderType, Borders, Clear, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
|
||||
|
|
@ -45,7 +46,7 @@ impl DrawableComponent for HelpComponent {
|
|||
f.render_widget(Clear, area);
|
||||
f.render_widget(
|
||||
Block::default()
|
||||
.title(&strings::help_title(&self.key_config))
|
||||
.title(strings::help_title(&self.key_config))
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Thick),
|
||||
area,
|
||||
|
|
@ -62,23 +63,17 @@ impl DrawableComponent for HelpComponent {
|
|||
.split(area);
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(self.get_text().iter())
|
||||
.scroll(scroll)
|
||||
Paragraph::new(self.get_text())
|
||||
.scroll((scroll, 0))
|
||||
.alignment(Alignment::Left),
|
||||
chunks[0],
|
||||
);
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(
|
||||
vec![Text::Styled(
|
||||
Cow::from(format!(
|
||||
"gitui {}",
|
||||
Version::new(),
|
||||
)),
|
||||
Style::default(),
|
||||
)]
|
||||
.iter(),
|
||||
)
|
||||
Paragraph::new(Spans::from(vec![Span::styled(
|
||||
Cow::from(format!("gitui {}", Version::new(),)),
|
||||
Style::default(),
|
||||
)]))
|
||||
.alignment(Alignment::Right),
|
||||
chunks[1],
|
||||
);
|
||||
|
|
@ -209,50 +204,43 @@ impl HelpComponent {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_text(&self) -> Vec<Text> {
|
||||
let mut txt = Vec::new();
|
||||
fn get_text(&self) -> Vec<Spans> {
|
||||
let mut txt: Vec<Spans> = Vec::new();
|
||||
|
||||
let mut processed = 0_u16;
|
||||
|
||||
for (key, group) in
|
||||
&self.cmds.iter().group_by(|e| e.text.group)
|
||||
{
|
||||
txt.push(Text::Styled(
|
||||
Cow::from(format!("{}\n", key)),
|
||||
Style::default().modifier(Modifier::REVERSED),
|
||||
));
|
||||
txt.push(Spans::from(Span::styled(
|
||||
Cow::from(key.to_string()),
|
||||
Style::default().add_modifier(Modifier::REVERSED),
|
||||
)));
|
||||
|
||||
txt.extend(
|
||||
group
|
||||
.sorted_by_key(|e| e.order)
|
||||
.map(|e| {
|
||||
let is_selected = self.selection == processed;
|
||||
for command_info in group {
|
||||
let is_selected = self.selection == processed;
|
||||
|
||||
processed += 1;
|
||||
processed += 1;
|
||||
|
||||
let mut out = String::from(if is_selected {
|
||||
">"
|
||||
} else {
|
||||
" "
|
||||
});
|
||||
txt.push(Spans::from(Span::styled(
|
||||
Cow::from(if is_selected {
|
||||
format!(">{}", command_info.text.name)
|
||||
} else {
|
||||
format!(" {}", command_info.text.name)
|
||||
}),
|
||||
self.theme.text(true, is_selected),
|
||||
)));
|
||||
|
||||
e.print(&mut out);
|
||||
out.push('\n');
|
||||
|
||||
if is_selected {
|
||||
out.push_str(
|
||||
format!(" {}\n", e.text.desc)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
Text::Styled(
|
||||
Cow::from(out),
|
||||
self.theme.text(true, is_selected),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
if is_selected {
|
||||
txt.push(Spans::from(Span::styled(
|
||||
Cow::from(format!(
|
||||
" {}\n",
|
||||
command_info.text.desc
|
||||
)),
|
||||
self.theme.text(true, is_selected),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
txt
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ use crate::ui::style::Theme;
|
|||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Alignment, Rect},
|
||||
widgets::{Block, BorderType, Borders, Paragraph, Text},
|
||||
text::{Span, Spans, Text},
|
||||
widgets::{Block, BorderType, Borders, Paragraph, Wrap},
|
||||
Frame,
|
||||
};
|
||||
|
||||
|
|
@ -183,44 +184,36 @@ pub trait Component {
|
|||
}
|
||||
}
|
||||
|
||||
fn dialog_paragraph<'a, 't, T>(
|
||||
fn dialog_paragraph<'a>(
|
||||
title: &'a str,
|
||||
content: T,
|
||||
content: Text<'a>,
|
||||
theme: &Theme,
|
||||
focused: bool,
|
||||
) -> Paragraph<'a, 't, T>
|
||||
where
|
||||
T: Iterator<Item = &'t Text<'t>>,
|
||||
{
|
||||
) -> Paragraph<'a> {
|
||||
Paragraph::new(content)
|
||||
.block(
|
||||
Block::default()
|
||||
.title(title)
|
||||
.title(Span::styled(title, theme.title(focused)))
|
||||
.borders(Borders::ALL)
|
||||
.title_style(theme.title(focused))
|
||||
.border_style(theme.block(focused)),
|
||||
)
|
||||
.alignment(Alignment::Left)
|
||||
}
|
||||
|
||||
fn popup_paragraph<'a, 't, T>(
|
||||
fn popup_paragraph<'a>(
|
||||
title: &'a str,
|
||||
content: T,
|
||||
content: Vec<Span<'a>>,
|
||||
theme: &Theme,
|
||||
focused: bool,
|
||||
) -> Paragraph<'a, 't, T>
|
||||
where
|
||||
T: Iterator<Item = &'t Text<'t>>,
|
||||
{
|
||||
Paragraph::new(content)
|
||||
) -> Paragraph<'a> {
|
||||
Paragraph::new(Spans::from(content))
|
||||
.block(
|
||||
Block::default()
|
||||
.title(title)
|
||||
.title(Span::styled(title, theme.title(focused)))
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Thick)
|
||||
.title_style(theme.title(focused))
|
||||
.border_style(theme.block(focused)),
|
||||
)
|
||||
.alignment(Alignment::Left)
|
||||
.wrap(true)
|
||||
.wrap(Wrap { trim: true })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ use super::{
|
|||
};
|
||||
use crate::{keys::SharedKeyConfig, strings, ui};
|
||||
use crossterm::event::Event;
|
||||
use std::borrow::Cow;
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Alignment, Rect},
|
||||
widgets::{Block, BorderType, Borders, Clear, Paragraph, Text},
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, BorderType, Borders, Clear, Paragraph, Wrap},
|
||||
Frame,
|
||||
};
|
||||
use ui::style::SharedTheme;
|
||||
|
|
@ -32,21 +32,28 @@ impl DrawableComponent for MsgComponent {
|
|||
if !self.visible {
|
||||
return Ok(());
|
||||
}
|
||||
let txt = vec![Text::Raw(Cow::from(self.msg.as_str()))];
|
||||
let txt = Spans::from(
|
||||
self.msg
|
||||
.split('\n')
|
||||
.map(|string| Span::raw::<String>(string.to_string()))
|
||||
.collect::<Vec<Span>>(),
|
||||
);
|
||||
|
||||
let area = ui::centered_rect_absolute(65, 25, f.size());
|
||||
f.render_widget(Clear, area);
|
||||
f.render_widget(
|
||||
Paragraph::new(txt.iter())
|
||||
Paragraph::new(txt)
|
||||
.block(
|
||||
Block::default()
|
||||
.title(self.title.as_str())
|
||||
.title_style(self.theme.text_danger())
|
||||
.title(Span::styled(
|
||||
self.title.as_str(),
|
||||
self.theme.text_danger(),
|
||||
))
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Thick),
|
||||
)
|
||||
.alignment(Alignment::Left)
|
||||
.wrap(true),
|
||||
.wrap(Wrap { trim: true }),
|
||||
area,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use tui::{
|
|||
backend::Backend,
|
||||
layout::Rect,
|
||||
style::{Color, Style},
|
||||
text::Span,
|
||||
widgets::{Block, BorderType, Borders, Clear, Gauge},
|
||||
Frame,
|
||||
};
|
||||
|
|
@ -146,10 +147,12 @@ impl DrawableComponent for PushComponent {
|
|||
.label(state.as_str())
|
||||
.block(
|
||||
Block::default()
|
||||
.title(strings::PUSH_POPUP_MSG)
|
||||
.title(Span::styled(
|
||||
strings::PUSH_POPUP_MSG,
|
||||
self.theme.title(true),
|
||||
))
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Thick)
|
||||
.title_style(self.theme.title(true))
|
||||
.border_style(self.theme.block(true)),
|
||||
)
|
||||
.style(
|
||||
|
|
|
|||
|
|
@ -11,10 +11,7 @@ use anyhow::Result;
|
|||
use crossterm::event::Event;
|
||||
use std::borrow::Cow;
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::Rect,
|
||||
widgets::{Clear, Text},
|
||||
Frame,
|
||||
backend::Backend, layout::Rect, text::Span, widgets::Clear, Frame,
|
||||
};
|
||||
use ui::style::SharedTheme;
|
||||
|
||||
|
|
@ -36,7 +33,7 @@ impl DrawableComponent for ResetComponent {
|
|||
if self.visible {
|
||||
let (title, msg) = self.get_text();
|
||||
|
||||
let txt = vec![Text::Styled(
|
||||
let txt = vec![Span::styled(
|
||||
Cow::from(msg),
|
||||
self.theme.text_danger(),
|
||||
)];
|
||||
|
|
@ -44,12 +41,7 @@ impl DrawableComponent for ResetComponent {
|
|||
let area = ui::centered_rect(30, 20, f.size());
|
||||
f.render_widget(Clear, area);
|
||||
f.render_widget(
|
||||
popup_paragraph(
|
||||
&title,
|
||||
txt.iter(),
|
||||
&self.theme,
|
||||
true,
|
||||
),
|
||||
popup_paragraph(&title, txt, &self.theme, true),
|
||||
area,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,8 @@ use crate::{
|
|||
use anyhow::Result;
|
||||
use crossterm::event::{Event, KeyCode, KeyModifiers};
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::Rect,
|
||||
style::Modifier,
|
||||
widgets::{Clear, Text},
|
||||
Frame,
|
||||
backend::Backend, layout::Rect, style::Modifier, text::Span,
|
||||
widgets::Clear, Frame,
|
||||
};
|
||||
|
||||
/// primarily a subcomponet for user input of text (used in `CommitComponent`)
|
||||
|
|
@ -108,14 +105,14 @@ impl TextInputComponent {
|
|||
self.title = t;
|
||||
}
|
||||
|
||||
fn get_draw_text(&self) -> Vec<Text> {
|
||||
fn get_draw_text(&self) -> Vec<Span> {
|
||||
let style = self.theme.text(true, false);
|
||||
|
||||
let mut txt = Vec::new();
|
||||
// The portion of the text before the cursor is added
|
||||
// if the cursor is not at the first character.
|
||||
if self.cursor_position > 0 {
|
||||
txt.push(Text::styled(
|
||||
txt.push(Span::styled(
|
||||
&self.msg[..self.cursor_position],
|
||||
style,
|
||||
));
|
||||
|
|
@ -128,24 +125,24 @@ impl TextInputComponent {
|
|||
.map_or(" ", |pos| &self.msg[self.cursor_position..pos]);
|
||||
|
||||
if cursor_str == "\n" {
|
||||
txt.push(Text::styled(
|
||||
txt.push(Span::styled(
|
||||
"\u{21b5}",
|
||||
self.theme
|
||||
.text(false, false)
|
||||
.modifier(Modifier::UNDERLINED),
|
||||
.add_modifier(Modifier::UNDERLINED),
|
||||
));
|
||||
}
|
||||
|
||||
txt.push(Text::styled(
|
||||
txt.push(Span::styled(
|
||||
cursor_str,
|
||||
style.modifier(Modifier::UNDERLINED),
|
||||
style.add_modifier(Modifier::UNDERLINED),
|
||||
));
|
||||
|
||||
// The final portion of the text is added if there are
|
||||
// still remaining characters.
|
||||
if let Some(pos) = self.next_char_position() {
|
||||
if pos < self.msg.len() {
|
||||
txt.push(Text::styled(&self.msg[pos..], style));
|
||||
txt.push(Span::styled(&self.msg[pos..], style));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +158,7 @@ impl DrawableComponent for TextInputComponent {
|
|||
) -> Result<()> {
|
||||
if self.visible {
|
||||
let txt = if self.msg.is_empty() {
|
||||
vec![Text::styled(
|
||||
vec![Span::styled(
|
||||
self.default_msg.as_str(),
|
||||
self.theme.text(false, false),
|
||||
)]
|
||||
|
|
@ -176,7 +173,7 @@ impl DrawableComponent for TextInputComponent {
|
|||
f.render_widget(
|
||||
popup_paragraph(
|
||||
self.title.as_str(),
|
||||
txt.iter(),
|
||||
txt,
|
||||
&self.theme,
|
||||
true,
|
||||
),
|
||||
|
|
@ -304,8 +301,9 @@ mod tests {
|
|||
"",
|
||||
);
|
||||
let theme = SharedTheme::default();
|
||||
let underlined =
|
||||
theme.text(true, false).modifier(Modifier::UNDERLINED);
|
||||
let underlined = theme
|
||||
.text(true, false)
|
||||
.add_modifier(Modifier::UNDERLINED);
|
||||
|
||||
comp.set_text(String::from("a"));
|
||||
|
||||
|
|
@ -325,10 +323,11 @@ mod tests {
|
|||
"",
|
||||
);
|
||||
let theme = SharedTheme::default();
|
||||
let underlined =
|
||||
theme.text(true, false).modifier(Modifier::UNDERLINED);
|
||||
let underlined = theme
|
||||
.text(true, false)
|
||||
.add_modifier(Modifier::UNDERLINED);
|
||||
|
||||
let not_underlined = Style::new();
|
||||
let not_underlined = Style::default();
|
||||
|
||||
comp.set_text(String::from("a"));
|
||||
comp.incr_cursor();
|
||||
|
|
@ -352,8 +351,9 @@ mod tests {
|
|||
);
|
||||
|
||||
let theme = SharedTheme::default();
|
||||
let underlined =
|
||||
theme.text(false, false).modifier(Modifier::UNDERLINED);
|
||||
let underlined = theme
|
||||
.text(false, false)
|
||||
.add_modifier(Modifier::UNDERLINED);
|
||||
|
||||
comp.set_text(String::from("a\nb"));
|
||||
comp.incr_cursor();
|
||||
|
|
@ -378,8 +378,9 @@ mod tests {
|
|||
);
|
||||
|
||||
let theme = SharedTheme::default();
|
||||
let underlined =
|
||||
theme.text(true, false).modifier(Modifier::UNDERLINED);
|
||||
let underlined = theme
|
||||
.text(true, false)
|
||||
.add_modifier(Modifier::UNDERLINED);
|
||||
|
||||
comp.set_text(String::from("a\nb"));
|
||||
|
||||
|
|
@ -391,19 +392,11 @@ mod tests {
|
|||
assert_eq!(get_text(&txt[1]), Some("\nb"));
|
||||
}
|
||||
|
||||
fn get_text<'a>(t: &'a Text) -> Option<&'a str> {
|
||||
if let Text::Styled(c, _) = t {
|
||||
Some(c.as_ref())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
fn get_text<'a>(t: &'a Span) -> Option<&'a str> {
|
||||
Some(&t.content)
|
||||
}
|
||||
|
||||
fn get_style<'a>(t: &'a Text) -> Option<&'a Style> {
|
||||
if let Text::Styled(_, c) = t {
|
||||
Some(c)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
fn get_style<'a>(t: &'a Span) -> Option<&'a Style> {
|
||||
Some(&t.style)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ use crossterm::event::Event;
|
|||
use std::borrow::Cow;
|
||||
use tui::{
|
||||
layout::{Alignment, Constraint, Direction, Layout},
|
||||
widgets::{Block, Borders, Paragraph, Text},
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, Borders, Paragraph},
|
||||
};
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
|
|
@ -101,32 +102,36 @@ impl Stashing {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_option_text(&self) -> Vec<Text> {
|
||||
let bracket_open = Text::Raw(Cow::from("["));
|
||||
let bracket_close = Text::Raw(Cow::from("]"));
|
||||
fn get_option_text(&self) -> Vec<Spans> {
|
||||
let bracket_open = Span::raw(Cow::from("["));
|
||||
let bracket_close = Span::raw(Cow::from("]"));
|
||||
let option_on =
|
||||
Text::Styled(Cow::from("x"), self.theme.option(true));
|
||||
Span::styled(Cow::from("x"), self.theme.option(true));
|
||||
|
||||
let option_off =
|
||||
Text::Styled(Cow::from("_"), self.theme.option(false));
|
||||
Span::styled(Cow::from("_"), self.theme.option(false));
|
||||
|
||||
vec![
|
||||
bracket_open.clone(),
|
||||
if self.options.stash_untracked {
|
||||
option_on.clone()
|
||||
} else {
|
||||
option_off.clone()
|
||||
},
|
||||
bracket_close.clone(),
|
||||
Text::Raw(Cow::from(" stash untracked\n")),
|
||||
bracket_open,
|
||||
if self.options.keep_index {
|
||||
option_on.clone()
|
||||
} else {
|
||||
option_off.clone()
|
||||
},
|
||||
bracket_close,
|
||||
Text::Raw(Cow::from(" keep index")),
|
||||
Spans::from(vec![
|
||||
bracket_open.clone(),
|
||||
if self.options.stash_untracked {
|
||||
option_on.clone()
|
||||
} else {
|
||||
option_off.clone()
|
||||
},
|
||||
bracket_close.clone(),
|
||||
Span::raw(Cow::from(" stash untracked")),
|
||||
]),
|
||||
Spans::from(vec![
|
||||
bracket_open,
|
||||
if self.options.keep_index {
|
||||
option_on.clone()
|
||||
} else {
|
||||
option_off.clone()
|
||||
},
|
||||
bracket_close,
|
||||
Span::raw(Cow::from(" keep index")),
|
||||
]),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -152,11 +157,9 @@ impl DrawableComponent for Stashing {
|
|||
.split(chunks[1]);
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(self.get_option_text().iter())
|
||||
Paragraph::new(self.get_option_text())
|
||||
.block(Block::default().borders(Borders::ALL).title(
|
||||
&strings::stashing_options_title(
|
||||
&self.key_config,
|
||||
),
|
||||
strings::stashing_options_title(&self.key_config),
|
||||
))
|
||||
.alignment(Alignment::Left),
|
||||
right_chunks[0],
|
||||
|
|
|
|||
|
|
@ -5,14 +5,15 @@ use tui::{
|
|||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::Style,
|
||||
widgets::{Block, Borders, List, Text, Widget},
|
||||
text::Span,
|
||||
widgets::{Block, Borders, List, ListItem, Widget},
|
||||
Frame,
|
||||
};
|
||||
|
||||
///
|
||||
struct ScrollableList<'b, L>
|
||||
where
|
||||
L: Iterator<Item = Text<'b>>,
|
||||
L: Iterator<Item = Span<'b>>,
|
||||
{
|
||||
block: Option<Block<'b>>,
|
||||
/// Items to be displayed
|
||||
|
|
@ -25,7 +26,7 @@ where
|
|||
|
||||
impl<'b, L> ScrollableList<'b, L>
|
||||
where
|
||||
L: Iterator<Item = Text<'b>>,
|
||||
L: Iterator<Item = Span<'b>>,
|
||||
{
|
||||
fn new(items: L) -> Self {
|
||||
Self {
|
||||
|
|
@ -49,14 +50,16 @@ where
|
|||
|
||||
impl<'b, L> Widget for ScrollableList<'b, L>
|
||||
where
|
||||
L: Iterator<Item = Text<'b>>,
|
||||
L: Iterator<Item = Span<'b>>,
|
||||
{
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
// Render items
|
||||
List::new(self.items)
|
||||
.block(self.block.unwrap_or_default())
|
||||
.style(self.style)
|
||||
.render(area, buf);
|
||||
List::new(
|
||||
self.items.map(ListItem::new).collect::<Vec<ListItem>>(),
|
||||
)
|
||||
.block(self.block.unwrap_or_default())
|
||||
.style(self.style)
|
||||
.render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,14 +72,13 @@ pub fn draw_list<'b, B: Backend, L>(
|
|||
selected: bool,
|
||||
theme: &SharedTheme,
|
||||
) where
|
||||
L: Iterator<Item = Text<'b>>,
|
||||
L: Iterator<Item = Span<'b>>,
|
||||
{
|
||||
let list = ScrollableList::new(items)
|
||||
.block(
|
||||
Block::default()
|
||||
.title(title)
|
||||
.title(Span::styled(title, theme.title(selected)))
|
||||
.borders(Borders::ALL)
|
||||
.title_style(theme.title(selected))
|
||||
.border_style(theme.block(selected)),
|
||||
)
|
||||
.scroll(select.unwrap_or_default());
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ impl Theme {
|
|||
|
||||
pub fn title(&self, focused: bool) -> Style {
|
||||
if focused {
|
||||
Style::default().modifier(Modifier::BOLD)
|
||||
Style::default().add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
Style::default().fg(self.disabled_fg)
|
||||
}
|
||||
|
|
@ -73,7 +73,9 @@ impl Theme {
|
|||
|
||||
pub fn tab(&self, selected: bool) -> Style {
|
||||
if selected {
|
||||
self.text(true, false).modifier(Modifier::UNDERLINED)
|
||||
self.text(true, false)
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::UNDERLINED)
|
||||
} else {
|
||||
self.text(false, false)
|
||||
}
|
||||
|
|
@ -82,7 +84,7 @@ impl Theme {
|
|||
pub fn tags(&self, selected: bool) -> Style {
|
||||
Style::default()
|
||||
.fg(self.selected_tab)
|
||||
.modifier(Modifier::BOLD)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.bg(if selected {
|
||||
self.selection_bg
|
||||
} else {
|
||||
|
|
@ -120,11 +122,7 @@ impl Theme {
|
|||
self.apply_select(style, selected)
|
||||
}
|
||||
|
||||
const fn apply_select(
|
||||
&self,
|
||||
style: Style,
|
||||
selected: bool,
|
||||
) -> Style {
|
||||
fn apply_select(&self, style: Style, selected: bool) -> Style {
|
||||
if selected {
|
||||
style.bg(self.selection_bg)
|
||||
} else {
|
||||
|
|
@ -162,7 +160,7 @@ impl Theme {
|
|||
}
|
||||
DiffLineType::Header => Style::default()
|
||||
.fg(self.disabled_fg)
|
||||
.modifier(Modifier::BOLD),
|
||||
.add_modifier(Modifier::BOLD),
|
||||
DiffLineType::None => Style::default().fg(if selected {
|
||||
self.command_fg
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue