From 106744c7ca4e5444d993a04c23bc04d93e2bfa6a Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Wed, 10 Apr 2019 22:01:02 +0300 Subject: [PATCH] ui: remove Entity --- src/bin.rs | 22 ++- ui/src/components.rs | 69 +-------- ui/src/components/contacts.rs | 15 +- ui/src/components/contacts/contact_list.rs | 31 ++-- ui/src/components/indexer.rs | 9 ++ ui/src/components/indexer/index.rs | 8 + ui/src/components/mail.rs | 9 ++ ui/src/components/mail/accounts.rs | 9 ++ ui/src/components/mail/compose.rs | 9 ++ ui/src/components/mail/listing.rs | 15 ++ ui/src/components/mail/listing/compact.rs | 18 +++ ui/src/components/mail/listing/plain.rs | 9 ++ ui/src/components/mail/listing/thread.rs | 9 ++ ui/src/components/mail/view.rs | 9 ++ ui/src/components/mail/view/envelope.rs | 9 ++ ui/src/components/mail/view/html.rs | 17 ++- ui/src/components/mail/view/thread.rs | 8 + ui/src/components/notifications.rs | 9 ++ ui/src/components/utilities.rs | 168 +++++++++++++-------- ui/src/components/utilities/widgets.rs | 31 ++++ ui/src/state.rs | 38 ++--- ui/src/types.rs | 2 +- 22 files changed, 343 insertions(+), 180 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index f757a4054..f7bcaa0e1 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -62,32 +62,30 @@ fn main() { let worker_receiver = state.worker_receiver(); /* Register some reasonably useful interfaces */ - let menu = Entity::from(Box::new(AccountMenu::new(&state.context.accounts))); + let menu = Box::new(AccountMenu::new(&state.context.accounts)); let listing = listing::Listing::from(IndexStyle::Compact); - let b = Entity::from(Box::new(listing)); - let tabs = Box::new(Tabbed::new(vec![ + let b = Box::new(listing); + let window = Box::new(Tabbed::new(vec![ Box::new(VSplit::new(menu, b, 90, false)), Box::new(AccountsPanel::new(&state.context)), Box::new(ContactList::default()), ])); - let window = Entity::from(tabs); - let status_bar = Entity::from(Box::new(StatusBar::new(window))); - state.register_entity(status_bar); + let status_bar = Box::new(StatusBar::new(window)); + state.register_component(status_bar); - let xdg_notifications = - Entity::from(Box::new(ui::components::notifications::XDGNotifications {})); - state.register_entity(xdg_notifications); - state.register_entity(Entity::from(Box::new( + let xdg_notifications = Box::new(ui::components::notifications::XDGNotifications {}); + state.register_component(xdg_notifications); + state.register_component(Box::new( ui::components::notifications::NotificationFilter {}, - ))); + )); /* Keep track of the input mode. See ui::UIMode for details */ 'main: loop { state.render(); 'inner: loop { - /* Check if any entities have sent reply events to State. */ + /* Check if any components have sent reply events to State. */ let events: Vec = state.context.replies(); for e in events { state.rcv_event(e); diff --git a/ui/src/components.rs b/ui/src/components.rs index 3e90ab94d..f5b843d68 100644 --- a/ui/src/components.rs +++ b/ui/src/components.rs @@ -76,69 +76,7 @@ const _DOUBLE_DOWN_AND_LEFT: char = '╗'; const _DOUBLE_UP_AND_LEFT: char = '╝'; const _DOUBLE_UP_AND_RIGHT: char = '╚'; -type EntityId = Uuid; - -/// `Entity` is a container for Components. -#[derive(Debug)] -pub struct Entity { - id: EntityId, - pub component: Box, // more than one? -} - -impl From> for Entity { - fn from(mut kind: Box) -> Entity { - let id = Uuid::new_v4(); - kind.set_id(id); - Entity { - id, - component: kind, - } - } -} - -impl From> for Entity -where - C: Component, -{ - fn from(mut kind: Box) -> Entity { - let id = Uuid::new_v4(); - kind.set_id(id); - Entity { - id, - component: kind, - } - } -} - -impl Display for Entity { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(&self.component, f) - } -} - -impl DerefMut for Entity { - fn deref_mut(&mut self) -> &mut Box { - &mut self.component - } -} - -impl Deref for Entity { - type Target = Box; - - fn deref(&self) -> &Box { - &self.component - } -} - -impl Entity { - pub fn id(&self) -> &EntityId { - &self.id - } - /// Pass events to child component. - pub fn rcv_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { - self.component.process_event(event, context) - } -} +type ComponentId = Uuid; pub type ShortcutMap = FnvHashMap<&'static str, Key>; @@ -155,8 +93,9 @@ pub trait Component: Display + Debug + Send { true } fn set_dirty(&mut self); - fn kill(&mut self, _id: EntityId) {} - fn set_id(&mut self, _id: EntityId) {} + fn kill(&mut self, _id: ComponentId) {} + fn set_id(&mut self, _id: ComponentId) {} + fn id(&self) -> ComponentId; fn get_shortcuts(&self, _context: &Context) -> ShortcutMap { Default::default() diff --git a/ui/src/components/contacts.rs b/ui/src/components/contacts.rs index dddbb0dac..592db0b1e 100644 --- a/ui/src/components/contacts.rs +++ b/ui/src/components/contacts.rs @@ -36,7 +36,7 @@ enum ViewMode { #[derive(Debug)] pub struct ContactManager { - id: Uuid, + id: ComponentId, pub card: Card, mode: ViewMode, form: FormWidget, @@ -169,13 +169,13 @@ impl Component for ContactManager { }); context.replies.push_back(UIEvent { id: 0, - event_type: UIEventType::EntityKill(self.id), + event_type: UIEventType::ComponentKill(self.id), }); } Some(false) => { context.replies.push_back(UIEvent { id: 0, - event_type: UIEventType::EntityKill(self.id), + event_type: UIEventType::ComponentKill(self.id), }); } } @@ -186,7 +186,7 @@ impl Component for ContactManager { UIEventType::Input(Key::Char('\n')) => { context.replies.push_back(UIEvent { id: 0, - event_type: UIEventType::EntityKill(self.id), + event_type: UIEventType::ComponentKill(self.id), }); return true; }, @@ -206,7 +206,10 @@ impl Component for ContactManager { self.form.set_dirty(); } - fn set_id(&mut self, uuid: Uuid) { - self.id = uuid; + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; } } diff --git a/ui/src/components/contacts/contact_list.rs b/ui/src/components/contacts/contact_list.rs index 3554b5b53..e124a2a58 100644 --- a/ui/src/components/contacts/contact_list.rs +++ b/ui/src/components/contacts/contact_list.rs @@ -23,7 +23,8 @@ pub struct ContactList { mode: ViewMode, dirty: bool, - view: Option, + view: Option>, + id: ComponentId, } impl Default for ContactList { @@ -51,6 +52,7 @@ impl ContactList { content, dirty: true, view: None, + id: ComponentId::default(), } } @@ -234,10 +236,10 @@ impl Component for ContactList { UIEventType::Input(ref key) if *key == shortcuts["create_contact"] => { let mut manager = ContactManager::default(); manager.account_pos = self.account_pos; - let entity = Entity::from(Box::new(manager)); + let component = Box::new(manager); - self.mode = ViewMode::View(*entity.id()); - self.view = Some(entity); + self.mode = ViewMode::View(component.id()); + self.view = Some(component); return true; } @@ -249,10 +251,10 @@ impl Component for ContactList { let mut manager = ContactManager::default(); manager.card = card; manager.account_pos = self.account_pos; - let entity = Entity::from(Box::new(manager)); + let component = Box::new(manager); - self.mode = ViewMode::View(*entity.id()); - self.view = Some(entity); + self.mode = ViewMode::View(component.id()); + self.view = Some(component); return true; } @@ -261,9 +263,9 @@ impl Component for ContactList { let mut manager = ContactManager::default(); manager.card = card; manager.account_pos = self.account_pos; - let entity = Entity::from(Box::new(manager)); - self.mode = ViewMode::View(*entity.id()); - self.view = Some(entity); + let component = Box::new(manager); + self.mode = ViewMode::View(component.id()); + self.view = Some(component); return true; } @@ -277,7 +279,7 @@ impl Component for ContactList { self.new_cursor_pos += 1; return true; } - UIEventType::EntityKill(ref kill_id) if self.mode == ViewMode::View(*kill_id) => { + UIEventType::ComponentKill(ref kill_id) if self.mode == ViewMode::View(*kill_id) => { self.mode = ViewMode::List; self.view.take(); self.set_dirty(); @@ -315,4 +317,11 @@ impl Component for ContactList { map } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } diff --git a/ui/src/components/indexer.rs b/ui/src/components/indexer.rs index 2d1640eab..89a0996d4 100644 --- a/ui/src/components/indexer.rs +++ b/ui/src/components/indexer.rs @@ -38,6 +38,7 @@ pub struct Indexer { entries: Vec, dirty: bool, cursor: Vec, + id: ComponentId, } impl fmt::Display for Indexer { @@ -53,6 +54,7 @@ impl Default for Indexer { entries: Vec::with_capacity(8), dirty: true, cursor: Vec::with_capacity(8), + id: ComponentId::default(), } } } @@ -125,4 +127,11 @@ impl Component for Indexer { fn set_dirty(&mut self) { self.dirty = true; } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } diff --git a/ui/src/components/indexer/index.rs b/ui/src/components/indexer/index.rs index 4794baca5..17272d252 100644 --- a/ui/src/components/indexer/index.rs +++ b/ui/src/components/indexer/index.rs @@ -34,6 +34,7 @@ pub struct Index { state: IndexState, content: Box, + id: ComponentId, } impl Index { @@ -169,6 +170,13 @@ impl Component for Index { fn set_dirty(&mut self) { self.dirty = true; } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } impl fmt::Display for Index { diff --git a/ui/src/components/mail.rs b/ui/src/components/mail.rs index 503c669b7..0eda59424 100644 --- a/ui/src/components/mail.rs +++ b/ui/src/components/mail.rs @@ -48,6 +48,7 @@ pub struct AccountMenu { dirty: bool, visible: bool, cursor: Option<(usize, usize)>, + id: ComponentId, } impl fmt::Display for AccountMenu { @@ -72,6 +73,7 @@ impl AccountMenu { visible: true, dirty: true, cursor: None, + id: ComponentId::default(), } } /* @@ -301,4 +303,11 @@ impl Component for AccountMenu { .cloned() .collect() } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } diff --git a/ui/src/components/mail/accounts.rs b/ui/src/components/mail/accounts.rs index 42ee3f8d1..afd49ee45 100644 --- a/ui/src/components/mail/accounts.rs +++ b/ui/src/components/mail/accounts.rs @@ -27,6 +27,7 @@ pub struct AccountsPanel { cursor: usize, content: CellBuffer, dirty: bool, + id: ComponentId, } impl fmt::Display for AccountsPanel { @@ -84,6 +85,13 @@ impl Component for AccountsPanel { fn set_dirty(&mut self) { self.dirty = true; } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } impl AccountsPanel { @@ -94,6 +102,7 @@ impl AccountsPanel { cursor: 0, content, dirty: true, + id: ComponentId::default(), } } fn initialize(&mut self, context: &Context) { diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index d38f1355c..d449147c1 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -45,6 +45,7 @@ pub struct Composer { mode: ViewMode, dirty: bool, initialized: bool, + id: ComponentId, } impl Default for Composer { @@ -62,6 +63,7 @@ impl Default for Composer { mode: ViewMode::Edit, dirty: true, initialized: false, + id: ComponentId::default(), } } } @@ -688,6 +690,13 @@ impl Component for Composer { map } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } fn get_display_name(context: &Context, idx: usize) -> String { diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index 9e2371d75..f108d9f80 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -163,6 +163,21 @@ impl Component for Listing { Listing::Threaded(l) => l.get_shortcuts(context), } } + + fn id(&self) -> ComponentId { + match self { + Listing::Compact(l) => l.id(), + Listing::Plain(l) => l.id(), + Listing::Threaded(l) => l.id(), + } + } + fn set_id(&mut self, id: ComponentId) { + match self { + Listing::Compact(l) => l.set_id(id), + Listing::Plain(l) => l.set_id(id), + Listing::Threaded(l) => l.set_id(id), + } + } } impl From for Listing { diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index e885cf092..a22942d69 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -43,6 +43,7 @@ struct MailboxView { view: ThreadView, movement: Option, + id: ComponentId, } impl fmt::Display for MailboxView { @@ -87,6 +88,7 @@ impl MailboxView { view: ThreadView::default(), movement: None, + id: ComponentId::default(), } } /// Fill the `self.content` `CellBuffer` with the contents of the account folder the user has @@ -587,6 +589,13 @@ impl Component for MailboxView { map } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } /// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a @@ -597,6 +606,7 @@ pub struct CompactListing { cursor: usize, dirty: bool, populated: bool, + id: ComponentId, } impl ListingTrait for CompactListing { @@ -627,6 +637,7 @@ impl CompactListing { cursor: 0, dirty: true, populated: false, + id: ComponentId::default(), } } } @@ -762,4 +773,11 @@ impl Component for CompactListing { map } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } diff --git a/ui/src/components/mail/listing/plain.rs b/ui/src/components/mail/listing/plain.rs index 01e1f6209..30fae2bb9 100644 --- a/ui/src/components/mail/listing/plain.rs +++ b/ui/src/components/mail/listing/plain.rs @@ -41,6 +41,7 @@ pub struct PlainListing { /// If `self.view` exists or not. unfocused: bool, view: Option, + id: ComponentId, } impl ListingTrait for PlainListing { @@ -89,6 +90,7 @@ impl PlainListing { dirty: true, unfocused: false, view: None, + id: ComponentId::default(), } } /// Fill the `self.content` `CellBuffer` with the contents of the account folder the user has @@ -561,4 +563,11 @@ impl Component for PlainListing { }; self.dirty = true; } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } diff --git a/ui/src/components/mail/listing/thread.rs b/ui/src/components/mail/listing/thread.rs index a6ccfccc4..95e311ca6 100644 --- a/ui/src/components/mail/listing/thread.rs +++ b/ui/src/components/mail/listing/thread.rs @@ -44,6 +44,7 @@ pub struct ThreadListing { unfocused: bool, initialised: bool, view: Option, + id: ComponentId, } impl ListingTrait for ThreadListing { @@ -86,6 +87,7 @@ impl ThreadListing { unfocused: false, view: None, initialised: false, + id: ComponentId::default(), } } /// Fill the `self.content` `CellBuffer` with the contents of the account folder the user has @@ -729,4 +731,11 @@ impl Component for ThreadListing { .map(|p| p.get_shortcuts(context)) .unwrap_or_default() } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index 0fb3b77a1..6ee2160ff 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -69,6 +69,7 @@ pub struct MailView { mode: ViewMode, cmd_buf: String, + id: ComponentId, } impl fmt::Display for MailView { @@ -112,6 +113,7 @@ impl MailView { mode: ViewMode::Normal, cmd_buf: String::with_capacity(4), + id: ComponentId::default(), } } @@ -723,4 +725,11 @@ impl Component for MailView { _ => {} } } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } diff --git a/ui/src/components/mail/view/envelope.rs b/ui/src/components/mail/view/envelope.rs index 51299f122..13217a5f6 100644 --- a/ui/src/components/mail/view/envelope.rs +++ b/ui/src/components/mail/view/envelope.rs @@ -55,6 +55,7 @@ pub struct EnvelopeView { account_pos: usize, cmd_buf: String, + id: ComponentId, } impl fmt::Display for EnvelopeView { @@ -79,6 +80,7 @@ impl EnvelopeView { wrapper, account_pos, cmd_buf: String::with_capacity(4), + id: ComponentId::default(), } } @@ -545,4 +547,11 @@ impl Component for EnvelopeView { fn set_dirty(&mut self) { self.dirty = true; } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } diff --git a/ui/src/components/mail/view/html.rs b/ui/src/components/mail/view/html.rs index b4ca9f55c..c5e5cfab5 100644 --- a/ui/src/components/mail/view/html.rs +++ b/ui/src/components/mail/view/html.rs @@ -27,10 +27,12 @@ use std::process::{Command, Stdio}; pub struct HtmlView { pager: Pager, bytes: Vec, + id: ComponentId, } impl HtmlView { pub fn new(bytes: Vec, context: &mut Context, account_pos: usize) -> Self { + let id = ComponentId::default(); let settings = context.accounts[account_pos].runtime_settings.conf(); if let Some(filter_invocation) = settings.html_filter() { let parts = split_command!(filter_invocation); @@ -57,7 +59,7 @@ impl HtmlView { None, None, ); - HtmlView { pager, bytes } + HtmlView { pager, bytes, id } } else { let mut html_filter = command_obj.unwrap(); html_filter @@ -75,7 +77,7 @@ impl HtmlView { )); let pager = Pager::from_string(display_text, None, None, None); - HtmlView { pager, bytes } + HtmlView { pager, bytes, id } } } else { if let Ok(mut html_filter) = Command::new("w3m") @@ -98,7 +100,7 @@ impl HtmlView { )); let pager = Pager::from_string(display_text, None, None, None); - HtmlView { pager, bytes } + HtmlView { pager, bytes, id } } else { context.replies.push_back(UIEvent { id: 0, @@ -115,7 +117,7 @@ impl HtmlView { None, None, ); - HtmlView { pager, bytes } + HtmlView { pager, bytes, id } } } } @@ -168,4 +170,11 @@ impl Component for HtmlView { fn set_dirty(&mut self) { self.pager.set_dirty(); } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } diff --git a/ui/src/components/mail/view/thread.rs b/ui/src/components/mail/view/thread.rs index 0a625cd92..d5d2986fd 100644 --- a/ui/src/components/mail/view/thread.rs +++ b/ui/src/components/mail/view/thread.rs @@ -51,6 +51,7 @@ pub struct ThreadView { dirty: bool, content: CellBuffer, initiated: bool, + id: ComponentId, } #[derive(Debug)] @@ -985,4 +986,11 @@ impl Component for ThreadView { map } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } diff --git a/ui/src/components/notifications.rs b/ui/src/components/notifications.rs index f7df74faf..55aabbf72 100644 --- a/ui/src/components/notifications.rs +++ b/ui/src/components/notifications.rs @@ -54,6 +54,11 @@ impl Component for XDGNotifications { false } fn set_dirty(&mut self) {} + + fn id(&self) -> ComponentId { + ComponentId::nil() + } + fn set_id(&mut self, _id: ComponentId) {} } fn escape_str(s: &str) -> String { @@ -133,5 +138,9 @@ impl Component for NotificationFilter { } false } + fn id(&self) -> ComponentId { + ComponentId::nil() + } fn set_dirty(&mut self) {} + fn set_id(&mut self, _id: ComponentId) {} } diff --git a/ui/src/components/utilities.rs b/ui/src/components/utilities.rs index 8f713396a..86dc6c546 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -30,10 +30,11 @@ pub use self::widgets::*; /// A horizontally split in half container. #[derive(Debug)] pub struct HSplit { - top: Entity, - bottom: Entity, + top: Box, + bottom: Box, show_divider: bool, ratio: usize, // bottom/whole height * 100 + id: ComponentId, } impl fmt::Display for HSplit { @@ -44,12 +45,18 @@ impl fmt::Display for HSplit { } impl HSplit { - pub fn new(top: Entity, bottom: Entity, ratio: usize, show_divider: bool) -> Self { + pub fn new( + top: Box, + bottom: Box, + ratio: usize, + show_divider: bool, + ) -> Self { HSplit { top, bottom, show_divider, ratio, + id: ComponentId::default(), } } } @@ -62,8 +69,8 @@ impl Component for HSplit { let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); let total_rows = get_y(bottom_right) - get_y(upper_left); - let bottom_entity_height = (self.ratio * total_rows) / 100; - let mid = get_y(upper_left) + total_rows - bottom_entity_height; + let bottom_component_height = (self.ratio * total_rows) / 100; + let mid = get_y(upper_left) + total_rows - bottom_component_height; if self.show_divider { for i in get_x(upper_left)..=get_x(bottom_right) { @@ -74,7 +81,7 @@ impl Component for HSplit { .push_back(((get_x(upper_left), mid), (get_x(bottom_right), mid))); } - self.top.component.draw( + self.top.draw( grid, ( upper_left, @@ -82,23 +89,23 @@ impl Component for HSplit { ), context, ); - self.bottom.component.draw( + self.bottom.draw( grid, ((get_x(upper_left), get_y(upper_left) + mid), bottom_right), context, ); } fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { - self.top.rcv_event(event, context) || self.bottom.rcv_event(event, context) + self.top.process_event(event, context) || self.bottom.process_event(event, context) } fn is_dirty(&self) -> bool { - self.top.component.is_dirty() || self.bottom.component.is_dirty() + self.top.is_dirty() || self.bottom.is_dirty() } fn set_dirty(&mut self) { - self.top.component.set_dirty(); - self.bottom.component.set_dirty(); + self.top.set_dirty(); + self.bottom.set_dirty(); } fn get_shortcuts(&self, context: &Context) -> ShortcutMap { @@ -106,34 +113,48 @@ impl Component for HSplit { top_map.extend(self.bottom.get_shortcuts(context).into_iter()); top_map } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } /// A vertically split in half container. #[derive(Debug)] pub struct VSplit { - left: Entity, - right: Entity, + left: Box, + right: Box, show_divider: bool, prev_visibility: (bool, bool), /// This is the width of the right container to the entire width. ratio: usize, // right/(container width) * 100 + id: ComponentId, } impl fmt::Display for VSplit { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // TODO display focused entity + // TODO display focused component Display::fmt(&self.right, f) } } impl VSplit { - pub fn new(left: Entity, right: Entity, ratio: usize, show_divider: bool) -> Self { + pub fn new( + left: Box, + right: Box, + ratio: usize, + show_divider: bool, + ) -> Self { VSplit { left, right, show_divider, prev_visibility: (true, true), ratio, + id: ComponentId::default(), } } } @@ -151,7 +172,7 @@ impl Component for VSplit { self.set_dirty(); self.prev_visibility = visibility; } - let right_entity_width = match visibility { + let right_component_width = match visibility { (true, true) => (self.ratio * total_cols) / 100, (false, true) => total_cols, (true, false) => 0, @@ -161,7 +182,7 @@ impl Component for VSplit { } }; - let mid = get_x(bottom_right) - right_entity_width; + let mid = get_x(bottom_right) - right_component_width; if get_y(upper_left) > 1 { let c = grid @@ -193,12 +214,12 @@ impl Component for VSplit { .push_back(((mid, get_y(upper_left)), (mid, get_y(bottom_right)))); } - if right_entity_width == total_cols { - self.right.component.draw(grid, area, context); - } else if right_entity_width == 0 { - self.left.component.draw(grid, area, context); + if right_component_width == total_cols { + self.right.draw(grid, area, context); + } else if right_component_width == 0 { + self.left.draw(grid, area, context); } else { - self.left.component.draw( + self.left.draw( grid, ( upper_left, @@ -210,22 +231,21 @@ impl Component for VSplit { context, ); self.right - .component .draw(grid, (set_x(upper_left, mid + 1), bottom_right), context); } } fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { - (self.left.rcv_event(event, context) || self.right.rcv_event(event, context)) + (self.left.process_event(event, context) || self.right.process_event(event, context)) } fn is_dirty(&self) -> bool { - self.left.component.is_dirty() || self.right.component.is_dirty() + self.left.is_dirty() || self.right.is_dirty() } fn set_dirty(&mut self) { - self.left.component.set_dirty(); - self.right.component.set_dirty(); + self.left.set_dirty(); + self.right.set_dirty(); } fn get_shortcuts(&self, context: &Context) -> ShortcutMap { @@ -233,6 +253,13 @@ impl Component for VSplit { right_map.extend(self.left.get_shortcuts(context).into_iter()); right_map } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } #[derive(Debug)] @@ -254,6 +281,7 @@ pub struct Pager { dirty: bool, content: CellBuffer, movement: Option, + id: ComponentId, } impl fmt::Display for Pager { @@ -524,12 +552,19 @@ impl Component for Pager { map } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } /// Status bar. #[derive(Debug)] pub struct StatusBar { - container: Entity, + container: Box, status: String, notifications: VecDeque, ex_buffer: String, @@ -537,6 +572,7 @@ pub struct StatusBar { mode: UIMode, height: usize, dirty: bool, + id: ComponentId, } impl fmt::Display for StatusBar { @@ -547,7 +583,7 @@ impl fmt::Display for StatusBar { } impl StatusBar { - pub fn new(container: Entity) -> Self { + pub fn new(container: Box) -> Self { StatusBar { container, status: String::with_capacity(256), @@ -557,6 +593,7 @@ impl StatusBar { dirty: true, mode: UIMode::Normal, height: 1, + id: ComponentId::default(), } } fn draw_status_bar(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { @@ -615,7 +652,7 @@ impl Component for StatusBar { } let height = self.height; - self.container.component.draw( + self.container.draw( grid, ( upper_left, @@ -649,7 +686,7 @@ impl Component for StatusBar { } } fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { - if self.container.rcv_event(event, context) { + if self.container.process_event(event, context) { return true; } @@ -728,7 +765,7 @@ impl Component for StatusBar { false } fn is_dirty(&self) -> bool { - self.dirty || self.container.component.is_dirty() + self.dirty || self.container.is_dirty() } fn set_dirty(&mut self) { self.dirty = true; @@ -737,40 +774,21 @@ impl Component for StatusBar { fn get_shortcuts(&self, context: &Context) -> ShortcutMap { self.container.get_shortcuts(context) } -} -// A box with a text content. -#[derive(Debug)] -pub struct TextBox { - _content: String, -} - -impl TextBox { - pub fn new(s: String) -> Self { - TextBox { _content: s } + fn id(&self) -> ComponentId { + self.id } -} - -impl fmt::Display for TextBox { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // TODO display info - write!(f, "text box") + fn set_id(&mut self, id: ComponentId) { + self.id = id; } } -impl Component for TextBox { - fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {} - fn process_event(&mut self, _event: &mut UIEvent, _context: &mut Context) -> bool { - false - } - fn set_dirty(&mut self) {} -} - #[derive(Debug)] pub struct Progress { description: String, total_work: usize, finished: usize, + id: ComponentId, } impl Progress { @@ -779,6 +797,7 @@ impl Progress { description: s, total_work, finished: 0, + id: ComponentId::default(), } } @@ -817,17 +836,25 @@ impl Component for Progress { false } fn set_dirty(&mut self) {} + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } #[derive(Debug)] pub struct Tabbed { pinned: usize, - children: Vec, + children: Vec>, cursor_pos: usize, show_shortcuts: bool, dirty: bool, + id: ComponentId, } impl Tabbed { @@ -835,10 +862,11 @@ impl Tabbed { let pinned = children.len(); Tabbed { pinned, - children: children.into_iter().map(Entity::from).collect(), + children, cursor_pos: 0, show_shortcuts: false, dirty: true, + id: ComponentId::default(), } } fn draw_tabs(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { @@ -895,7 +923,7 @@ impl Tabbed { context.dirty_areas.push_back(area); } pub fn add_component(&mut self, new: Box) { - self.children.push(Entity::from(new)); + self.children.push(new); } } @@ -1027,7 +1055,7 @@ impl Component for Tabbed { if self.pinned > self.cursor_pos { return true; } - let id = *self.children[self.cursor_pos].id(); + let id = self.children[self.cursor_pos].id(); self.children[self.cursor_pos].kill(id); self.set_dirty(); return true; @@ -1036,14 +1064,14 @@ impl Component for Tabbed { if self.pinned > self.cursor_pos { return true; } - if let Some(c_idx) = self.children.iter().position(|x| x.id() == id) { + if let Some(c_idx) = self.children.iter().position(|x| x.id() == *id) { self.children.remove(c_idx); self.cursor_pos = self.cursor_pos.saturating_sub(1); self.set_dirty(); return true; } else { eprintln!( - "DEBUG: Child entity with id {:?} not found.\nList: {:?}", + "DEBUG: Child component with id {:?} not found.\nList: {:?}", id, self.children ); } @@ -1059,6 +1087,13 @@ impl Component for Tabbed { self.dirty = true; self.children[self.cursor_pos].set_dirty(); } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } type EntryIdentifier = Vec; @@ -1074,6 +1109,7 @@ pub struct Selector { cursor: usize, dirty: bool, + id: ComponentId, } impl fmt::Display for Selector { @@ -1136,6 +1172,13 @@ impl Component for Selector { fn set_dirty(&mut self) { self.dirty = true; } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } impl Selector { @@ -1170,6 +1213,7 @@ impl Selector { content, cursor: 0, dirty: true, + id: ComponentId::default(), } } diff --git a/ui/src/components/utilities/widgets.rs b/ui/src/components/utilities/widgets.rs index 2d527ac0d..6a2b6cd28 100644 --- a/ui/src/components/utilities/widgets.rs +++ b/ui/src/components/utilities/widgets.rs @@ -187,6 +187,11 @@ impl Component for Field { true } fn set_dirty(&mut self) {} + + fn id(&self) -> ComponentId { + ComponentId::nil() + } + fn set_id(&mut self, _id: ComponentId) {} } impl fmt::Display for Field { @@ -206,6 +211,7 @@ pub struct FormWidget { focus: FormFocus, hide_buttons: bool, dirty: bool, + id: ComponentId, } impl fmt::Display for FormWidget { @@ -441,6 +447,13 @@ impl Component for FormWidget { fn set_dirty(&mut self) { self.dirty = true; } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } #[derive(Debug, Default)] @@ -453,6 +466,7 @@ where result: Option, cursor: usize, + id: ComponentId, } impl fmt::Display for ButtonWidget @@ -474,6 +488,7 @@ where buttons: vec![init_val].into_iter().collect(), result: None, cursor: 0, + id: ComponentId::default(), } } @@ -542,6 +557,13 @@ where true } fn set_dirty(&mut self) {} + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } #[derive(Debug, PartialEq)] @@ -551,6 +573,7 @@ pub struct AutoComplete { cursor: usize, dirty: bool, + id: ComponentId, } impl fmt::Display for AutoComplete { @@ -595,6 +618,13 @@ impl Component for AutoComplete { fn set_dirty(&mut self) { self.dirty = true; } + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } } impl AutoComplete { @@ -604,6 +634,7 @@ impl AutoComplete { content: CellBuffer::default(), cursor: 0, dirty: true, + id: ComponentId::default(), }; ret.set_suggestions(entries); ret diff --git a/ui/src/state.rs b/ui/src/state.rs index 0ae49dca1..0530fdd17 100644 --- a/ui/src/state.rs +++ b/ui/src/state.rs @@ -21,9 +21,9 @@ /*! The application's state. -The UI crate has an Entity-Component-System design. The System part, is also the application's state, so they're both merged in the `State` struct. +The UI crate has an Box-Component-System design. The System part, is also the application's state, so they're both merged in the `State` struct. -`State` owns all the Entities of the UI, which are currently plain Containers for `Component`s. In the application's main event loop, input is handed to the state in the form of `UIEvent` objects which traverse the entity graph. Components decide to handle each input or not. +`State` owns all the Components of the UI. In the application's main event loop, input is handed to the state in the form of `UIEvent` objects which traverse the component graph. Components decide to handle each input or not. Input is received in the main loop from threads which listen on the stdin for user input, observe folders for file changes etc. The relevant struct is `ThreadEvent`. */ @@ -115,7 +115,7 @@ impl Context { } } -/// A State object to manage and own components and entities of the UI. `State` is responsible for +/// A State object to manage and own components and components of the UI. `State` is responsible for /// managing the terminal and interfacing with `melib` pub struct State { cols: usize, @@ -125,7 +125,7 @@ pub struct State { stdout: Option, child: Option, pub mode: UIMode, - entities: Vec, + components: Vec>, pub context: Context, threads: FnvHashMap, thread::JoinHandle<()>)>, work_controller: WorkController, @@ -199,7 +199,7 @@ impl State { stdout: Some(stdout), child: None, mode: UIMode::Normal, - entities: Vec::with_capacity(1), + components: Vec::with_capacity(1), context: Context { accounts, @@ -376,8 +376,8 @@ impl State { /// Force a redraw for all dirty components. pub fn redraw(&mut self) { - for i in 0..self.entities.len() { - self.draw_entity(i); + for i in 0..self.components.len() { + self.draw_component(i); } let areas: Vec = self.context.dirty_areas.drain(0..).collect(); /* draw each dirty area */ @@ -433,30 +433,30 @@ impl State { pub fn render(&mut self) { self.update_size(); - /* draw each entity */ - for i in 0..self.entities.len() { - self.draw_entity(i); + /* draw each component */ + for i in 0..self.components.len() { + self.draw_component(i); } let cols = self.cols; let rows = self.rows; self.draw_area(((0, 0), (cols - 1, rows - 1))); } - pub fn draw_entity(&mut self, idx: usize) { - let entity = &mut self.entities[idx]; + pub fn draw_component(&mut self, idx: usize) { + let component = &mut self.components[idx]; let upper_left = (0, 0); let bottom_right = (self.cols - 1, self.rows - 1); - if entity.component.is_dirty() { - entity.component.draw( + if component.is_dirty() { + component.draw( &mut self.grid, (upper_left, bottom_right), &mut self.context, ); } } - pub fn register_entity(&mut self, entity: Entity) { - self.entities.push(entity); + pub fn register_component(&mut self, component: Box) { + self.components.push(component); } /// Convert user commands to actions/method calls. fn parse_command(&mut self, cmd: &str) { @@ -499,9 +499,9 @@ impl State { } _ => {} } - /* inform each entity */ - for i in 0..self.entities.len() { - self.entities[i].rcv_event(&mut event, &mut self.context); + /* inform each component */ + for i in 0..self.components.len() { + self.components[i].process_event(&mut event, &mut self.context); } if !self.context.replies.is_empty() { diff --git a/ui/src/types.rs b/ui/src/types.rs index 03a662103..cf40134b5 100644 --- a/ui/src/types.rs +++ b/ui/src/types.rs @@ -84,7 +84,7 @@ pub enum UIEventType { Action(Action), StatusEvent(StatusEvent), MailboxUpdate((usize, usize)), // (account_idx, mailbox_idx) - EntityKill(Uuid), + ComponentKill(Uuid), StartupCheck(FolderHash), RefreshEvent(Box), EnvelopeUpdate(EnvelopeHash),