diff --git a/src/bin.rs b/src/bin.rs index f757a405..f7bcaa0e 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 3e90ab94..f5b843d6 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 dddbb0da..592db0b1 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 3554b5b5..e124a2a5 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 2d1640ea..89a0996d 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 4794baca..17272d25 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 503c669b..0eda5942 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 42ee3f8d..afd49ee4 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 d38f1355..d449147c 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 9e2371d7..f108d9f8 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 e885cf09..a22942d6 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 01e1f620..30fae2bb 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 a6ccfccc..95e311ca 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 0fb3b77a..6ee2160f 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 51299f12..13217a5f 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 b4ca9f55..c5e5cfab 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 0a625cd9..d5d2986f 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 f7df74fa..55aabbf7 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 8f713396..86dc6c54 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 2d527ac0..6a2b6cd2 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 0ae49dca..0530fdd1 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 03a66210..cf40134b 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),