Add {Timer,Component}Id wrapper types over Uuid
parent
4da5366959
commit
96537e48c5
|
@ -19,16 +19,14 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* User actions that need to be handled by the UI
|
||||
*/
|
||||
//! User actions that need to be handled by the UI
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use melib::email::mailto::Mailto;
|
||||
pub use melib::thread::{SortField, SortOrder};
|
||||
use melib::{email::mailto::Mailto, uuid::Uuid};
|
||||
|
||||
use crate::components::Component;
|
||||
use crate::components::{Component, ComponentId};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TagAction {
|
||||
|
@ -61,7 +59,7 @@ pub enum ListingAction {
|
|||
#[derive(Debug)]
|
||||
pub enum TabAction {
|
||||
Close,
|
||||
Kill(Uuid),
|
||||
Kill(ComponentId),
|
||||
New(Option<Box<dyn Component>>),
|
||||
ManageMailboxes,
|
||||
}
|
||||
|
|
|
@ -22,8 +22,9 @@
|
|||
/*! Components visual and logical separations of application interfaces.
|
||||
*
|
||||
* They can draw on the terminal and receive events, but also do other stuff
|
||||
* as well. (For example, see the `notifications` module.)
|
||||
* See the `Component` Trait for more details.
|
||||
* as well.
|
||||
* For an example, see the [`notifications`] module.
|
||||
* See also the [`Component`] trait for more details.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
|
@ -57,7 +58,46 @@ use std::{
|
|||
use indexmap::IndexMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub type ComponentId = Uuid;
|
||||
#[derive(Clone, Copy, Eq, Deserialize, Hash, Ord, PartialOrd, PartialEq, Serialize)]
|
||||
#[repr(transparent)]
|
||||
pub struct ComponentId(Uuid);
|
||||
|
||||
impl AsRef<Uuid> for ComponentId {
|
||||
fn as_ref(&self) -> &Uuid {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ComponentId {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(&self.0.as_simple(), fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ComponentId {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(&self.0.as_simple(), fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ComponentId {
|
||||
fn default() -> Self {
|
||||
Self(Uuid::new_v4())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::LowerHex for ComponentId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::LowerHex::fmt(self.0.as_hyphenated(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::UpperHex for ComponentId {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::UpperHex::fmt(self.0.as_hyphenated(), f)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ShortcutMap = IndexMap<&'static str, Key>;
|
||||
pub type ShortcutMaps = IndexMap<&'static str, ShortcutMap>;
|
||||
|
@ -92,28 +132,37 @@ pub enum ScrollUpdate {
|
|||
|
||||
/// Types implementing this Trait can draw on the terminal and receive events.
|
||||
/// If a type wants to skip drawing if it has not changed anything, it can hold
|
||||
/// some flag in its fields (eg self.dirty = false) and act upon that in their
|
||||
/// `draw` implementation.
|
||||
/// some flag in its fields (eg `self.dirty = false`) and act upon that in their
|
||||
/// [`draw`](Component::draw) implementation.
|
||||
pub trait Component: Display + Debug + Send + Sync {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context);
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool;
|
||||
fn is_dirty(&self) -> bool;
|
||||
/// If the component is meant to be currently visible to the user.
|
||||
fn is_visible(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// If the component can quit right away without any unsaved or ongoing
|
||||
/// operations.
|
||||
fn can_quit_cleanly(&mut self, _context: &Context) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, value: bool);
|
||||
|
||||
fn kill(&mut self, _id: ComponentId, _context: &mut Context) {}
|
||||
|
||||
fn set_id(&mut self, _id: ComponentId) {}
|
||||
|
||||
fn id(&self) -> ComponentId;
|
||||
|
||||
fn get_shortcuts(&self, _context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, _context: &Context) -> ShortcutMaps {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn get_status(&self, _context: &Context) -> String {
|
||||
/// Get status message for the status line.
|
||||
fn status(&self, _context: &Context) -> String {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ enum ViewMode {
|
|||
#[derive(Debug)]
|
||||
pub struct ContactManager {
|
||||
id: ComponentId,
|
||||
parent_id: ComponentId,
|
||||
parent_id: Option<ComponentId>,
|
||||
pub card: Card,
|
||||
mode: ViewMode,
|
||||
form: FormWidget<bool>,
|
||||
|
@ -63,8 +63,8 @@ impl ContactManager {
|
|||
fn new(context: &Context) -> Self {
|
||||
let theme_default: ThemeAttribute = crate::conf::value(context, "theme_default");
|
||||
ContactManager {
|
||||
id: Uuid::nil(),
|
||||
parent_id: Uuid::nil(),
|
||||
id: ComponentId::default(),
|
||||
parent_id: None,
|
||||
card: Card::new(),
|
||||
mode: ViewMode::Edit,
|
||||
form: FormWidget::default(),
|
||||
|
@ -135,7 +135,7 @@ impl ContactManager {
|
|||
}
|
||||
|
||||
pub fn set_parent_id(&mut self, new_val: ComponentId) {
|
||||
self.parent_id = new_val;
|
||||
self.parent_id = Some(new_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,11 +188,11 @@ impl Component for ContactManager {
|
|||
}
|
||||
}
|
||||
ViewMode::Edit => {
|
||||
if let &mut UIEvent::Input(Key::Esc) = event {
|
||||
if let (Some(parent_id), &UIEvent::Input(Key::Esc)) = (self.parent_id, &event) {
|
||||
if self.can_quit_cleanly(context) {
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::Action(Tab(Kill(self.parent_id))));
|
||||
.push_back(UIEvent::Action(Tab(Kill(parent_id))));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ impl Component for ContactManager {
|
|||
s.to_string(),
|
||||
match v {
|
||||
Field::Text(v) => v.as_str().to_string(),
|
||||
Field::Choice(mut v, c) => v.remove(c).to_string(),
|
||||
Field::Choice(mut v, c, _) => v.remove(c).to_string(),
|
||||
},
|
||||
)
|
||||
})
|
||||
|
@ -277,25 +277,28 @@ impl Component for ContactManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
let parent_id = self.parent_id;
|
||||
/* Play it safe and ask user for confirmation */
|
||||
self.mode = ViewMode::Discard(Box::new(UIDialog::new(
|
||||
"this contact has unsaved changes",
|
||||
vec![
|
||||
('x', "quit without saving".to_string()),
|
||||
('y', "save draft and quit".to_string()),
|
||||
('n', "cancel".to_string()),
|
||||
],
|
||||
true,
|
||||
Some(Box::new(move |_, results: &[char]| match results[0] {
|
||||
'x' => Some(UIEvent::Action(Tab(Kill(parent_id)))),
|
||||
'n' => None,
|
||||
'y' => None,
|
||||
_ => None,
|
||||
})),
|
||||
context,
|
||||
)));
|
||||
self.set_dirty(true);
|
||||
false
|
||||
if let Some(parent_id) = self.parent_id {
|
||||
/* Play it safe and ask user for confirmation */
|
||||
self.mode = ViewMode::Discard(Box::new(UIDialog::new(
|
||||
"this contact has unsaved changes",
|
||||
vec![
|
||||
('x', "quit without saving".to_string()),
|
||||
('y', "save draft and quit".to_string()),
|
||||
('n', "cancel".to_string()),
|
||||
],
|
||||
true,
|
||||
Some(Box::new(move |_, results: &[char]| match results[0] {
|
||||
'x' => Some(UIEvent::Action(Tab(Kill(parent_id)))),
|
||||
'n' => None,
|
||||
'y' => None,
|
||||
_ => None,
|
||||
})),
|
||||
context,
|
||||
)));
|
||||
self.set_dirty(true);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ impl ContactList {
|
|||
sidebar_divider: context.settings.listing.sidebar_divider,
|
||||
sidebar_divider_theme: conf::value(context, "mail.sidebar_divider"),
|
||||
menu_visibility: true,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -631,7 +631,7 @@ impl Component for ContactList {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
if self.view.is_none() {
|
||||
match *event {
|
||||
UIEvent::Input(ref key)
|
||||
|
@ -724,7 +724,7 @@ impl Component for ContactList {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.get_status(context),
|
||||
self.status(context),
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -761,7 +761,7 @@ impl Component for ContactList {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.get_status(context),
|
||||
self.status(context),
|
||||
)));
|
||||
}
|
||||
return true;
|
||||
|
@ -928,15 +928,15 @@ impl Component for ContactList {
|
|||
self.dirty = value;
|
||||
}
|
||||
|
||||
fn kill(&mut self, uuid: Uuid, context: &mut Context) {
|
||||
fn kill(&mut self, uuid: ComponentId, context: &mut Context) {
|
||||
debug_assert!(uuid == self.id);
|
||||
context.replies.push_back(UIEvent::Action(Tab(Kill(uuid))));
|
||||
}
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = self
|
||||
.view
|
||||
.as_ref()
|
||||
.map(|p| p.get_shortcuts(context))
|
||||
.map(|p| p.shortcuts(context))
|
||||
.unwrap_or_default();
|
||||
|
||||
map.insert(
|
||||
|
@ -966,7 +966,7 @@ impl Component for ContactList {
|
|||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
fn get_status(&self, context: &Context) -> String {
|
||||
fn status(&self, context: &Context) -> String {
|
||||
format!(
|
||||
"{} entries",
|
||||
context.accounts[self.account_pos].address_book.len()
|
||||
|
|
|
@ -113,7 +113,7 @@ pub struct Composer {
|
|||
|
||||
#[derive(Debug)]
|
||||
enum ViewMode {
|
||||
Discard(Uuid, UIDialog<char>),
|
||||
Discard(ComponentId, UIDialog<char>),
|
||||
EditAttachments {
|
||||
widget: EditAttachments,
|
||||
},
|
||||
|
@ -175,7 +175,7 @@ impl Composer {
|
|||
embed_area: ((0, 0), (0, 0)),
|
||||
embed: None,
|
||||
initialized: false,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1122,7 +1122,7 @@ impl Component for Composer {
|
|||
if let UIEvent::VisibilityChange(_) = event {
|
||||
self.pager.process_event(event, context);
|
||||
}
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
match (&mut self.mode, &mut event) {
|
||||
(ViewMode::Edit, _) => {
|
||||
if self.pager.process_event(event, context) {
|
||||
|
@ -2090,7 +2090,7 @@ impl Component for Composer {
|
|||
}
|
||||
}
|
||||
|
||||
fn kill(&mut self, uuid: Uuid, context: &mut Context) {
|
||||
fn kill(&mut self, uuid: ComponentId, context: &mut Context) {
|
||||
if self.id != uuid {
|
||||
return;
|
||||
}
|
||||
|
@ -2121,9 +2121,9 @@ impl Component for Composer {
|
|||
);
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = if self.mode.is_edit() {
|
||||
self.pager.get_shortcuts(context)
|
||||
self.pager.shortcuts(context)
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
|
|
@ -57,7 +57,7 @@ impl EditAttachments {
|
|||
buttons,
|
||||
cursor: EditAttachmentCursor::Buttons,
|
||||
dirty: true,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,9 +291,9 @@ impl Component for EditAttachmentsRefMut<'_, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn kill(&mut self, _uuid: Uuid, _context: &mut Context) {}
|
||||
fn kill(&mut self, _uuid: ComponentId, _context: &mut Context) {}
|
||||
|
||||
fn get_shortcuts(&self, _context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, _context: &Context) -> ShortcutMaps {
|
||||
ShortcutMaps::default()
|
||||
}
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ impl Component for KeySelection {
|
|||
Ok(Some(Err(err))) => {
|
||||
*self = KeySelection::Error {
|
||||
err,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -229,14 +229,14 @@ impl Component for KeySelection {
|
|||
}
|
||||
}
|
||||
|
||||
fn kill(&mut self, _uuid: Uuid, _context: &mut Context) {}
|
||||
fn kill(&mut self, _uuid: ComponentId, _context: &mut Context) {}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
match self {
|
||||
KeySelection::LoadingKeys { .. } | KeySelection::Error { .. } => {
|
||||
ShortcutMaps::default()
|
||||
}
|
||||
KeySelection::Loaded { ref widget, .. } => widget.get_shortcuts(context),
|
||||
KeySelection::Loaded { ref widget, .. } => widget.shortcuts(context),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1041,8 +1041,8 @@ impl Component for Listing {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(match msg {
|
||||
Some(msg) => format!("{} {}", self.get_status(context), msg),
|
||||
None => self.get_status(context),
|
||||
Some(msg) => format!("{} {}", self.status(context), msg),
|
||||
None => self.status(context),
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
@ -1105,7 +1105,7 @@ impl Component for Listing {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.get_status(context),
|
||||
self.status(context),
|
||||
)));
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
|
@ -1149,7 +1149,7 @@ impl Component for Listing {
|
|||
return true;
|
||||
}
|
||||
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
if self.focus == ListingFocus::Mailbox {
|
||||
match *event {
|
||||
UIEvent::Input(Key::Mouse(MouseEvent::Press(MouseButton::Left, x, _y)))
|
||||
|
@ -1634,7 +1634,7 @@ impl Component for Listing {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.get_status(context),
|
||||
self.status(context),
|
||||
)));
|
||||
return true;
|
||||
}
|
||||
|
@ -1910,7 +1910,7 @@ impl Component for Listing {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.get_status(context),
|
||||
self.status(context),
|
||||
)));
|
||||
}
|
||||
UIEvent::Input(Key::Backspace) if !self.cmd_buf.is_empty() => {
|
||||
|
@ -1963,11 +1963,11 @@ impl Component for Listing {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = if let Some(s) = self.status.as_ref() {
|
||||
s.get_shortcuts(context)
|
||||
s.shortcuts(context)
|
||||
} else {
|
||||
self.component.get_shortcuts(context)
|
||||
self.component.shortcuts(context)
|
||||
};
|
||||
let mut config_map = context.settings.shortcuts.listing.key_values();
|
||||
if self.focus != ListingFocus::Menu {
|
||||
|
@ -1985,7 +1985,7 @@ impl Component for Listing {
|
|||
self.component.set_id(id);
|
||||
}
|
||||
|
||||
fn get_status(&self, context: &Context) -> String {
|
||||
fn status(&self, context: &Context) -> String {
|
||||
let mailbox_hash = match self.cursor_pos.1 {
|
||||
MenuEntryCursor::Mailbox(idx) => {
|
||||
if let Some(MailboxMenuEntry { mailbox_hash, .. }) =
|
||||
|
@ -2075,7 +2075,7 @@ impl Listing {
|
|||
show_menu_scrollbar: ShowMenuScrollbar::Never,
|
||||
startup_checks_rate: RateLimit::new(2, 1000, context.job_executor.clone()),
|
||||
theme_default: conf::value(context, "theme_default"),
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
sidebar_divider: *account_settings!(
|
||||
context[first_account_hash].listing.sidebar_divider
|
||||
),
|
||||
|
@ -2592,7 +2592,7 @@ impl Listing {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.get_status(context),
|
||||
self.status(context),
|
||||
)));
|
||||
}
|
||||
MenuEntryCursor::Status => {
|
||||
|
@ -2618,7 +2618,7 @@ impl Listing {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.get_status(context),
|
||||
self.status(context),
|
||||
)));
|
||||
}
|
||||
|
||||
|
|
|
@ -894,7 +894,7 @@ impl CompactListing {
|
|||
movement: None,
|
||||
modifier_active: false,
|
||||
modifier_command: None,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1687,7 +1687,7 @@ impl Component for CompactListing {
|
|||
}
|
||||
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
|
||||
match (&event, self.focus) {
|
||||
(UIEvent::Input(ref k), Focus::Entry)
|
||||
|
@ -2023,9 +2023,9 @@ impl Component for CompactListing {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = if self.unfocused() {
|
||||
self.view.get_shortcuts(context)
|
||||
self.view.shortcuts(context)
|
||||
} else {
|
||||
ShortcutMaps::default()
|
||||
};
|
||||
|
|
|
@ -636,7 +636,7 @@ impl ConversationsListing {
|
|||
movement: None,
|
||||
modifier_active: false,
|
||||
modifier_command: None,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1234,7 +1234,7 @@ impl Component for ConversationsListing {
|
|||
}
|
||||
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
|
||||
match (&event, self.focus) {
|
||||
(UIEvent::Input(ref k), Focus::Entry)
|
||||
|
@ -1545,9 +1545,9 @@ impl Component for ConversationsListing {
|
|||
self.dirty = value;
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = if self.unfocused() {
|
||||
self.view.get_shortcuts(context)
|
||||
self.view.shortcuts(context)
|
||||
} else {
|
||||
ShortcutMaps::default()
|
||||
};
|
||||
|
|
|
@ -117,7 +117,7 @@ impl OfflineListing {
|
|||
_selection: HashMap::default(),
|
||||
messages: vec![],
|
||||
dirty: true,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -628,7 +628,7 @@ impl PlainListing {
|
|||
movement: None,
|
||||
modifier_active: false,
|
||||
modifier_command: None,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1346,7 +1346,7 @@ impl Component for PlainListing {
|
|||
}
|
||||
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
|
||||
match (&event, self.focus) {
|
||||
(UIEvent::Input(ref k), Focus::Entry)
|
||||
|
@ -1594,9 +1594,9 @@ impl Component for PlainListing {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = if self.unfocused() {
|
||||
self.view.get_shortcuts(context)
|
||||
self.view.shortcuts(context)
|
||||
} else {
|
||||
ShortcutMaps::default()
|
||||
};
|
||||
|
|
|
@ -696,7 +696,7 @@ impl ThreadListing {
|
|||
movement: None,
|
||||
modifier_active: false,
|
||||
modifier_command: None,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
search_job: None,
|
||||
})
|
||||
}
|
||||
|
@ -1316,7 +1316,7 @@ impl Component for ThreadListing {
|
|||
}
|
||||
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
|
||||
match (&event, self.focus) {
|
||||
(UIEvent::Input(ref k), Focus::Entry)
|
||||
|
@ -1559,11 +1559,11 @@ impl Component for ThreadListing {
|
|||
self.dirty = value;
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = if self.unfocused() {
|
||||
self.view
|
||||
.as_ref()
|
||||
.map(|p| p.get_shortcuts(context))
|
||||
.map(|p| p.shortcuts(context))
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
ShortcutMaps::default()
|
||||
|
|
|
@ -57,7 +57,7 @@ impl AccountStatus {
|
|||
content,
|
||||
dirty: true,
|
||||
theme_default,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ impl Component for AccountStatus {
|
|||
}
|
||||
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
match *event {
|
||||
UIEvent::ConfigReload { old_settings: _ } => {
|
||||
self.theme_default = crate::conf::value(context, "theme_default");
|
||||
|
@ -447,7 +447,7 @@ impl Component for AccountStatus {
|
|||
false
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut ret: ShortcutMaps = ShortcutMaps::default();
|
||||
ret.insert(
|
||||
Shortcuts::GENERAL,
|
||||
|
|
|
@ -354,7 +354,7 @@ impl MailView {
|
|||
state: MailViewState::default(),
|
||||
force_charset: ForceCharset::None,
|
||||
cmd_buf: String::with_capacity(4),
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
};
|
||||
|
||||
ret.init_futures(context);
|
||||
|
@ -1789,7 +1789,7 @@ impl Component for MailView {
|
|||
_ => {}
|
||||
}
|
||||
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
match (&mut self.mode, &mut event) {
|
||||
/*(ViewMode::Ansi(ref mut buf), _) => {
|
||||
if buf.process_event(event, context) {
|
||||
|
@ -2015,7 +2015,7 @@ impl Component for MailView {
|
|||
},
|
||||
}
|
||||
|
||||
let shortcuts = &self.get_shortcuts(context);
|
||||
let shortcuts = &self.shortcuts(context);
|
||||
match *event {
|
||||
UIEvent::ConfigReload { old_settings: _ } => {
|
||||
self.theme_default = crate::conf::value(context, "theme_default");
|
||||
|
@ -2571,8 +2571,10 @@ impl Component for MailView {
|
|||
if let Some(filename) = u.filename() {
|
||||
path.push(filename);
|
||||
} else {
|
||||
let u = melib::uuid::Uuid::new_v4();
|
||||
path.push(u.as_hyphenated().to_string());
|
||||
path.push(format!(
|
||||
"meli_attachment_{a_i}_{}",
|
||||
Uuid::new_v4().as_simple()
|
||||
));
|
||||
}
|
||||
}
|
||||
match save_attachment(&path, &u.decode(Default::default())) {
|
||||
|
@ -2874,11 +2876,11 @@ impl Component for MailView {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = if let Some(ref sbv) = self.subview {
|
||||
sbv.get_shortcuts(context)
|
||||
sbv.shortcuts(context)
|
||||
} else {
|
||||
self.pager.get_shortcuts(context)
|
||||
self.pager.shortcuts(context)
|
||||
};
|
||||
|
||||
let mut our_map = context.settings.shortcuts.envelope_view.key_values();
|
||||
|
|
|
@ -79,7 +79,7 @@ impl EnvelopeView {
|
|||
mail,
|
||||
_account_hash,
|
||||
cmd_buf: String::with_capacity(4),
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ pub struct HtmlView {
|
|||
|
||||
impl HtmlView {
|
||||
pub fn new(body: &Attachment, context: &mut Context) -> Self {
|
||||
let id = ComponentId::new_v4();
|
||||
let id = ComponentId::default();
|
||||
let bytes: Vec<u8> = body.decode_rec(Default::default());
|
||||
|
||||
let settings = &context.settings;
|
||||
|
@ -193,12 +193,15 @@ impl Component for HtmlView {
|
|||
}
|
||||
false
|
||||
}
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
self.pager.get_shortcuts(context)
|
||||
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
self.pager.shortcuts(context)
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
self.pager.is_dirty()
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, value: bool) {
|
||||
self.pager.set_dirty(value);
|
||||
}
|
||||
|
@ -206,6 +209,7 @@ impl Component for HtmlView {
|
|||
fn id(&self) -> ComponentId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: ComponentId) {
|
||||
self.id = id;
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ impl ThreadView {
|
|||
cursor_pos: 1,
|
||||
new_cursor_pos: 0,
|
||||
dirty: true,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
indentation_colors: [
|
||||
crate::conf::value(context, "mail.view.thread.indentation.a"),
|
||||
crate::conf::value(context, "mail.view.thread.indentation.b"),
|
||||
|
@ -1002,7 +1002,7 @@ impl Component for ThreadView {
|
|||
return true;
|
||||
}
|
||||
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
match *event {
|
||||
UIEvent::Input(ref key)
|
||||
if shortcut!(key == shortcuts[Shortcuts::THREAD_VIEW]["scroll_up"]) =>
|
||||
|
@ -1157,8 +1157,8 @@ impl Component for ThreadView {
|
|||
self.mailview.set_dirty(value);
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = self.mailview.get_shortcuts(context);
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = self.mailview.shortcuts(context);
|
||||
|
||||
map.insert(
|
||||
Shortcuts::THREAD_VIEW,
|
||||
|
|
|
@ -87,7 +87,7 @@ impl MailboxManager {
|
|||
initialized: false,
|
||||
dirty: true,
|
||||
movement: None,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,7 +416,7 @@ impl Component for MailboxManager {
|
|||
return s.process_event(event, context);
|
||||
}
|
||||
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
match event {
|
||||
UIEvent::AccountStatusChange(account_hash, msg)
|
||||
if *account_hash == self.account_hash =>
|
||||
|
@ -428,8 +428,8 @@ impl Component for MailboxManager {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(match msg {
|
||||
Some(msg) => format!("{} {}", self.get_status(context), msg),
|
||||
None => self.get_status(context),
|
||||
Some(msg) => format!("{} {}", self.status(context), msg),
|
||||
None => self.status(context),
|
||||
})));
|
||||
}
|
||||
UIEvent::Input(ref key)
|
||||
|
@ -522,12 +522,12 @@ impl Component for MailboxManager {
|
|||
}
|
||||
}
|
||||
|
||||
fn kill(&mut self, uuid: Uuid, context: &mut Context) {
|
||||
fn kill(&mut self, uuid: ComponentId, context: &mut Context) {
|
||||
debug_assert!(uuid == self.id);
|
||||
context.replies.push_back(UIEvent::Action(Tab(Kill(uuid))));
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = ShortcutMaps::default();
|
||||
|
||||
map.insert(
|
||||
|
@ -550,7 +550,7 @@ impl Component for MailboxManager {
|
|||
true
|
||||
}
|
||||
|
||||
fn get_status(&self, _context: &Context) -> String {
|
||||
fn status(&self, _context: &Context) -> String {
|
||||
format!("{} entries", self.entries.len())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ mod dbus {
|
|||
#[derive(Debug)]
|
||||
pub struct DbusNotifications {
|
||||
rate_limit: RateLimit,
|
||||
id: ComponentId,
|
||||
}
|
||||
|
||||
impl fmt::Display for DbusNotifications {
|
||||
|
@ -50,6 +51,7 @@ mod dbus {
|
|||
pub fn new(context: &Context) -> Self {
|
||||
DbusNotifications {
|
||||
rate_limit: RateLimit::new(1000, 1000, context.job_executor.clone()),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,10 +133,12 @@ mod dbus {
|
|||
}
|
||||
|
||||
fn id(&self) -> ComponentId {
|
||||
ComponentId::nil()
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, _id: ComponentId) {}
|
||||
fn set_id(&mut self, id: ComponentId) {
|
||||
self.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
fn escape_str(s: &str) -> String {
|
||||
|
@ -168,11 +172,13 @@ mod dbus {
|
|||
|
||||
/// Passes notifications to a user defined shell command
|
||||
#[derive(Default, Debug)]
|
||||
pub struct NotificationCommand {}
|
||||
pub struct NotificationCommand {
|
||||
id: ComponentId,
|
||||
}
|
||||
|
||||
impl NotificationCommand {
|
||||
pub fn new() -> Self {
|
||||
NotificationCommand {}
|
||||
NotificationCommand::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,14 +276,20 @@ impl Component for NotificationCommand {
|
|||
|
||||
false
|
||||
}
|
||||
fn id(&self) -> ComponentId {
|
||||
ComponentId::nil()
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, _value: bool) {}
|
||||
fn set_id(&mut self, _id: ComponentId) {}
|
||||
|
||||
fn id(&self) -> ComponentId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: ComponentId) {
|
||||
self.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
fn update_xbiff(path: &str) -> Result<()> {
|
||||
|
|
|
@ -25,6 +25,7 @@ use super::*;
|
|||
#[derive(Debug)]
|
||||
pub struct SVGScreenshotFilter {
|
||||
save_screenshot: bool,
|
||||
id: ComponentId,
|
||||
}
|
||||
|
||||
impl fmt::Display for SVGScreenshotFilter {
|
||||
|
@ -37,6 +38,7 @@ impl SVGScreenshotFilter {
|
|||
pub fn new() -> Self {
|
||||
SVGScreenshotFilter {
|
||||
save_screenshot: false,
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -442,9 +444,12 @@ impl Component for SVGScreenshotFilter {
|
|||
}
|
||||
|
||||
fn id(&self) -> ComponentId {
|
||||
ComponentId::nil()
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: ComponentId) {
|
||||
self.id = id;
|
||||
}
|
||||
fn set_id(&mut self, _id: ComponentId) {}
|
||||
}
|
||||
|
||||
const CSS_STYLE: &str = r#"#t{font-family:'DejaVu Sans Mono',monospace;font-style:normal;font-size:14px;} text {dominant-baseline: text-before-edge; white-space: pre;} .f{fill:#e5e5e5;} .b{fill:#000;} .c0 {fill:#000;} .c1 {fill:#cd0000;} .c2 {fill:#00cd00;} .c3 {fill:#cdcd00;} .c4 {fill:#00e;} .c5 {fill:#cd00cd;} .c6 {fill:#00cdcd;} .c7 {fill:#e5e5e5;} .c8 {fill:#7f7f7f;} .c9 {fill:#f00;} .c10 {fill:#0f0;} .c11 {fill:#ff0;} .c12 {fill:#5c5cff;} .c13 {fill:#f0f;} .c14 {fill:#0ff;} .c15 {fill:#fff;}"#;
|
||||
|
|
|
@ -115,7 +115,7 @@ impl StatusBar {
|
|||
mode: UIMode::Normal,
|
||||
mouse: context.settings.terminal.use_mouse.is_true(),
|
||||
height: 1,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
auto_complete: AutoComplete::new(Vec::new()),
|
||||
progress_spinner,
|
||||
in_progress_jobs: HashSet::default(),
|
||||
|
@ -784,8 +784,8 @@ impl Component for StatusBar {
|
|||
self.progress_spinner.set_dirty(value);
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
self.container.get_shortcuts(context)
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
self.container.shortcuts(context)
|
||||
}
|
||||
|
||||
fn id(&self) -> ComponentId {
|
||||
|
@ -823,7 +823,7 @@ impl Tabbed {
|
|||
let mut ret = Tabbed {
|
||||
help_curr_views: children
|
||||
.get(0)
|
||||
.map(|c| c.get_shortcuts(context))
|
||||
.map(|c| c.shortcuts(context))
|
||||
.unwrap_or_default(),
|
||||
help_content: CellBuffer::default(),
|
||||
help_screen_cursor: (0, 0),
|
||||
|
@ -834,10 +834,10 @@ impl Tabbed {
|
|||
cursor_pos: 0,
|
||||
show_shortcuts: false,
|
||||
dirty: true,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
};
|
||||
ret.help_curr_views
|
||||
.extend(ret.get_shortcuts(context).into_iter());
|
||||
.extend(ret.shortcuts(context).into_iter());
|
||||
ret
|
||||
}
|
||||
fn draw_tabs(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
|
@ -969,8 +969,8 @@ impl Component for Tabbed {
|
|||
}
|
||||
|
||||
if (self.show_shortcuts && self.dirty) || must_redraw_shortcuts {
|
||||
let mut children_maps = self.children[self.cursor_pos].get_shortcuts(context);
|
||||
let our_map = self.get_shortcuts(context);
|
||||
let mut children_maps = self.children[self.cursor_pos].shortcuts(context);
|
||||
let our_map = self.shortcuts(context);
|
||||
children_maps.extend(our_map.into_iter());
|
||||
if children_maps.is_empty() {
|
||||
return;
|
||||
|
@ -1343,13 +1343,13 @@ impl Component for Tabbed {
|
|||
self.children[self.cursor_pos]
|
||||
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||
self.cursor_pos = no % self.children.len();
|
||||
let mut children_maps = self.children[self.cursor_pos].get_shortcuts(context);
|
||||
children_maps.extend(self.get_shortcuts(context));
|
||||
let mut children_maps = self.children[self.cursor_pos].shortcuts(context);
|
||||
children_maps.extend(self.shortcuts(context));
|
||||
self.help_curr_views = children_maps;
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.children[self.cursor_pos].get_status(context),
|
||||
self.children[self.cursor_pos].status(context),
|
||||
)));
|
||||
self.set_dirty(true);
|
||||
}
|
||||
|
@ -1361,13 +1361,13 @@ impl Component for Tabbed {
|
|||
self.children[self.cursor_pos]
|
||||
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||
self.cursor_pos = (self.cursor_pos + 1) % self.children.len();
|
||||
let mut children_maps = self.children[self.cursor_pos].get_shortcuts(context);
|
||||
children_maps.extend(self.get_shortcuts(context));
|
||||
let mut children_maps = self.children[self.cursor_pos].shortcuts(context);
|
||||
children_maps.extend(self.shortcuts(context));
|
||||
self.help_curr_views = children_maps;
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
self.children[self.cursor_pos].get_status(context),
|
||||
self.children[self.cursor_pos].status(context),
|
||||
)));
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
|
@ -1394,8 +1394,8 @@ impl Component for Tabbed {
|
|||
.process_event(&mut UIEvent::VisibilityChange(false), context);
|
||||
self.cursor_pos = self.children.len() - 1;
|
||||
self.children[self.cursor_pos].set_dirty(true);
|
||||
let mut children_maps = self.children[self.cursor_pos].get_shortcuts(context);
|
||||
children_maps.extend(self.get_shortcuts(context));
|
||||
let mut children_maps = self.children[self.cursor_pos].shortcuts(context);
|
||||
children_maps.extend(self.shortcuts(context));
|
||||
self.help_curr_views = children_maps;
|
||||
return true;
|
||||
}
|
||||
|
@ -1405,8 +1405,8 @@ impl Component for Tabbed {
|
|||
}
|
||||
let id = self.children[self.cursor_pos].id();
|
||||
self.children[self.cursor_pos].kill(id, context);
|
||||
let mut children_maps = self.children[self.cursor_pos].get_shortcuts(context);
|
||||
children_maps.extend(self.get_shortcuts(context));
|
||||
let mut children_maps = self.children[self.cursor_pos].shortcuts(context);
|
||||
children_maps.extend(self.shortcuts(context));
|
||||
self.help_curr_views = children_maps;
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
|
@ -1421,8 +1421,8 @@ impl Component for Tabbed {
|
|||
self.children.remove(c_idx);
|
||||
self.cursor_pos = 0;
|
||||
self.set_dirty(true);
|
||||
let mut children_maps = self.children[self.cursor_pos].get_shortcuts(context);
|
||||
children_maps.extend(self.get_shortcuts(context));
|
||||
let mut children_maps = self.children[self.cursor_pos].shortcuts(context);
|
||||
children_maps.extend(self.shortcuts(context));
|
||||
self.help_curr_views = children_maps;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -1525,9 +1525,11 @@ impl Component for Tabbed {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
self.dirty || self.children[self.cursor_pos].is_dirty()
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, value: bool) {
|
||||
self.dirty = value;
|
||||
self.children[self.cursor_pos].set_dirty(value);
|
||||
|
@ -1536,11 +1538,12 @@ impl Component for Tabbed {
|
|||
fn id(&self) -> ComponentId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: ComponentId) {
|
||||
self.id = id;
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = ShortcutMaps::default();
|
||||
map.insert(
|
||||
Shortcuts::GENERAL,
|
||||
|
@ -1566,6 +1569,7 @@ pub struct RawBuffer {
|
|||
pub buf: CellBuffer,
|
||||
title: Option<String>,
|
||||
cursor: (usize, usize),
|
||||
id: ComponentId,
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
|
@ -1639,7 +1643,11 @@ impl Component for RawBuffer {
|
|||
}
|
||||
|
||||
fn id(&self) -> ComponentId {
|
||||
ComponentId::nil()
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: ComponentId) {
|
||||
self.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1648,8 +1656,9 @@ impl RawBuffer {
|
|||
RawBuffer {
|
||||
buf,
|
||||
title,
|
||||
dirty: true,
|
||||
cursor: (0, 0),
|
||||
dirty: true,
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
pub fn title(&self) -> &str {
|
||||
|
|
|
@ -113,7 +113,7 @@ impl<T: 'static + PartialEq + Debug + Clone + Sync + Send> Component for UIDialo
|
|||
}
|
||||
|
||||
let (width, height) = self.content.size();
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
let mut highlighted_attrs = crate::conf::value(context, "widgets.options.highlighted");
|
||||
if !context.settings.terminal.use_color() {
|
||||
highlighted_attrs.attrs |= Attr::REVERSE;
|
||||
|
@ -404,7 +404,7 @@ impl<T: 'static + PartialEq + Debug + Clone + Sync + Send> Component for UIDialo
|
|||
false
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = ShortcutMaps::default();
|
||||
map.insert(
|
||||
Shortcuts::GENERAL,
|
||||
|
@ -442,7 +442,7 @@ impl Component for UIConfirmationDialog {
|
|||
}
|
||||
|
||||
let (width, height) = self.content.size();
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
let mut highlighted_attrs = crate::conf::value(context, "widgets.options.highlighted");
|
||||
if !context.settings.terminal.use_color() {
|
||||
highlighted_attrs.attrs |= Attr::REVERSE;
|
||||
|
@ -733,7 +733,7 @@ impl Component for UIConfirmationDialog {
|
|||
false
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut map = ShortcutMaps::default();
|
||||
map.insert(
|
||||
Shortcuts::GENERAL,
|
||||
|
@ -791,7 +791,7 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
|
|||
done_fn,
|
||||
dirty: true,
|
||||
theme_default: Default::default(),
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
};
|
||||
ret.initialise(context);
|
||||
ret
|
||||
|
|
|
@ -49,7 +49,7 @@ impl HSplit {
|
|||
bottom,
|
||||
show_divider,
|
||||
ratio,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,9 +101,9 @@ impl Component for HSplit {
|
|||
self.bottom.set_dirty(value);
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut top_map = self.top.get_shortcuts(context);
|
||||
top_map.extend(self.bottom.get_shortcuts(context).into_iter());
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut top_map = self.top.shortcuts(context);
|
||||
top_map.extend(self.bottom.shortcuts(context).into_iter());
|
||||
top_map
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ impl VSplit {
|
|||
show_divider,
|
||||
prev_visibility: (true, true),
|
||||
ratio,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -241,9 +241,9 @@ impl Component for VSplit {
|
|||
self.right.set_dirty(value);
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut right_map = self.right.get_shortcuts(context);
|
||||
right_map.extend(self.left.get_shortcuts(context).into_iter());
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut right_map = self.right.shortcuts(context);
|
||||
right_map.extend(self.left.shortcuts(context).into_iter());
|
||||
right_map
|
||||
}
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ impl Pager {
|
|||
minimum_width: pager_minimum_width,
|
||||
initialised: false,
|
||||
dirty: true,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
filtered_content: None,
|
||||
colors,
|
||||
..Default::default()
|
||||
|
@ -648,7 +648,7 @@ impl Component for Pager {
|
|||
}
|
||||
|
||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
let shortcuts = self.shortcuts(context);
|
||||
match event {
|
||||
UIEvent::ConfigReload { old_settings: _ } => {
|
||||
self.set_colors(crate::conf::value(context, "theme_default"));
|
||||
|
@ -829,7 +829,7 @@ impl Component for Pager {
|
|||
self.dirty = value;
|
||||
}
|
||||
|
||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
fn shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||
let mut ret: ShortcutMaps = Default::default();
|
||||
ret.insert(
|
||||
Shortcuts::PAGER,
|
||||
|
|
|
@ -24,11 +24,13 @@ use super::*;
|
|||
pub struct TextField {
|
||||
inner: UText,
|
||||
autocomplete: Option<(AutoCompleteFn, Box<AutoComplete>)>,
|
||||
id: ComponentId,
|
||||
}
|
||||
|
||||
impl Debug for TextField {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct(stringify!(TextField))
|
||||
.field("id", &self.id)
|
||||
.field("inner", &self.inner)
|
||||
.field("has AutoComplete", &self.autocomplete.is_some())
|
||||
.finish()
|
||||
|
@ -40,6 +42,7 @@ impl Default for TextField {
|
|||
Self {
|
||||
inner: UText::new(String::with_capacity(256)),
|
||||
autocomplete: None,
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +52,7 @@ impl TextField {
|
|||
Self {
|
||||
inner,
|
||||
autocomplete,
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,15 +308,20 @@ impl Component for TextField {
|
|||
self.set_dirty(true);
|
||||
true
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, _value: bool) {}
|
||||
|
||||
fn id(&self) -> ComponentId {
|
||||
ComponentId::nil()
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: ComponentId) {
|
||||
self.id = id;
|
||||
}
|
||||
fn set_id(&mut self, _id: ComponentId) {}
|
||||
}
|
||||
|
||||
impl fmt::Display for TextField {
|
||||
|
|
|
@ -35,7 +35,7 @@ type Cursor = usize;
|
|||
|
||||
pub enum Field {
|
||||
Text(TextField),
|
||||
Choice(Vec<Cow<'static, str>>, Cursor),
|
||||
Choice(Vec<Cow<'static, str>>, Cursor, ComponentId),
|
||||
}
|
||||
|
||||
impl Debug for Field {
|
||||
|
@ -57,7 +57,7 @@ impl Field {
|
|||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Text(ref s) => s.as_str(),
|
||||
Self::Choice(ref v, cursor) => {
|
||||
Self::Choice(ref v, cursor, _) => {
|
||||
if v.is_empty() {
|
||||
""
|
||||
} else {
|
||||
|
@ -70,7 +70,7 @@ impl Field {
|
|||
pub fn cursor(&self) -> usize {
|
||||
match self {
|
||||
Self::Text(ref s) => s.cursor(),
|
||||
Self::Choice(_, ref cursor) => *cursor,
|
||||
Self::Choice(_, ref cursor, _) => *cursor,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,14 +81,14 @@ impl Field {
|
|||
pub fn into_string(self) -> String {
|
||||
match self {
|
||||
Self::Text(s) => s.into_string(),
|
||||
Self::Choice(mut v, cursor) => v.remove(cursor).to_string(),
|
||||
Self::Choice(mut v, cursor, _) => v.remove(cursor).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
match self {
|
||||
Self::Text(s) => s.clear(),
|
||||
Self::Choice(_, _) => {}
|
||||
Self::Choice(_, _, _) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ impl Field {
|
|||
Self::Text(ref mut text_field) => {
|
||||
text_field.draw_cursor(grid, area, secondary_area, context);
|
||||
}
|
||||
Self::Choice(_, _cursor) => {}
|
||||
Self::Choice(_, _cursor, _) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ impl Component for Field {
|
|||
Self::Text(ref mut text_field) => {
|
||||
text_field.draw(grid, area, context);
|
||||
}
|
||||
Self::Choice(_, _) => {
|
||||
Self::Choice(_, _, _) => {
|
||||
write_string_to_grid(
|
||||
str,
|
||||
grid,
|
||||
|
@ -139,7 +139,7 @@ impl Component for Field {
|
|||
Self::Text(_) => {
|
||||
return false;
|
||||
}
|
||||
Self::Choice(ref vec, ref mut cursor) => {
|
||||
Self::Choice(ref vec, ref mut cursor, _) => {
|
||||
*cursor = if *cursor == vec.len().saturating_sub(1) {
|
||||
0
|
||||
} else {
|
||||
|
@ -151,7 +151,7 @@ impl Component for Field {
|
|||
Self::Text(_) => {
|
||||
return false;
|
||||
}
|
||||
Self::Choice(_, ref mut cursor) => {
|
||||
Self::Choice(_, ref mut cursor, _) => {
|
||||
if *cursor == 0 {
|
||||
return false;
|
||||
} else {
|
||||
|
@ -174,10 +174,18 @@ impl Component for Field {
|
|||
fn set_dirty(&mut self, _value: bool) {}
|
||||
|
||||
fn id(&self) -> ComponentId {
|
||||
ComponentId::nil()
|
||||
match self {
|
||||
Self::Text(i) => i.id(),
|
||||
Self::Choice(_, _, i) => *i,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_id(&mut self, _id: ComponentId) {}
|
||||
fn set_id(&mut self, id: ComponentId) {
|
||||
match self {
|
||||
Self::Text(ref mut i) => i.set_id(id),
|
||||
Self::Choice(_, _, i) => *i = id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Field {
|
||||
|
@ -187,7 +195,7 @@ impl fmt::Display for Field {
|
|||
"{}",
|
||||
match self {
|
||||
Self::Text(ref s) => s.as_str(),
|
||||
Self::Choice(ref v, ref cursor) => v[*cursor].as_ref(),
|
||||
Self::Choice(ref v, ref cursor, _) => v[*cursor].as_ref(),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -231,7 +239,7 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> FormWidget<T>
|
|||
buttons: ButtonWidget::new(action),
|
||||
focus: FormFocus::Fields,
|
||||
hide_buttons: false,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
dirty: true,
|
||||
..Default::default()
|
||||
}
|
||||
|
@ -265,7 +273,8 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> FormWidget<T>
|
|||
pub fn push_choices(&mut self, value: (Cow<'static, str>, Vec<Cow<'static, str>>)) {
|
||||
self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len());
|
||||
self.layout.push(value.0.clone());
|
||||
self.fields.insert(value.0, Field::Choice(value.1, 0));
|
||||
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());
|
||||
|
@ -568,7 +577,7 @@ where
|
|||
cursor: 0,
|
||||
focus: false,
|
||||
dirty: true,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -814,7 +823,7 @@ impl AutoComplete {
|
|||
content: CellBuffer::default(),
|
||||
cursor: 0,
|
||||
dirty: true,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
};
|
||||
ret.set_suggestions(entries);
|
||||
Box::new(ret)
|
||||
|
@ -1145,7 +1154,7 @@ impl ProgressSpinner {
|
|||
theme_attr,
|
||||
dirty: true,
|
||||
active: false,
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
31
src/jobs.rs
31
src/jobs.rs
|
@ -63,9 +63,7 @@ fn find_task<T>(local: &Worker<T>, global: &Injector<T>, stealers: &[Stealer<T>]
|
|||
|
||||
macro_rules! uuid_hash_type {
|
||||
($n:ident) => {
|
||||
#[derive(
|
||||
PartialEq, Hash, Eq, Copy, Clone, Ord, PartialOrd, Serialize, Deserialize, Default,
|
||||
)]
|
||||
#[derive(PartialEq, Hash, Eq, Copy, Clone, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct $n(Uuid);
|
||||
|
||||
impl core::fmt::Debug for $n {
|
||||
|
@ -80,17 +78,24 @@ macro_rules! uuid_hash_type {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for $n {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl $n {
|
||||
pub fn new() -> Self {
|
||||
$n(Uuid::new_v4())
|
||||
Self(Uuid::new_v4())
|
||||
}
|
||||
pub fn null() -> Self {
|
||||
$n(Uuid::nil())
|
||||
Self(Uuid::nil())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
uuid_hash_type!(JobId);
|
||||
uuid_hash_type!(TimerId);
|
||||
|
||||
/// A spawned future and its current state.
|
||||
pub struct MeliTask {
|
||||
|
@ -105,7 +110,7 @@ pub struct JobExecutor {
|
|||
workers: Vec<Stealer<MeliTask>>,
|
||||
sender: Sender<ThreadEvent>,
|
||||
parkers: Vec<Unparker>,
|
||||
timers: Arc<Mutex<HashMap<Uuid, TimerPrivate>>>,
|
||||
timers: Arc<Mutex<HashMap<TimerId, TimerPrivate>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -121,12 +126,12 @@ struct TimerPrivate {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Timer {
|
||||
id: Uuid,
|
||||
id: TimerId,
|
||||
job_executor: Arc<JobExecutor>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
pub fn id(&self) -> Uuid {
|
||||
pub fn id(&self) -> TimerId {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
@ -260,7 +265,6 @@ impl JobExecutor {
|
|||
}
|
||||
|
||||
pub fn create_timer(self: Arc<JobExecutor>, interval: Duration, value: Duration) -> Timer {
|
||||
let id = Uuid::new_v4();
|
||||
let timer = TimerPrivate {
|
||||
interval,
|
||||
cancel: Arc::new(Mutex::new(false)),
|
||||
|
@ -268,6 +272,7 @@ impl JobExecutor {
|
|||
active: true,
|
||||
handle: None,
|
||||
};
|
||||
let id = TimerId::default();
|
||||
self.timers.lock().unwrap().insert(id, timer);
|
||||
self.arm_timer(id, value);
|
||||
Timer {
|
||||
|
@ -276,7 +281,7 @@ impl JobExecutor {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn rearm(&self, timer_id: Uuid) {
|
||||
pub fn rearm(&self, timer_id: TimerId) {
|
||||
let mut timers_lck = self.timers.lock().unwrap();
|
||||
if let Some(timer) = timers_lck.get_mut(&timer_id) {
|
||||
let value = timer.value;
|
||||
|
@ -285,7 +290,7 @@ impl JobExecutor {
|
|||
}
|
||||
}
|
||||
|
||||
fn arm_timer(&self, id: Uuid, value: Duration) {
|
||||
fn arm_timer(&self, id: TimerId, value: Duration) {
|
||||
let job_id = JobId::new();
|
||||
let sender = self.sender.clone();
|
||||
let injector = self.global_queue.clone();
|
||||
|
@ -337,7 +342,7 @@ impl JobExecutor {
|
|||
}
|
||||
}
|
||||
|
||||
fn disable_timer(&self, id: Uuid) {
|
||||
fn disable_timer(&self, id: TimerId) {
|
||||
let mut timers_lck = self.timers.lock().unwrap();
|
||||
if let Some(timer) = timers_lck.get_mut(&id) {
|
||||
timer.active = false;
|
||||
|
@ -345,7 +350,7 @@ impl JobExecutor {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_interval(&self, id: Uuid, new_val: Duration) {
|
||||
fn set_interval(&self, id: TimerId, new_val: Duration) {
|
||||
let mut timers_lck = self.timers.lock().unwrap();
|
||||
if let Some(timer) = timers_lck.get_mut(&id) {
|
||||
timer.interval = new_val;
|
||||
|
|
228
src/types.rs
228
src/types.rs
|
@ -19,27 +19,27 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! UI types used throughout meli.
|
||||
*
|
||||
* The `segment_tree` module performs maximum range queries. This is used in
|
||||
* getting the maximum element of a column within a specific range in e-mail
|
||||
* lists. That way a very large value that is not the in the currently
|
||||
* displayed page does not cause the column to be rendered bigger
|
||||
* than it has to.
|
||||
*
|
||||
* `UIMode` describes the application's... mode. Same as in the modal editor
|
||||
* `vi`.
|
||||
*
|
||||
* `UIEvent` is the type passed around `Component`s when something happens.
|
||||
*/
|
||||
extern crate serde;
|
||||
//! UI types used throughout meli.
|
||||
//!
|
||||
//! The [`segment_tree`] module performs maximum range queries.
|
||||
//! This is used in getting the maximum element of a column within a specific
|
||||
//! range in e-mail lists.
|
||||
//! That way a very large value that is not the in the currently displayed page
|
||||
//! does not cause the column to be rendered bigger than it has to.
|
||||
//!
|
||||
//! [`UIMode`] describes the application's... mode. Same as in the modal editor
|
||||
//! `vi`.
|
||||
//!
|
||||
//! [`UIEvent`] is the type passed around
|
||||
//! [`Component`](crate::components::Component)'s when something happens.
|
||||
|
||||
#[macro_use]
|
||||
mod helpers;
|
||||
|
||||
use std::{borrow::Cow, fmt, sync::Arc};
|
||||
|
||||
use melib::{
|
||||
backends::{AccountHash, BackendEvent, MailboxHash},
|
||||
uuid::Uuid,
|
||||
EnvelopeHash, RefreshEvent, ThreadHash,
|
||||
};
|
||||
use nix::unistd::Pid;
|
||||
|
@ -47,7 +47,7 @@ use nix::unistd::Pid;
|
|||
pub use self::helpers::*;
|
||||
use super::{
|
||||
command::Action,
|
||||
jobs::{JobExecutor, JobId},
|
||||
jobs::{JobExecutor, JobId, TimerId},
|
||||
terminal::*,
|
||||
};
|
||||
use crate::components::{Component, ComponentId, ScrollUpdate};
|
||||
|
@ -66,7 +66,7 @@ pub enum StatusEvent {
|
|||
ScrollUpdate(ScrollUpdate),
|
||||
}
|
||||
|
||||
/// `ThreadEvent` encapsulates all of the possible values we need to transfer
|
||||
/// [`ThreadEvent`] encapsulates all of the possible values we need to transfer
|
||||
/// between our threads to the main process.
|
||||
#[derive(Debug)]
|
||||
pub enum ThreadEvent {
|
||||
|
@ -140,7 +140,7 @@ pub enum UIEvent {
|
|||
MailboxDelete((AccountHash, MailboxHash)),
|
||||
MailboxCreate((AccountHash, MailboxHash)),
|
||||
AccountStatusChange(AccountHash, Option<Cow<'static, str>>),
|
||||
ComponentKill(Uuid),
|
||||
ComponentKill(ComponentId),
|
||||
BackendEvent(AccountHash, BackendEvent),
|
||||
StartupCheck(MailboxHash),
|
||||
RefreshEvent(Box<RefreshEvent>),
|
||||
|
@ -152,7 +152,7 @@ pub enum UIEvent {
|
|||
FinishedUIDialog(ComponentId, UIMessage),
|
||||
Callback(CallbackFn),
|
||||
GlobalUIDialog(Box<dyn Component>),
|
||||
Timer(Uuid),
|
||||
Timer(TimerId),
|
||||
ConfigReload {
|
||||
old_settings: Box<crate::conf::Settings>,
|
||||
},
|
||||
|
@ -200,10 +200,10 @@ impl fmt::Display for UIMode {
|
|||
}
|
||||
|
||||
pub mod segment_tree {
|
||||
/*! Simple segment tree implementation for maximum in range queries. This
|
||||
* is useful if given an array of numbers you want to get the
|
||||
* maximum value inside an interval quickly.
|
||||
*/
|
||||
//! Simple segment tree implementation for maximum in range queries. This is
|
||||
//! useful if given an array of numbers you want to get the maximum
|
||||
//! value inside an interval quickly.
|
||||
|
||||
use std::{convert::TryFrom, iter::FromIterator};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
@ -298,20 +298,25 @@ pub mod segment_tree {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_segment_tree() {
|
||||
let array: SmallVec<[u8; 1024]> = [9, 1, 17, 2, 3, 23, 4, 5, 6, 37]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<SmallVec<[u8; 1024]>>();
|
||||
let mut segment_tree = SegmentTree::from(array);
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
assert_eq!(segment_tree.get_max(0, 5), 23);
|
||||
assert_eq!(segment_tree.get_max(6, 9), 37);
|
||||
#[test]
|
||||
fn test_segment_tree() {
|
||||
let array: SmallVec<[u8; 1024]> = [9, 1, 17, 2, 3, 23, 4, 5, 6, 37]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<SmallVec<[u8; 1024]>>();
|
||||
let mut segment_tree = SegmentTree::from(array);
|
||||
|
||||
segment_tree.update(2_usize, 24_u8);
|
||||
assert_eq!(segment_tree.get_max(0, 5), 23);
|
||||
assert_eq!(segment_tree.get_max(6, 9), 37);
|
||||
|
||||
assert_eq!(segment_tree.get_max(0, 5), 24);
|
||||
segment_tree.update(2_usize, 24_u8);
|
||||
|
||||
assert_eq!(segment_tree.get_max(0, 5), 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,85 +359,11 @@ impl RateLimit {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn id(&self) -> Uuid {
|
||||
pub fn id(&self) -> TimerId {
|
||||
self.timer.id()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rate_limit() {
|
||||
/*
|
||||
let (sender, receiver) =
|
||||
crossbeam::channel::bounded(4096 * ::std::mem::size_of::<ThreadEvent>());
|
||||
use std::sync::Arc;
|
||||
let job_executor = Arc::new(JobExecutor::new(sender));
|
||||
/* Accept at most one request per 3 milliseconds */
|
||||
let mut rt = RateLimit::new(1, 3, job_executor.clone());
|
||||
std::thread::sleep(std::time::Duration::from_millis(2000));
|
||||
/* assert that only one request per 3 milliseconds is accepted */
|
||||
for _ in 0..5 {
|
||||
assert!(rt.tick());
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
assert!(!rt.tick());
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
assert!(!rt.tick());
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
/* How many times was the signal handler called? We've slept for at least 3
|
||||
* milliseconds, so it should have been called once */
|
||||
let mut ctr = 0;
|
||||
while receiver.try_recv().is_ok() {
|
||||
ctr += 1;
|
||||
println!("got {}", ctr);
|
||||
}
|
||||
println!("ctr = {} {}", ctr, ctr == 1);
|
||||
assert_eq!(ctr, 1);
|
||||
}
|
||||
/* next, test at most 100 requests per second */
|
||||
let mut rt = RateLimit::new(100, 1000, job_executor.clone());
|
||||
for _ in 0..5 {
|
||||
let mut ctr = 0;
|
||||
for _ in 0..500 {
|
||||
if rt.tick() {
|
||||
ctr += 1;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(2));
|
||||
}
|
||||
/* around 100 requests should succeed. might be 99 if in first loop, since
|
||||
* RateLimit::new() has a delay */
|
||||
assert!(ctr > 97 && ctr < 103);
|
||||
/* alarm should expire in 1 second */
|
||||
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||
/* How many times was the signal handler called? */
|
||||
ctr = 0;
|
||||
while receiver.try_recv().is_ok() {
|
||||
ctr += 1;
|
||||
}
|
||||
assert_eq!(ctr, 1);
|
||||
}
|
||||
/* next, test at most 500 requests per second */
|
||||
let mut rt = RateLimit::new(500, 1000, job_executor.clone());
|
||||
for _ in 0..5 {
|
||||
let mut ctr = 0;
|
||||
for _ in 0..500 {
|
||||
if rt.tick() {
|
||||
ctr += 1;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(2));
|
||||
}
|
||||
/* all requests should succeed. */
|
||||
assert!(ctr < 503 && ctr > 497);
|
||||
/* alarm should expire in 1 second */
|
||||
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||
/* How many times was the signal handler called? */
|
||||
ctr = 0;
|
||||
while receiver.try_recv().is_ok() {
|
||||
ctr += 1;
|
||||
}
|
||||
assert_eq!(ctr, 1);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ContactEvent {
|
||||
CreateContacts(Vec<melib::Card>),
|
||||
|
@ -444,3 +375,82 @@ pub enum ComposeEvent {
|
|||
}
|
||||
|
||||
pub type UIMessage = Box<dyn 'static + std::any::Any + Send + Sync>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_rate_limit() {
|
||||
/*
|
||||
let (sender, receiver) =
|
||||
crossbeam::channel::bounded(4096 * ::std::mem::size_of::<ThreadEvent>());
|
||||
use std::sync::Arc;
|
||||
let job_executor = Arc::new(JobExecutor::new(sender));
|
||||
/* Accept at most one request per 3 milliseconds */
|
||||
let mut rt = RateLimit::new(1, 3, job_executor.clone());
|
||||
std::thread::sleep(std::time::Duration::from_millis(2000));
|
||||
/* assert that only one request per 3 milliseconds is accepted */
|
||||
for _ in 0..5 {
|
||||
assert!(rt.tick());
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
assert!(!rt.tick());
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
assert!(!rt.tick());
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
/* How many times was the signal handler called? We've slept for at least 3
|
||||
* milliseconds, so it should have been called once */
|
||||
let mut ctr = 0;
|
||||
while receiver.try_recv().is_ok() {
|
||||
ctr += 1;
|
||||
println!("got {}", ctr);
|
||||
}
|
||||
println!("ctr = {} {}", ctr, ctr == 1);
|
||||
assert_eq!(ctr, 1);
|
||||
}
|
||||
/* next, test at most 100 requests per second */
|
||||
let mut rt = RateLimit::new(100, 1000, job_executor.clone());
|
||||
for _ in 0..5 {
|
||||
let mut ctr = 0;
|
||||
for _ in 0..500 {
|
||||
if rt.tick() {
|
||||
ctr += 1;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(2));
|
||||
}
|
||||
/* around 100 requests should succeed. might be 99 if in first loop, since
|
||||
* RateLimit::new() has a delay */
|
||||
assert!(ctr > 97 && ctr < 103);
|
||||
/* alarm should expire in 1 second */
|
||||
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||
/* How many times was the signal handler called? */
|
||||
ctr = 0;
|
||||
while receiver.try_recv().is_ok() {
|
||||
ctr += 1;
|
||||
}
|
||||
assert_eq!(ctr, 1);
|
||||
}
|
||||
/* next, test at most 500 requests per second */
|
||||
let mut rt = RateLimit::new(500, 1000, job_executor.clone());
|
||||
for _ in 0..5 {
|
||||
let mut ctr = 0;
|
||||
for _ in 0..500 {
|
||||
if rt.tick() {
|
||||
ctr += 1;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(2));
|
||||
}
|
||||
/* all requests should succeed. */
|
||||
assert!(ctr < 503 && ctr > 497);
|
||||
/* alarm should expire in 1 second */
|
||||
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||
/* How many times was the signal handler called? */
|
||||
ctr = 0;
|
||||
while receiver.try_recv().is_ok() {
|
||||
ctr += 1;
|
||||
}
|
||||
assert_eq!(ctr, 1);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ pub fn create_temp_file(
|
|||
dir.push(filename)
|
||||
} else {
|
||||
let u = Uuid::new_v4();
|
||||
dir.push(u.as_hyphenated().to_string());
|
||||
dir.push(u.as_simple().to_string());
|
||||
}
|
||||
&mut dir
|
||||
});
|
||||
|
|
|
@ -101,7 +101,7 @@ impl EmbedContainer {
|
|||
embed_area: ((0, 0), (80, 20)),
|
||||
dirty: true,
|
||||
log_file: File::open(".embed.out").unwrap(),
|
||||
id: ComponentId::new_v4(),
|
||||
id: ComponentId::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue