mirror of
https://git.meli.delivery/meli/meli
synced 2024-11-19 03:25:38 +00:00
mail/composer: add scrollbars
This commit is contained in:
parent
1e7b40e6b3
commit
3949cecb75
@ -642,7 +642,7 @@ impl Component for ContactList {
|
||||
let mut draft: Draft = Draft::default();
|
||||
*draft.headers_mut().get_mut("To").unwrap() =
|
||||
format!("{} <{}>", &card.name(), &card.email());
|
||||
let mut composer = Composer::new(account_hash, context);
|
||||
let mut composer = Composer::with_account(account_hash, context);
|
||||
composer.set_draft(draft);
|
||||
context
|
||||
.replies
|
||||
|
@ -98,33 +98,6 @@ pub struct Composer {
|
||||
id: ComponentId,
|
||||
}
|
||||
|
||||
impl Default for Composer {
|
||||
fn default() -> Self {
|
||||
let mut pager = Pager::default();
|
||||
pager.set_reflow(text_processing::Reflow::FormatFlowed);
|
||||
Composer {
|
||||
reply_context: None,
|
||||
account_hash: 0,
|
||||
|
||||
cursor: Cursor::Headers,
|
||||
|
||||
pager,
|
||||
draft: Draft::default(),
|
||||
form: FormWidget::default(),
|
||||
|
||||
mode: ViewMode::Edit,
|
||||
#[cfg(feature = "gpgme")]
|
||||
gpg_state: gpg::GpgComposeState::new(),
|
||||
dirty: true,
|
||||
has_changes: false,
|
||||
embed_area: ((0, 0), (0, 0)),
|
||||
embed: None,
|
||||
initialized: false,
|
||||
id: ComponentId::new_v4(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ViewMode {
|
||||
Discard(Uuid, UIDialog<char>),
|
||||
@ -174,11 +147,32 @@ impl fmt::Display for Composer {
|
||||
|
||||
impl Composer {
|
||||
const DESCRIPTION: &'static str = "composing";
|
||||
pub fn new(account_hash: AccountHash, context: &Context) -> Self {
|
||||
pub fn new(context: &Context) -> Self {
|
||||
let mut pager = Pager::new(context);
|
||||
pager.set_show_scrollbar(true);
|
||||
Composer {
|
||||
reply_context: None,
|
||||
account_hash: 0,
|
||||
cursor: Cursor::Headers,
|
||||
pager,
|
||||
draft: Draft::default(),
|
||||
form: FormWidget::default(),
|
||||
mode: ViewMode::Edit,
|
||||
#[cfg(feature = "gpgme")]
|
||||
gpg_state: gpg::GpgComposeState::new(),
|
||||
dirty: true,
|
||||
has_changes: false,
|
||||
embed_area: ((0, 0), (0, 0)),
|
||||
embed: None,
|
||||
initialized: false,
|
||||
id: ComponentId::new_v4(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_account(account_hash: AccountHash, context: &Context) -> Self {
|
||||
let mut ret = Composer {
|
||||
account_hash,
|
||||
id: ComponentId::new_v4(),
|
||||
..Default::default()
|
||||
..Composer::new(context)
|
||||
};
|
||||
for (h, v) in
|
||||
account_settings!(context[account_hash].composing.default_header_values).iter()
|
||||
@ -194,8 +188,6 @@ impl Composer {
|
||||
format!("meli {}", option_env!("CARGO_PKG_VERSION").unwrap_or("0.0")),
|
||||
);
|
||||
}
|
||||
ret.pager
|
||||
.set_colors(crate::conf::value(context, "theme_default"));
|
||||
ret
|
||||
}
|
||||
|
||||
@ -205,7 +197,7 @@ impl Composer {
|
||||
bytes: &[u8],
|
||||
context: &Context,
|
||||
) -> Result<Self> {
|
||||
let mut ret = Composer::default();
|
||||
let mut ret = Composer::with_account(account_hash, context);
|
||||
let envelope: EnvelopeRef = context.accounts[&account_hash].collection.get_env(env_hash);
|
||||
|
||||
ret.draft = Draft::edit(&envelope, bytes)?;
|
||||
@ -220,7 +212,7 @@ impl Composer {
|
||||
context: &mut Context,
|
||||
reply_to_all: bool,
|
||||
) -> Self {
|
||||
let mut ret = Composer::new(coordinates.0, context);
|
||||
let mut ret = Composer::with_account(coordinates.0, context);
|
||||
let account = &context.accounts[&coordinates.0];
|
||||
let envelope = account.collection.get_env(coordinates.2);
|
||||
let subject = envelope.subject();
|
||||
@ -676,8 +668,8 @@ impl Component for Composer {
|
||||
);
|
||||
|
||||
let body_area = (
|
||||
pos_inc(upper_left, (mid + 1, header_height + 1)),
|
||||
pos_dec(bottom_right, (mid, 4 + attachments_no)),
|
||||
pos_inc(upper_left, (mid, header_height + 1)),
|
||||
pos_dec(bottom_right, (mid, 5 + attachments_no)),
|
||||
);
|
||||
|
||||
let (x, y) = write_string_to_grid(
|
||||
@ -763,13 +755,24 @@ impl Component for Composer {
|
||||
self.embed_area = (upper_left!(header_area), bottom_right!(body_area));
|
||||
}
|
||||
|
||||
if !self.mode.is_edit_attachments() {
|
||||
self.pager.set_dirty(true);
|
||||
if self.pager.size().0 > width!(body_area) {
|
||||
self.pager.set_initialised(false);
|
||||
}
|
||||
self.pager.draw(grid, body_area, context);
|
||||
}
|
||||
|
||||
match self.cursor {
|
||||
Cursor::Headers => {
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
set_y(upper_left!(body_area), get_y(bottom_right!(body_area))),
|
||||
bottom_right!(body_area),
|
||||
pos_dec(upper_left!(body_area), (1, 0)),
|
||||
pos_dec(
|
||||
set_y(upper_left!(body_area), get_y(bottom_right!(body_area))),
|
||||
(1, 0),
|
||||
),
|
||||
),
|
||||
theme_default.fg,
|
||||
theme_default.bg,
|
||||
@ -779,8 +782,11 @@ impl Component for Composer {
|
||||
change_colors(
|
||||
grid,
|
||||
(
|
||||
set_y(upper_left!(body_area), get_y(bottom_right!(body_area))),
|
||||
bottom_right!(body_area),
|
||||
pos_dec(upper_left!(body_area), (1, 0)),
|
||||
pos_dec(
|
||||
set_y(upper_left!(body_area), get_y(bottom_right!(body_area))),
|
||||
(1, 0),
|
||||
),
|
||||
),
|
||||
theme_default.fg,
|
||||
Color::Byte(237),
|
||||
@ -789,11 +795,6 @@ impl Component for Composer {
|
||||
Cursor::Sign | Cursor::Encrypt | Cursor::Attachments => {}
|
||||
}
|
||||
|
||||
if !self.mode.is_edit_attachments() {
|
||||
self.pager.set_dirty(true);
|
||||
self.pager.draw(grid, body_area, context);
|
||||
}
|
||||
|
||||
match self.mode {
|
||||
ViewMode::Edit | ViewMode::Embed => {}
|
||||
ViewMode::EditAttachments { ref mut widget } => {
|
||||
|
@ -1211,7 +1211,7 @@ impl Component for Listing {
|
||||
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["new_mail"]) =>
|
||||
{
|
||||
let account_hash = context.accounts[self.cursor_pos.0].hash();
|
||||
let composer = Composer::new(account_hash, context);
|
||||
let composer = Composer::with_account(account_hash, context);
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::Action(Tab(New(Some(Box::new(composer))))));
|
||||
|
@ -2143,7 +2143,8 @@ impl Component for MailView {
|
||||
{
|
||||
if let Ok(mailto) = Mailto::try_from(list_post_addr) {
|
||||
let draft: Draft = mailto.into();
|
||||
let mut composer = Composer::new(self.coordinates.0, context);
|
||||
let mut composer =
|
||||
Composer::with_account(self.coordinates.0, context);
|
||||
composer.set_draft(draft);
|
||||
context.replies.push_back(UIEvent::Action(Tab(New(Some(
|
||||
Box::new(composer),
|
||||
|
@ -570,8 +570,7 @@ impl ThreadView {
|
||||
|
||||
self.highlight_line(grid, dest_area, src_area, idx);
|
||||
if rows < visibles.len() {
|
||||
ScrollBar::draw(
|
||||
ScrollBar::default(),
|
||||
ScrollBar::default().draw(
|
||||
grid,
|
||||
(
|
||||
upper_left!(area),
|
||||
@ -624,8 +623,7 @@ impl ThreadView {
|
||||
|
||||
self.highlight_line(grid, dest_area, src_area, entry_idx);
|
||||
if rows < visibles.len() {
|
||||
ScrollBar::draw(
|
||||
ScrollBar::default(),
|
||||
ScrollBar::default().draw(
|
||||
grid,
|
||||
(
|
||||
upper_left!(area),
|
||||
|
@ -316,10 +316,7 @@ impl Component for StatusBar {
|
||||
}
|
||||
let hist_height = std::cmp::min(15, self.auto_complete.suggestions().len());
|
||||
let hist_area = if height < self.auto_complete.suggestions().len() {
|
||||
let mut scrollbar = ScrollBar::default();
|
||||
scrollbar.set_show_arrows(false);
|
||||
scrollbar.set_block_character(Some('▌'));
|
||||
scrollbar.draw(
|
||||
ScrollBar::default().set_show_arrows(false).draw(
|
||||
grid,
|
||||
(
|
||||
(
|
||||
|
@ -37,6 +37,7 @@ pub struct Pager {
|
||||
|
||||
colors: ThemeAttribute,
|
||||
initialised: bool,
|
||||
show_scrollbar: bool,
|
||||
content: CellBuffer,
|
||||
text_lines: (usize, Vec<String>),
|
||||
movement: Option<PageMovement>,
|
||||
@ -51,6 +52,23 @@ impl fmt::Display for Pager {
|
||||
|
||||
impl Pager {
|
||||
pub const DESCRIPTION: &'static str = "pager";
|
||||
pub fn new(context: &Context) -> Self {
|
||||
let mut ret = Pager::default();
|
||||
ret.minimum_width = context.settings.pager.minimum_width;
|
||||
ret.set_colors(crate::conf::value(context, "theme_default"))
|
||||
.set_reflow(if context.settings.pager.split_long_lines {
|
||||
Reflow::All
|
||||
} else {
|
||||
Reflow::No
|
||||
});
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn set_show_scrollbar(&mut self, new_val: bool) -> &mut Self {
|
||||
self.show_scrollbar = new_val;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_colors(&mut self, new_val: ThemeAttribute) -> &mut Self {
|
||||
self.colors = new_val;
|
||||
self
|
||||
@ -61,6 +79,11 @@ impl Pager {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_initialised(&mut self, new_val: bool) -> &mut Self {
|
||||
self.initialised = new_val;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn reflow(&self) -> Reflow {
|
||||
self.reflow
|
||||
}
|
||||
@ -264,13 +287,13 @@ impl Component for Pager {
|
||||
width = self.minimum_width;
|
||||
}
|
||||
|
||||
let lines: &[String] = if self.text_lines.0 == width.saturating_sub(2) {
|
||||
let lines: &[String] = if self.text_lines.0 == width.saturating_sub(4) {
|
||||
&self.text_lines.1
|
||||
} else {
|
||||
let lines = self
|
||||
.text
|
||||
.split_lines_reflow(self.reflow, Some(width.saturating_sub(2)));
|
||||
self.text_lines = (width.saturating_sub(2), lines);
|
||||
.split_lines_reflow(self.reflow, Some(width.saturating_sub(4)));
|
||||
self.text_lines = (width.saturating_sub(4), lines);
|
||||
&self.text_lines.1
|
||||
};
|
||||
let height = lines.len() + 2;
|
||||
@ -344,30 +367,36 @@ impl Component for Pager {
|
||||
self.cursor.1 = self.cursor.1.saturating_sub(height * multiplier);
|
||||
}
|
||||
PageMovement::Down(amount) => {
|
||||
if self.cursor.1 + amount < self.height {
|
||||
if self.cursor.1 + amount + 1 < self.height {
|
||||
self.cursor.1 += amount;
|
||||
} else {
|
||||
self.cursor.1 = self.height.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
PageMovement::PageDown(multiplier) => {
|
||||
if self.cursor.1 + height * multiplier < self.height {
|
||||
if self.cursor.1 + height * multiplier + 1 < self.height {
|
||||
self.cursor.1 += height * multiplier;
|
||||
} else if self.cursor.1 + height * multiplier > self.height {
|
||||
self.cursor.1 = self.height - 1;
|
||||
} else {
|
||||
self.cursor.1 = (self.height / height) * height;
|
||||
}
|
||||
}
|
||||
PageMovement::Right(multiplier) => {
|
||||
let offset = width!(area) / 3;
|
||||
if self.cursor.0 + offset * multiplier < self.content.size().0 {
|
||||
self.cursor.0 += offset * multiplier;
|
||||
PageMovement::Right(amount) => {
|
||||
if self.cursor.0 + amount + 1 < self.width {
|
||||
self.cursor.0 += amount;
|
||||
} else {
|
||||
self.cursor.0 = self.width.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
PageMovement::Left(multiplier) => {
|
||||
let offset = width!(area) / 3;
|
||||
self.cursor.0 = self.cursor.0.saturating_sub(offset * multiplier);
|
||||
PageMovement::Left(amount) => {
|
||||
self.cursor.0 = self.cursor.0.saturating_sub(amount);
|
||||
}
|
||||
PageMovement::Home => {
|
||||
self.cursor.1 = 0;
|
||||
}
|
||||
PageMovement::End => {
|
||||
self.cursor.1 = (self.height / height) * height;
|
||||
self.cursor.1 = self.height.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -397,7 +426,13 @@ impl Component for Pager {
|
||||
|
||||
clear_area(grid, area, crate::conf::value(context, "theme_default"));
|
||||
let (width, height) = self.content.size();
|
||||
let (cols, rows) = (width!(area), height!(area));
|
||||
let (mut cols, mut rows) = (width!(area), height!(area));
|
||||
if self.show_scrollbar && rows < height {
|
||||
cols -= 1;
|
||||
}
|
||||
if self.show_scrollbar && cols < width {
|
||||
rows -= 1;
|
||||
}
|
||||
self.cursor = (
|
||||
std::cmp::min(width.saturating_sub(cols), self.cursor.0),
|
||||
std::cmp::min(height.saturating_sub(rows), self.cursor.1),
|
||||
@ -417,6 +452,32 @@ impl Component for Pager {
|
||||
),
|
||||
),
|
||||
);
|
||||
if self.show_scrollbar && rows + 1 < height {
|
||||
ScrollBar::default().set_show_arrows(true).draw(
|
||||
grid,
|
||||
(
|
||||
set_x(upper_left!(area), get_x(bottom_right!(area))),
|
||||
bottom_right!(area),
|
||||
),
|
||||
context,
|
||||
self.cursor.1,
|
||||
rows,
|
||||
height,
|
||||
);
|
||||
}
|
||||
if self.show_scrollbar && cols + 1 < width {
|
||||
ScrollBar::default().set_show_arrows(true).draw_horizontal(
|
||||
grid,
|
||||
(
|
||||
set_y(upper_left!(area), get_y(bottom_right!(area))),
|
||||
bottom_right!(area),
|
||||
),
|
||||
context,
|
||||
self.cursor.0,
|
||||
cols,
|
||||
width,
|
||||
);
|
||||
}
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
|
@ -980,19 +980,17 @@ impl AutoComplete {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScrollBar {
|
||||
show_arrows: bool,
|
||||
block_character: Option<char>,
|
||||
pub show_arrows: bool,
|
||||
}
|
||||
|
||||
impl ScrollBar {
|
||||
pub fn set_show_arrows(&mut self, flag: bool) {
|
||||
self.show_arrows = flag;
|
||||
}
|
||||
pub fn set_block_character(&mut self, val: Option<char>) {
|
||||
self.block_character = val;
|
||||
pub fn set_show_arrows(&mut self, new_val: bool) -> &mut Self {
|
||||
self.show_arrows = new_val;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
self,
|
||||
&mut self,
|
||||
grid: &mut CellBuffer,
|
||||
area: Area,
|
||||
context: &Context,
|
||||
@ -1007,17 +1005,17 @@ impl ScrollBar {
|
||||
if height < 3 {
|
||||
return;
|
||||
}
|
||||
if self.show_arrows {
|
||||
height -= height;
|
||||
}
|
||||
clear_area(grid, area, crate::conf::value(context, "theme_default"));
|
||||
|
||||
let visible_ratio: f32 = (std::cmp::min(visible_rows, length) as f32) / (length as f32);
|
||||
let scrollbar_height = std::cmp::max((visible_ratio * (height as f32)) as usize, 1);
|
||||
if self.show_arrows {
|
||||
height -= 3;
|
||||
}
|
||||
let scrollbar_offset = {
|
||||
let temp = (((pos as f32) / (length as f32)) * (height as f32)) as usize;
|
||||
if temp + scrollbar_height >= height {
|
||||
height - scrollbar_height
|
||||
if scrollbar_height + temp > height {
|
||||
height.saturating_sub(scrollbar_height)
|
||||
} else {
|
||||
temp
|
||||
}
|
||||
@ -1025,24 +1023,77 @@ impl ScrollBar {
|
||||
let (mut upper_left, bottom_right) = area;
|
||||
|
||||
if self.show_arrows {
|
||||
grid[upper_left].set_ch('▴');
|
||||
upper_left = (upper_left.0, upper_left.1 + 1);
|
||||
grid[upper_left]
|
||||
.set_ch('▄')
|
||||
.set_fg(crate::conf::value(context, "widgets.options.highlighted").bg);
|
||||
upper_left = pos_inc(upper_left, (0, 1));
|
||||
}
|
||||
|
||||
for y in get_y(upper_left)..(get_y(upper_left) + scrollbar_offset) {
|
||||
grid[set_y(upper_left, y)].set_ch(' ');
|
||||
}
|
||||
for y in (get_y(upper_left) + scrollbar_offset)
|
||||
..=(get_y(upper_left) + scrollbar_offset + scrollbar_height)
|
||||
{
|
||||
grid[set_y(upper_left, y)].set_ch(self.block_character.unwrap_or('█'));
|
||||
}
|
||||
for y in (get_y(upper_left) + scrollbar_offset + scrollbar_height + 1)..get_y(bottom_right)
|
||||
{
|
||||
grid[set_y(upper_left, y)].set_ch(' ');
|
||||
upper_left = pos_inc(upper_left, (0, scrollbar_offset));
|
||||
for _ in 0..=scrollbar_height {
|
||||
grid[upper_left].set_bg(crate::conf::value(context, "widgets.options.highlighted").bg);
|
||||
upper_left = pos_inc(upper_left, (0, 1));
|
||||
}
|
||||
if self.show_arrows {
|
||||
grid[set_x(bottom_right, get_x(upper_left))].set_ch('▾');
|
||||
grid[pos_dec(bottom_right, (0, 1))]
|
||||
.set_ch('▀')
|
||||
.set_fg(crate::conf::value(context, "widgets.options.highlighted").bg)
|
||||
.set_bg(crate::conf::value(context, "theme_default").bg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_horizontal(
|
||||
&mut self,
|
||||
grid: &mut CellBuffer,
|
||||
area: Area,
|
||||
context: &Context,
|
||||
pos: usize,
|
||||
visible_cols: usize,
|
||||
length: usize,
|
||||
) {
|
||||
if length == 0 {
|
||||
return;
|
||||
}
|
||||
let mut width = width!(area);
|
||||
if width < 3 {
|
||||
return;
|
||||
}
|
||||
clear_area(grid, area, crate::conf::value(context, "theme_default"));
|
||||
|
||||
let visible_ratio: f32 = (std::cmp::min(visible_cols, length) as f32) / (length as f32);
|
||||
let scrollbar_width = std::cmp::max((visible_ratio * (width as f32)) as usize, 1);
|
||||
if self.show_arrows {
|
||||
width -= 3;
|
||||
}
|
||||
let scrollbar_offset = {
|
||||
let temp = (((pos as f32) / (length as f32)) * (width as f32)) as usize;
|
||||
if scrollbar_width + temp > width {
|
||||
width.saturating_sub(scrollbar_width)
|
||||
} else {
|
||||
temp
|
||||
}
|
||||
};
|
||||
let (mut upper_left, bottom_right) = area;
|
||||
|
||||
if self.show_arrows {
|
||||
grid[upper_left]
|
||||
.set_ch('▐')
|
||||
.set_fg(crate::conf::value(context, "widgets.options.highlighted").bg);
|
||||
upper_left = pos_inc(upper_left, (1, 0));
|
||||
}
|
||||
|
||||
upper_left = pos_inc(upper_left, (scrollbar_offset, 0));
|
||||
for _ in 0..=scrollbar_width {
|
||||
grid[upper_left]
|
||||
.set_ch('█')
|
||||
.set_fg(crate::conf::value(context, "widgets.options.highlighted").bg);
|
||||
upper_left = pos_inc(upper_left, (1, 0));
|
||||
}
|
||||
if self.show_arrows {
|
||||
grid[pos_dec(bottom_right, (1, 0))]
|
||||
.set_ch('▌')
|
||||
.set_fg(crate::conf::value(context, "widgets.options.highlighted").bg)
|
||||
.set_bg(crate::conf::value(context, "theme_default").bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user