forked from meli/meli
1
Fork 0

Add {Timer,Component}Id wrapper types over Uuid

master
Manos Pitsidianakis 2023-06-13 17:45:48 +03:00
parent 4da5366959
commit 96537e48c5
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
31 changed files with 424 additions and 309 deletions

View File

@ -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,
}

View File

@ -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()
}
}

View File

@ -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
}
}
}

View File

@ -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()

View File

@ -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()
};

View File

@ -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()
}

View File

@ -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),
}
}

View File

@ -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),
)));
}

View File

@ -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()
};

View File

@ -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()
};

View File

@ -117,7 +117,7 @@ impl OfflineListing {
_selection: HashMap::default(),
messages: vec![],
dirty: true,
id: ComponentId::new_v4(),
id: ComponentId::default(),
})
}
}

View File

@ -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()
};

View File

@ -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()

View File

@ -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,

View File

@ -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();

View File

@ -79,7 +79,7 @@ impl EnvelopeView {
mail,
_account_hash,
cmd_buf: String::with_capacity(4),
id: ComponentId::new_v4(),
id: ComponentId::default(),
}
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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())
}
}

View File

@ -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<()> {

View File

@ -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;}"#;

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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,

View File

@ -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 {

View File

@ -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(),
}
}

View File

@ -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;

View File

@ -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);
}
*/
}
}

View File

@ -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
});

View File

@ -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(),
})
}
}