composer: fix cursor/widget focus scrolling logic
Scrolling up/down with scroll_{up,down} shortcuts didn't work correctly, because the form widget used its own shortcuts. This commit refactors the cursor logic.master
parent
0c0a678cff
commit
65179d4816
|
@ -292,7 +292,7 @@ mode and
|
|||
key to exit.
|
||||
.It
|
||||
At any time you may press
|
||||
.Shortcut e composing edit_mail Ns
|
||||
.Shortcut e composing edit Ns
|
||||
to launch your editor (see
|
||||
.Xr meli.conf 5 COMPOSING Ns
|
||||
, setting
|
||||
|
@ -320,7 +320,7 @@ To stop your editor and return to
|
|||
press
|
||||
.Aq Ctrl-z
|
||||
and to resume editing press the
|
||||
.Ic edit_mail
|
||||
.Ic edit
|
||||
command again.
|
||||
.El
|
||||
.Ss Attachments
|
||||
|
@ -353,7 +353,7 @@ To save your draft without sending it, issue
|
|||
and select 'save as draft'.
|
||||
.sp
|
||||
To open a draft for further editing, select your draft in the mail listing and press
|
||||
.Ic edit_mail Ns
|
||||
.Ic edit Ns
|
||||
\&.
|
||||
.Sh CONTACTS
|
||||
.Nm
|
||||
|
|
|
@ -605,7 +605,7 @@ Reply to all.
|
|||
.El
|
||||
.sp
|
||||
To launch your editor, press
|
||||
.ShortcutPeriod e composing edit_mail
|
||||
.ShortcutPeriod e composing edit
|
||||
\&.
|
||||
To send your draft, press
|
||||
.ShortcutPeriod s composing send_mail
|
||||
|
@ -619,7 +619,7 @@ and select
|
|||
You can return to the draft by going to your
|
||||
.Qq Drafts
|
||||
mailbox and selecting
|
||||
.ShortcutPeriod e envelope_view edit_mail
|
||||
.ShortcutPeriod e envelope_view edit
|
||||
\&.
|
||||
.Bd -literal -offset center
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
|
@ -648,7 +648,7 @@ mailbox and selecting
|
|||
.Ed
|
||||
.sp
|
||||
If you enable the embed terminal option, you can launch your terminal editor of choice when you press
|
||||
.Ic edit_mail Ns
|
||||
.Ic edit Ns
|
||||
\&.
|
||||
.Bd -literal -offset center
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
|
|
|
@ -114,7 +114,7 @@ editor_command = 'vim +/^$'
|
|||
|
||||
[shortcuts]
|
||||
[shortcuts.composing]
|
||||
edit_mail = 'e'
|
||||
edit = 'e'
|
||||
|
||||
[shortcuts.listing]
|
||||
new_mail = 'm'
|
||||
|
@ -964,7 +964,7 @@ Toggle visibility of side menu in mail list.
|
|||
.sp
|
||||
.Em composing
|
||||
.Bl -tag -width 36n
|
||||
.It Ic edit_mail
|
||||
.It Ic edit
|
||||
Edit mail.
|
||||
.\" default value
|
||||
.Pq Em e
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
#
|
||||
###shortcuts
|
||||
#[shortcuts.composing]
|
||||
#edit_mail = 'e'
|
||||
#edit = 'e'
|
||||
#
|
||||
#[shortcuts.contact-list]
|
||||
#create_contact = 'c'
|
||||
|
|
|
@ -77,7 +77,7 @@ impl ContactManager {
|
|||
}
|
||||
}
|
||||
|
||||
fn initialize(&mut self) {
|
||||
fn initialize(&mut self, context: &Context) {
|
||||
let (width, _) = self.content.size();
|
||||
|
||||
let (x, _) = write_string_to_grid(
|
||||
|
@ -113,7 +113,12 @@ impl ContactManager {
|
|||
);
|
||||
}
|
||||
|
||||
self.form = FormWidget::new(("Save".into(), true));
|
||||
self.form = FormWidget::new(
|
||||
("Save".into(), true),
|
||||
/* cursor_up_shortcut */ context.settings.shortcuts.general.scroll_up.clone(),
|
||||
/* cursor_down_shortcut */
|
||||
context.settings.shortcuts.general.scroll_down.clone(),
|
||||
);
|
||||
self.form.add_button(("Cancel(Esc)".into(), false));
|
||||
self.form
|
||||
.push(("NAME".into(), self.card.name().to_string()));
|
||||
|
@ -142,7 +147,7 @@ impl ContactManager {
|
|||
impl Component for ContactManager {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
if !self.initialized {
|
||||
self.initialize();
|
||||
self.initialize(context);
|
||||
self.initialized = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -686,7 +686,7 @@ impl Component for ContactList {
|
|||
*draft.headers_mut().get_mut("To").unwrap() =
|
||||
format!("{} <{}>", &card.name(), &card.email());
|
||||
let mut composer = Composer::with_account(account_hash, context);
|
||||
composer.set_draft(draft);
|
||||
composer.set_draft(draft, context);
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::Action(Tab(New(Some(Box::new(composer))))));
|
||||
|
|
|
@ -535,13 +535,13 @@ To: {}
|
|||
draft.attachments.push(preamble);
|
||||
draft.attachments.push(env.body_bytes(bytes).into());
|
||||
}
|
||||
composer.set_draft(draft);
|
||||
composer.set_draft(draft, context);
|
||||
composer
|
||||
}
|
||||
|
||||
pub fn set_draft(&mut self, draft: Draft) {
|
||||
pub fn set_draft(&mut self, draft: Draft, context: &Context) {
|
||||
self.draft = draft;
|
||||
self.update_form();
|
||||
self.update_form(context);
|
||||
}
|
||||
|
||||
fn update_draft(&mut self) {
|
||||
|
@ -554,9 +554,22 @@ To: {}
|
|||
}
|
||||
}
|
||||
|
||||
fn update_form(&mut self) {
|
||||
fn update_form(&mut self, context: &Context) {
|
||||
let old_cursor = self.form.cursor();
|
||||
self.form = FormWidget::new(("Save".into(), true));
|
||||
let shortcuts = self.shortcuts(context);
|
||||
self.form = FormWidget::new(
|
||||
("Save".into(), true),
|
||||
/* cursor_up_shortcut */
|
||||
shortcuts
|
||||
.get(Shortcuts::COMPOSING)
|
||||
.and_then(|c| c.get("scroll_up").cloned())
|
||||
.unwrap_or_else(|| context.settings.shortcuts.composing.scroll_up.clone()),
|
||||
/* cursor_down_shortcut */
|
||||
shortcuts
|
||||
.get(Shortcuts::COMPOSING)
|
||||
.and_then(|c| c.get("scroll_down").cloned())
|
||||
.unwrap_or_else(|| context.settings.shortcuts.composing.scroll_down.clone()),
|
||||
);
|
||||
self.form.hide_buttons();
|
||||
self.form.set_cursor(old_cursor);
|
||||
let headers = self.draft.headers();
|
||||
|
@ -814,8 +827,6 @@ impl Component for Composer {
|
|||
return;
|
||||
}
|
||||
|
||||
let width = width!(area);
|
||||
|
||||
if !self.initialized {
|
||||
#[cfg(feature = "gpgme")]
|
||||
if self.gpg_state.sign_mail.is_unset() {
|
||||
|
@ -835,12 +846,14 @@ impl Component for Composer {
|
|||
);
|
||||
}
|
||||
self.pager.update_from_str(self.draft.body(), Some(77));
|
||||
self.update_form();
|
||||
self.update_form(context);
|
||||
self.initialized = true;
|
||||
}
|
||||
let header_height = self.form.len();
|
||||
let theme_default = crate::conf::value(context, "theme_default");
|
||||
|
||||
let mid = 0;
|
||||
/*
|
||||
let mid = if width > 80 {
|
||||
let width = width - 80;
|
||||
let mid = width / 2;
|
||||
|
@ -861,6 +874,7 @@ impl Component for Composer {
|
|||
} else {
|
||||
0
|
||||
};
|
||||
*/
|
||||
|
||||
let header_area = (
|
||||
set_x(upper_left, mid + 1),
|
||||
|
@ -971,8 +985,8 @@ impl Component for Composer {
|
|||
let stopped_message: String =
|
||||
format!("Process with PID {} has stopped.", guard.child_pid);
|
||||
let stopped_message_2: String = format!(
|
||||
"-press '{}' (edit_mail shortcut) to re-activate.",
|
||||
shortcuts[Shortcuts::COMPOSING]["edit_mail"]
|
||||
"-press '{}' (edit shortcut) to re-activate.",
|
||||
shortcuts[Shortcuts::COMPOSING]["edit"]
|
||||
);
|
||||
const STOPPED_MESSAGE_3: &str =
|
||||
"-press Ctrl-C to forcefully kill it and return to editor.";
|
||||
|
@ -1024,13 +1038,14 @@ 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);
|
||||
if self.pager.size().0 > width!(body_area) {
|
||||
self.pager.set_initialised(false);
|
||||
}
|
||||
// Force clean pager area, because if body height is less than body_area it will
|
||||
// might leave draw artifacts in the remaining area.
|
||||
clear_area(grid, body_area, theme_default);
|
||||
self.set_dirty(true);
|
||||
self.pager.draw(grid, body_area, context);
|
||||
|
||||
match self.cursor {
|
||||
Cursor::Headers => {
|
||||
|
@ -1123,6 +1138,58 @@ impl Component for Composer {
|
|||
self.pager.process_event(event, context);
|
||||
}
|
||||
let shortcuts = self.shortcuts(context);
|
||||
// Process scrolling first, since in my infinite wisdom I made this so
|
||||
// unnecessarily complex
|
||||
match &event {
|
||||
UIEvent::Input(ref key)
|
||||
if self.mode.is_edit()
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["scroll_up"]) =>
|
||||
{
|
||||
self.set_dirty(true);
|
||||
self.cursor = match self.cursor {
|
||||
// match order is evaluation order, so it matters here because of the if guard
|
||||
// process_event side effects
|
||||
Cursor::Attachments => Cursor::Encrypt,
|
||||
Cursor::Encrypt => Cursor::Sign,
|
||||
Cursor::Sign => Cursor::Body,
|
||||
Cursor::Body if !self.pager.process_event(event, context) => {
|
||||
self.form.process_event(event, context);
|
||||
Cursor::Headers
|
||||
}
|
||||
Cursor::Body => Cursor::Body,
|
||||
Cursor::Headers if self.form.process_event(event, context) => Cursor::Headers,
|
||||
Cursor::Headers => Cursor::Headers,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(ref key)
|
||||
if self.mode.is_edit()
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["scroll_down"]) =>
|
||||
{
|
||||
self.set_dirty(true);
|
||||
self.cursor = match self.cursor {
|
||||
Cursor::Headers if self.form.process_event(event, context) => Cursor::Headers,
|
||||
Cursor::Headers => Cursor::Body,
|
||||
Cursor::Body if self.pager.process_event(event, context) => Cursor::Body,
|
||||
Cursor::Body => Cursor::Sign,
|
||||
Cursor::Sign => Cursor::Encrypt,
|
||||
Cursor::Encrypt => Cursor::Attachments,
|
||||
Cursor::Attachments => Cursor::Attachments,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if self.cursor == Cursor::Headers
|
||||
&& self.mode.is_edit()
|
||||
&& self.form.process_event(event, context)
|
||||
{
|
||||
if let UIEvent::InsertInput(_) = event {
|
||||
self.has_changes = true;
|
||||
}
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
match (&mut self.mode, &mut event) {
|
||||
(ViewMode::Edit, _) => {
|
||||
if self.pager.process_event(event, context) {
|
||||
|
@ -1136,10 +1203,13 @@ impl Component for Composer {
|
|||
})
|
||||
.process_event(event, context)
|
||||
{
|
||||
if widget.buttons.result() == Some(FormButtonActions::Cancel) {
|
||||
if matches!(
|
||||
widget.buttons.result(),
|
||||
Some(FormButtonActions::Cancel | FormButtonActions::Accept)
|
||||
) {
|
||||
self.mode = ViewMode::Edit;
|
||||
self.set_dirty(true);
|
||||
}
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1237,6 +1307,7 @@ impl Component for Composer {
|
|||
}
|
||||
(ViewMode::Send(ref mut selector), _) => {
|
||||
if selector.process_event(event, context) {
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1247,13 +1318,15 @@ impl Component for Composer {
|
|||
if let Some(to_val) = result.downcast_mut::<String>() {
|
||||
self.draft
|
||||
.set_header(HeaderName::TO, std::mem::take(to_val));
|
||||
self.update_form();
|
||||
self.update_form(context);
|
||||
}
|
||||
self.mode = ViewMode::Edit;
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
(ViewMode::SelectRecipients(ref mut selector), _) => {
|
||||
if selector.process_event(event, context) {
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1281,12 +1354,13 @@ impl Component for Composer {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
self.set_dirty(true);
|
||||
self.mode = ViewMode::Edit;
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
(ViewMode::Discard(_, ref mut selector), _) => {
|
||||
if selector.process_event(event, context) {
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1300,6 +1374,7 @@ impl Component for Composer {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::Action(Tab(Kill(self.id))));
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
'n' => {
|
||||
|
@ -1347,6 +1422,7 @@ impl Component for Composer {
|
|||
}
|
||||
(ViewMode::WaitingForSendResult(ref mut selector, _), _) => {
|
||||
if selector.process_event(event, context) {
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1373,20 +1449,12 @@ impl Component for Composer {
|
|||
#[cfg(feature = "gpgme")]
|
||||
(ViewMode::SelectEncryptKey(_, ref mut selector), _) => {
|
||||
if selector.process_event(event, context) {
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if self.cursor == Cursor::Headers
|
||||
&& self.mode.is_edit()
|
||||
&& self.form.process_event(event, context)
|
||||
{
|
||||
if let UIEvent::InsertInput(_) = event {
|
||||
self.has_changes = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
match *event {
|
||||
UIEvent::ConfigReload { old_settings: _ } => {
|
||||
|
@ -1399,6 +1467,7 @@ impl Component for Composer {
|
|||
if self.mode.is_edit()
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["scroll_up"]) =>
|
||||
{
|
||||
self.set_dirty(true);
|
||||
self.cursor = match self.cursor {
|
||||
Cursor::Headers => return true,
|
||||
Cursor::Body => {
|
||||
|
@ -1409,12 +1478,12 @@ impl Component for Composer {
|
|||
Cursor::Encrypt => Cursor::Sign,
|
||||
Cursor::Attachments => Cursor::Encrypt,
|
||||
};
|
||||
self.dirty = true;
|
||||
}
|
||||
UIEvent::Input(ref key)
|
||||
if self.mode.is_edit()
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["scroll_down"]) =>
|
||||
{
|
||||
self.set_dirty(true);
|
||||
self.cursor = match self.cursor {
|
||||
Cursor::Headers => Cursor::Body,
|
||||
Cursor::Body => Cursor::Sign,
|
||||
|
@ -1422,7 +1491,6 @@ impl Component for Composer {
|
|||
Cursor::Encrypt => Cursor::Attachments,
|
||||
Cursor::Attachments => return true,
|
||||
};
|
||||
self.dirty = true;
|
||||
}
|
||||
UIEvent::Input(Key::Char('\n'))
|
||||
if self.mode.is_edit()
|
||||
|
@ -1440,7 +1508,7 @@ impl Component for Composer {
|
|||
}
|
||||
_ => {}
|
||||
};
|
||||
self.dirty = true;
|
||||
self.set_dirty(true);
|
||||
}
|
||||
UIEvent::Input(ref key)
|
||||
if shortcut!(key == shortcuts[Shortcuts::COMPOSING]["send_mail"])
|
||||
|
@ -1565,6 +1633,8 @@ impl Component for Composer {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::EmbedInput((k.clone(), b.to_vec())));
|
||||
drop(embed_guard);
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
Ok(WaitStatus::Signaled(_, signal, _)) => {
|
||||
|
@ -1608,7 +1678,7 @@ impl Component for Composer {
|
|||
UIEvent::Input(ref key)
|
||||
if self.mode.is_edit()
|
||||
&& self.cursor == Cursor::Sign
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["edit_mail"]) =>
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["edit"]) =>
|
||||
{
|
||||
#[cfg(feature = "gpgme")]
|
||||
match melib::email::parser::address::rfc2822address_list(
|
||||
|
@ -1648,7 +1718,7 @@ impl Component for Composer {
|
|||
UIEvent::Input(ref key)
|
||||
if self.mode.is_edit()
|
||||
&& self.cursor == Cursor::Encrypt
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["edit_mail"]) =>
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["edit"]) =>
|
||||
{
|
||||
#[cfg(feature = "gpgme")]
|
||||
match melib::email::parser::address::rfc2822address_list(
|
||||
|
@ -1688,10 +1758,10 @@ impl Component for Composer {
|
|||
UIEvent::Input(ref key)
|
||||
if self.mode.is_edit()
|
||||
&& self.cursor == Cursor::Attachments
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["edit_mail"]) =>
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["edit"]) =>
|
||||
{
|
||||
self.mode = ViewMode::EditAttachments {
|
||||
widget: EditAttachments::new(),
|
||||
widget: EditAttachments::new(Some(self.account_hash)),
|
||||
};
|
||||
self.set_dirty(true);
|
||||
|
||||
|
@ -1699,7 +1769,7 @@ impl Component for Composer {
|
|||
}
|
||||
UIEvent::Input(ref key)
|
||||
if self.embed.is_some()
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["edit_mail"]) =>
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["edit"]) =>
|
||||
{
|
||||
self.embed.as_ref().unwrap().lock().unwrap().wake_up();
|
||||
match self.embed.take() {
|
||||
|
@ -1743,7 +1813,7 @@ impl Component for Composer {
|
|||
}
|
||||
UIEvent::Input(ref key)
|
||||
if self.mode.is_edit()
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["edit_mail"]) =>
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["edit"]) =>
|
||||
{
|
||||
/* Edit draft in $EDITOR */
|
||||
let editor = if let Some(editor_command) =
|
||||
|
@ -1760,6 +1830,7 @@ impl Component for Composer {
|
|||
.to_string(),
|
||||
Some(NotificationType::Error(melib::error::ErrorKind::None)),
|
||||
));
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
Ok(v) => v,
|
||||
|
@ -1805,6 +1876,7 @@ impl Component for Composer {
|
|||
));
|
||||
}
|
||||
}
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
/* Kill input thread so that spawned command can be sole receiver of stdin */
|
||||
|
@ -1834,6 +1906,7 @@ impl Component for Composer {
|
|||
));
|
||||
context.replies.push_back(UIEvent::Fork(ForkType::Finished));
|
||||
context.restore_input();
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1913,6 +1986,7 @@ impl Component for Composer {
|
|||
format!("could not execute pipe command {}: {}", command, &err),
|
||||
Some(NotificationType::Error(melib::error::ErrorKind::External)),
|
||||
));
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1945,9 +2019,12 @@ impl Component for Composer {
|
|||
} else {
|
||||
context.replies.push_back(UIEvent::Notification(
|
||||
None,
|
||||
"You haven't defined any command to launch.".into(),
|
||||
"You haven't defined any command to launch in \
|
||||
[terminal.file_picker_command]."
|
||||
.into(),
|
||||
Some(NotificationType::Error(melib::error::ErrorKind::None)),
|
||||
));
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
};
|
||||
/* Kill input thread so that spawned command can be sole receiver of stdin */
|
||||
|
@ -1997,6 +2074,7 @@ impl Component for Composer {
|
|||
Some(NotificationType::Error(melib::error::ErrorKind::External)),
|
||||
));
|
||||
context.restore_input();
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -2031,6 +2109,7 @@ impl Component for Composer {
|
|||
Flag::SEEN | Flag::DRAFT,
|
||||
self.account_hash,
|
||||
);
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
#[cfg(feature = "gpgme")]
|
||||
|
@ -2057,7 +2136,13 @@ impl Component for Composer {
|
|||
fn is_dirty(&self) -> bool {
|
||||
match self.mode {
|
||||
ViewMode::Embed => true,
|
||||
ViewMode::EditAttachments { ref widget } => widget.dirty || widget.buttons.is_dirty(),
|
||||
ViewMode::EditAttachments { ref widget } => {
|
||||
widget.dirty
|
||||
|| widget.buttons.is_dirty()
|
||||
|| self.dirty
|
||||
|| self.pager.is_dirty()
|
||||
|| self.form.is_dirty()
|
||||
}
|
||||
ViewMode::Edit => self.dirty || self.pager.is_dirty() || self.form.is_dirty(),
|
||||
ViewMode::Discard(_, ref widget) => {
|
||||
widget.is_dirty() || self.pager.is_dirty() || self.form.is_dirty()
|
||||
|
|
|
@ -39,6 +39,8 @@ pub enum EditAttachmentMode {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct EditAttachments {
|
||||
/// For shortcut setting retrieval.
|
||||
pub account_hash: Option<AccountHash>,
|
||||
pub mode: EditAttachmentMode,
|
||||
pub buttons: ButtonWidget<FormButtonActions>,
|
||||
pub cursor: EditAttachmentCursor,
|
||||
|
@ -47,12 +49,13 @@ pub struct EditAttachments {
|
|||
}
|
||||
|
||||
impl EditAttachments {
|
||||
pub fn new() -> Self {
|
||||
let mut buttons = ButtonWidget::new(("Add".into(), FormButtonActions::Other("add")));
|
||||
buttons.push(("Go Back".into(), FormButtonActions::Cancel));
|
||||
pub fn new(account_hash: Option<AccountHash>) -> Self {
|
||||
//ButtonWidget::new(("Add".into(), FormButtonActions::Other("add")));
|
||||
let mut buttons = ButtonWidget::new(("Go Back".into(), FormButtonActions::Cancel));
|
||||
buttons.set_focus(true);
|
||||
buttons.set_cursor(1);
|
||||
EditAttachments {
|
||||
account_hash,
|
||||
mode: EditAttachmentMode::Overview,
|
||||
buttons,
|
||||
cursor: EditAttachmentCursor::Buttons,
|
||||
|
@ -63,13 +66,31 @@ impl EditAttachments {
|
|||
}
|
||||
|
||||
impl EditAttachmentsRefMut<'_, '_> {
|
||||
fn new_edit_widget(&self, no: usize) -> Option<Box<FormWidget<FormButtonActions>>> {
|
||||
fn new_edit_widget(
|
||||
&self,
|
||||
no: usize,
|
||||
context: &Context,
|
||||
) -> Option<Box<FormWidget<FormButtonActions>>> {
|
||||
if no >= self.draft.attachments().len() {
|
||||
return None;
|
||||
}
|
||||
let filename = self.draft.attachments()[no].content_type().name();
|
||||
let mime_type = self.draft.attachments()[no].content_type();
|
||||
let mut ret = FormWidget::new(("Save".into(), FormButtonActions::Accept));
|
||||
let shortcuts = self.shortcuts(context);
|
||||
|
||||
let mut ret = FormWidget::new(
|
||||
("Save".into(), FormButtonActions::Accept),
|
||||
/* cursor_up_shortcut */
|
||||
shortcuts
|
||||
.get(Shortcuts::COMPOSING)
|
||||
.and_then(|c| c.get("scroll_up").cloned())
|
||||
.unwrap_or_else(|| context.settings.shortcuts.composing.scroll_up.clone()),
|
||||
/* cursor_down_shortcut */
|
||||
shortcuts
|
||||
.get(Shortcuts::COMPOSING)
|
||||
.and_then(|c| c.get("scroll_down").cloned())
|
||||
.unwrap_or_else(|| context.settings.shortcuts.composing.scroll_down.clone()),
|
||||
);
|
||||
|
||||
ret.add_button(("Reset".into(), FormButtonActions::Reset));
|
||||
ret.add_button(("Cancel".into(), FormButtonActions::Cancel));
|
||||
|
@ -188,7 +209,7 @@ impl Component for EditAttachmentsRefMut<'_, '_> {
|
|||
}
|
||||
Some(FormButtonActions::Reset) => {
|
||||
let no = *no;
|
||||
if let Some(inner) = self.new_edit_widget(no) {
|
||||
if let Some(inner) = self.new_edit_widget(no, context) {
|
||||
self.inner.mode = EditAttachmentMode::Edit { inner, no };
|
||||
}
|
||||
}
|
||||
|
@ -197,8 +218,12 @@ impl Component for EditAttachmentsRefMut<'_, '_> {
|
|||
return true;
|
||||
}
|
||||
} else {
|
||||
let shortcuts = self.shortcuts(context);
|
||||
|
||||
match event {
|
||||
UIEvent::Input(Key::Up) => {
|
||||
UIEvent::Input(ref key)
|
||||
if shortcut!(key == shortcuts[Shortcuts::COMPOSING]["scroll_up"]) =>
|
||||
{
|
||||
self.set_dirty(true);
|
||||
match self.inner.cursor {
|
||||
EditAttachmentCursor::AttachmentNo(ref mut n) => {
|
||||
|
@ -224,7 +249,9 @@ impl Component for EditAttachmentsRefMut<'_, '_> {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Down) => {
|
||||
UIEvent::Input(ref key)
|
||||
if shortcut!(key == shortcuts[Shortcuts::COMPOSING]["scroll_down"]) =>
|
||||
{
|
||||
self.set_dirty(true);
|
||||
match self.inner.cursor {
|
||||
EditAttachmentCursor::AttachmentNo(ref mut n) => {
|
||||
|
@ -246,7 +273,7 @@ impl Component for EditAttachmentsRefMut<'_, '_> {
|
|||
UIEvent::Input(Key::Char('\n')) => {
|
||||
match self.inner.cursor {
|
||||
EditAttachmentCursor::AttachmentNo(ref no) => {
|
||||
if let Some(inner) = self.new_edit_widget(*no) {
|
||||
if let Some(inner) = self.new_edit_widget(*no, context) {
|
||||
self.inner.mode = EditAttachmentMode::Edit { inner, no: *no };
|
||||
}
|
||||
self.set_dirty(true);
|
||||
|
@ -293,8 +320,17 @@ impl Component for EditAttachmentsRefMut<'_, '_> {
|
|||
|
||||
fn kill(&mut self, _uuid: ComponentId, _context: &mut Context) {}
|
||||
|
||||
fn shortcuts(&self, _context: &Context) -> ShortcutMaps {
|
||||
ShortcutMaps::default()
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = ShortcutMaps::default();
|
||||
|
||||
let our_map: ShortcutMap = self
|
||||
.inner
|
||||
.account_hash
|
||||
.map(|acc| account_settings!(context[acc].shortcuts.composing).key_values())
|
||||
.unwrap_or_else(|| context.settings.shortcuts.composing.key_values());
|
||||
map.insert(Shortcuts::COMPOSING, our_map);
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
fn id(&self) -> ComponentId {
|
||||
|
|
|
@ -2030,7 +2030,7 @@ impl Component for Listing {
|
|||
UIEvent::Action(Action::Compose(ComposeAction::Mailto(ref mailto))) => {
|
||||
let account_hash = context.accounts[self.cursor_pos.0].hash();
|
||||
let mut composer = Composer::with_account(account_hash, context);
|
||||
composer.set_draft(mailto.into());
|
||||
composer.set_draft(mailto.into(), context);
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::Action(Tab(New(Some(Box::new(composer))))));
|
||||
|
|
|
@ -58,6 +58,7 @@ pub struct MailView {
|
|||
coordinates: Option<(AccountHash, MailboxHash, EnvelopeHash)>,
|
||||
dirty: bool,
|
||||
contact_selector: Option<Box<UIDialog<Card>>>,
|
||||
forward_dialog: Option<Box<UIDialog<Option<PendingReplyAction>>>>,
|
||||
theme_default: ThemeAttribute,
|
||||
active_jobs: HashSet<JobId>,
|
||||
state: MailViewState,
|
||||
|
@ -68,6 +69,7 @@ impl Clone for MailView {
|
|||
fn clone(&self) -> Self {
|
||||
MailView {
|
||||
contact_selector: None,
|
||||
forward_dialog: None,
|
||||
state: MailViewState::default(),
|
||||
active_jobs: self.active_jobs.clone(),
|
||||
..*self
|
||||
|
@ -90,6 +92,7 @@ impl MailView {
|
|||
coordinates,
|
||||
dirty: true,
|
||||
contact_selector: None,
|
||||
forward_dialog: None,
|
||||
theme_default: crate::conf::value(context, "mail.view.body"),
|
||||
active_jobs: Default::default(),
|
||||
state: MailViewState::default(),
|
||||
|
@ -328,6 +331,8 @@ impl Component for MailView {
|
|||
};
|
||||
if let Some(ref mut s) = self.contact_selector.as_mut() {
|
||||
s.draw(grid, area, context);
|
||||
} else if let Some(ref mut s) = self.forward_dialog.as_mut() {
|
||||
s.draw(grid, area, context);
|
||||
}
|
||||
|
||||
self.dirty = false;
|
||||
|
@ -339,13 +344,29 @@ impl Component for MailView {
|
|||
return false;
|
||||
}
|
||||
|
||||
if let Some(ref mut s) = self.contact_selector {
|
||||
if s.process_event(event, context) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut s) = self.forward_dialog {
|
||||
if s.process_event(event, context) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* If envelope data is loaded, pass it to envelope views */
|
||||
if self.state.process_event(event, context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
match (&mut self.contact_selector, &mut event) {
|
||||
(Some(ref s), UIEvent::FinishedUIDialog(id, results)) if *id == s.id() => {
|
||||
match (
|
||||
&mut self.contact_selector,
|
||||
&mut self.forward_dialog,
|
||||
&mut event,
|
||||
) {
|
||||
(Some(ref s), _, UIEvent::FinishedUIDialog(id, results)) if *id == s.id() => {
|
||||
if let Some(results) = results.downcast_ref::<Vec<Card>>() {
|
||||
let account = &mut context.accounts[&coordinates.0];
|
||||
{
|
||||
|
@ -353,54 +374,61 @@ impl Component for MailView {
|
|||
account.address_book.add_card(card.clone());
|
||||
}
|
||||
}
|
||||
self.contact_selector = None;
|
||||
}
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
(Some(ref mut s), _) => {
|
||||
if s.process_event(event, context) {
|
||||
return true;
|
||||
(_, Some(ref s), UIEvent::FinishedUIDialog(id, result)) if *id == s.id() => {
|
||||
if let Some(result) = result.downcast_ref::<Option<PendingReplyAction>>() {
|
||||
self.forward_dialog = None;
|
||||
if let Some(result) = *result {
|
||||
self.perform_action(result, context);
|
||||
}
|
||||
}
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
_ => match event {
|
||||
UIEvent::StatusEvent(StatusEvent::JobFinished(ref job_id))
|
||||
if self.active_jobs.contains(job_id) =>
|
||||
{
|
||||
match self.state {
|
||||
MailViewState::LoadingBody {
|
||||
ref mut handle,
|
||||
pending_action: _,
|
||||
} if handle.job_id == *job_id => {
|
||||
match handle.chan.try_recv() {
|
||||
Err(_) => { /* Job was canceled */ }
|
||||
Ok(None) => { /* something happened, perhaps a worker
|
||||
* thread panicked */
|
||||
}
|
||||
Ok(Some(Ok(bytes))) => {
|
||||
MailViewState::load_bytes(self, bytes, context);
|
||||
}
|
||||
Ok(Some(Err(err))) => {
|
||||
self.state = MailViewState::Error { err };
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
match &event {
|
||||
UIEvent::StatusEvent(StatusEvent::JobFinished(ref job_id))
|
||||
if self.active_jobs.contains(job_id) =>
|
||||
{
|
||||
match self.state {
|
||||
MailViewState::LoadingBody {
|
||||
ref mut handle,
|
||||
pending_action: _,
|
||||
} if handle.job_id == *job_id => {
|
||||
match handle.chan.try_recv() {
|
||||
Err(_) => { /* Job was canceled */ }
|
||||
Ok(None) => { /* something happened, perhaps a worker
|
||||
* thread panicked */
|
||||
}
|
||||
Ok(Some(Ok(bytes))) => {
|
||||
MailViewState::load_bytes(self, bytes, context);
|
||||
}
|
||||
Ok(Some(Err(err))) => {
|
||||
self.state = MailViewState::Error { err };
|
||||
}
|
||||
}
|
||||
MailViewState::Init { .. } => {
|
||||
self.init_futures(context);
|
||||
}
|
||||
MailViewState::Loaded { .. } => {
|
||||
log::debug!(
|
||||
"MailView.active_jobs contains job id {:?} but MailViewState is \
|
||||
already loaded; what job was this and why was it in active_jobs?",
|
||||
job_id
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.active_jobs.remove(job_id);
|
||||
self.set_dirty(true);
|
||||
MailViewState::Init { .. } => {
|
||||
self.init_futures(context);
|
||||
}
|
||||
MailViewState::Loaded { .. } => {
|
||||
log::debug!(
|
||||
"MailView.active_jobs contains job id {:?} but MailViewState is \
|
||||
already loaded; what job was this and why was it in active_jobs?",
|
||||
job_id
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
self.active_jobs.remove(job_id);
|
||||
self.set_dirty(true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let shortcuts = &self.shortcuts(context);
|
||||
|
@ -436,27 +464,28 @@ impl Component for MailView {
|
|||
.forward_as_attachment
|
||||
) {
|
||||
f if f.is_ask() => {
|
||||
let id = self.id;
|
||||
context.replies.push_back(UIEvent::GlobalUIDialog(Box::new(
|
||||
UIConfirmationDialog::new(
|
||||
"How do you want the email to be forwarded?",
|
||||
vec![
|
||||
(true, "inline".to_string()),
|
||||
(false, "as attachment".to_string()),
|
||||
],
|
||||
true,
|
||||
Some(Box::new(move |_: ComponentId, result: bool| {
|
||||
self.forward_dialog = Some(Box::new(UIDialog::new(
|
||||
"How do you want the email to be forwarded?",
|
||||
vec![
|
||||
(
|
||||
Some(PendingReplyAction::ForwardInline),
|
||||
"inline".to_string(),
|
||||
),
|
||||
(
|
||||
Some(PendingReplyAction::ForwardAttachment),
|
||||
"as attachment".to_string(),
|
||||
),
|
||||
],
|
||||
true,
|
||||
Some(Box::new(
|
||||
move |id: ComponentId, result: &[Option<PendingReplyAction>]| {
|
||||
Some(UIEvent::FinishedUIDialog(
|
||||
id,
|
||||
Box::new(if result {
|
||||
PendingReplyAction::ForwardInline
|
||||
} else {
|
||||
PendingReplyAction::ForwardAttachment
|
||||
}),
|
||||
Box::new(result.get(0).cloned()),
|
||||
))
|
||||
})),
|
||||
context,
|
||||
),
|
||||
},
|
||||
)),
|
||||
context,
|
||||
)));
|
||||
}
|
||||
f if f.is_true() => {
|
||||
|
@ -562,9 +591,10 @@ impl Component for MailView {
|
|||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Alt(''))
|
||||
if self.contact_selector.is_some() =>
|
||||
if self.contact_selector.is_some() || self.forward_dialog.is_some() =>
|
||||
{
|
||||
self.contact_selector = None;
|
||||
self.forward_dialog = None;
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
|
@ -592,7 +622,7 @@ impl Component for MailView {
|
|||
let draft: Draft = mailto.into();
|
||||
let mut composer =
|
||||
Composer::with_account(coordinates.0, context);
|
||||
composer.set_draft(draft);
|
||||
composer.set_draft(draft, context);
|
||||
context.replies.push_back(UIEvent::Action(Tab(New(Some(
|
||||
Box::new(composer),
|
||||
)))));
|
||||
|
@ -747,12 +777,19 @@ impl Component for MailView {
|
|||
.as_ref()
|
||||
.map(|s| s.is_dirty())
|
||||
.unwrap_or(false)
|
||||
|| self
|
||||
.forward_dialog
|
||||
.as_ref()
|
||||
.map(|s| s.is_dirty())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, value: bool) {
|
||||
self.dirty = value;
|
||||
if let Some(ref mut s) = self.contact_selector {
|
||||
s.set_dirty(value);
|
||||
} else if let Some(ref mut s) = self.forward_dialog {
|
||||
s.set_dirty(value);
|
||||
}
|
||||
self.state.set_dirty(value);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ use melib::{Envelope, Error, Mail, Result};
|
|||
use super::{EnvelopeView, MailView, ViewSettings};
|
||||
use crate::{jobs::JoinHandle, mailbox_settings, Component, Context, ShortcutMaps, UIEvent};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum PendingReplyAction {
|
||||
Reply,
|
||||
ReplyToAuthor,
|
||||
|
|
|
@ -693,7 +693,7 @@ impl Component for Pager {
|
|||
}
|
||||
UIEvent::Input(ref key)
|
||||
if shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_right"])
|
||||
&& dbg!(self.cols_lt_width) =>
|
||||
&& self.cols_lt_width =>
|
||||
{
|
||||
self.movement = Some(PageMovement::Right(1));
|
||||
self.dirty = true;
|
||||
|
|
|
@ -203,7 +203,7 @@ pub enum FormButtonActions {
|
|||
Other(&'static str),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug)]
|
||||
pub struct FormWidget<T>
|
||||
where
|
||||
T: 'static + std::fmt::Debug + Copy + Default + Send + Sync,
|
||||
|
@ -217,21 +217,47 @@ where
|
|||
focus: FormFocus,
|
||||
hide_buttons: bool,
|
||||
dirty: bool,
|
||||
cursor_up_shortcut: Key,
|
||||
cursor_down_shortcut: Key,
|
||||
id: ComponentId,
|
||||
}
|
||||
|
||||
impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> Default for FormWidget<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
fields: Default::default(),
|
||||
layout: Default::default(),
|
||||
buttons: Default::default(),
|
||||
focus: FormFocus::Fields,
|
||||
hide_buttons: false,
|
||||
field_name_max_length: 10,
|
||||
cursor: 0,
|
||||
dirty: true,
|
||||
cursor_up_shortcut: Key::Up,
|
||||
cursor_down_shortcut: Key::Down,
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> fmt::Display for FormWidget<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Display::fmt("", f)
|
||||
write!(f, "form")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> FormWidget<T> {
|
||||
pub fn new(action: (Cow<'static, str>, T)) -> FormWidget<T> {
|
||||
pub fn new(
|
||||
action: (Cow<'static, str>, T),
|
||||
cursor_up_shortcut: Key,
|
||||
cursor_down_shortcut: Key,
|
||||
) -> FormWidget<T> {
|
||||
FormWidget {
|
||||
buttons: ButtonWidget::new(action),
|
||||
focus: FormFocus::Fields,
|
||||
hide_buttons: false,
|
||||
cursor_up_shortcut,
|
||||
cursor_down_shortcut,
|
||||
id: ComponentId::default(),
|
||||
dirty: true,
|
||||
..Default::default()
|
||||
|
@ -269,6 +295,7 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> FormWidget<T>
|
|||
self.fields
|
||||
.insert(value.0, Field::Choice(value.1, 0, ComponentId::default()));
|
||||
}
|
||||
|
||||
pub fn push_cl(&mut self, value: (Cow<'static, str>, String, AutoCompleteFn)) {
|
||||
self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len());
|
||||
self.layout.push(value.0.clone());
|
||||
|
@ -280,6 +307,7 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> FormWidget<T>
|
|||
)),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: (Cow<'static, str>, String)) {
|
||||
self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len());
|
||||
self.layout.push(value.0.clone());
|
||||
|
@ -309,6 +337,7 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> FormWidget<T>
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buttons_result(&self) -> Option<T> {
|
||||
self.buttons.result
|
||||
}
|
||||
|
@ -423,13 +452,19 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> Component for
|
|||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
}
|
||||
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
if self.focus == FormFocus::Buttons && self.buttons.process_event(event, context) {
|
||||
if !self.hide_buttons
|
||||
&& self.focus == FormFocus::Buttons
|
||||
&& self.buttons.process_event(event, context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
match *event {
|
||||
UIEvent::Input(Key::Up) if self.focus == FormFocus::Buttons => {
|
||||
UIEvent::Input(ref k)
|
||||
if *k == self.cursor_up_shortcut && self.focus == FormFocus::Buttons =>
|
||||
{
|
||||
self.focus = FormFocus::Fields;
|
||||
self.buttons.set_focus(false);
|
||||
self.set_dirty(true);
|
||||
|
@ -441,7 +476,7 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> Component for
|
|||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Up) => {
|
||||
UIEvent::Input(ref k) if *k == self.cursor_up_shortcut => {
|
||||
self.cursor = self.cursor.saturating_sub(1);
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
|
@ -452,12 +487,17 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> Component for
|
|||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Down) if self.cursor < self.layout.len().saturating_sub(1) => {
|
||||
UIEvent::Input(ref k)
|
||||
if *k == self.cursor_down_shortcut
|
||||
&& self.cursor < self.layout.len().saturating_sub(1) =>
|
||||
{
|
||||
self.cursor += 1;
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(Key::Down) if self.focus == FormFocus::Fields => {
|
||||
UIEvent::Input(ref k)
|
||||
if *k == self.cursor_down_shortcut && self.focus == FormFocus::Fields =>
|
||||
{
|
||||
self.focus = FormFocus::Buttons;
|
||||
self.buttons.set_focus(true);
|
||||
self.set_dirty(true);
|
||||
|
@ -517,9 +557,11 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> Component for
|
|||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
self.dirty || self.buttons.is_dirty()
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, value: bool) {
|
||||
self.dirty = value;
|
||||
self.buttons.set_dirty(value);
|
||||
|
@ -630,6 +672,7 @@ where
|
|||
self.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool {
|
||||
match *event {
|
||||
UIEvent::Input(Key::Char('\n')) => {
|
||||
|
@ -655,9 +698,11 @@ where
|
|||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
self.dirty
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, value: bool) {
|
||||
self.dirty = value;
|
||||
}
|
||||
|
|
|
@ -228,7 +228,7 @@ shortcut_key_values! { "general",
|
|||
|
||||
shortcut_key_values! { "composing",
|
||||