ui: remove Entity

embed
Manos Pitsidianakis 2019-04-10 22:01:02 +03:00
parent b993375fa0
commit 106744c7ca
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
22 changed files with 343 additions and 180 deletions

View File

@ -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<UIEvent> = state.context.replies();
for e in events {
state.rcv_event(e);

View File

@ -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<Component>, // more than one?
}
impl From<Box<Component>> for Entity {
fn from(mut kind: Box<Component>) -> Entity {
let id = Uuid::new_v4();
kind.set_id(id);
Entity {
id,
component: kind,
}
}
}
impl<C: 'static> From<Box<C>> for Entity
where
C: Component,
{
fn from(mut kind: Box<C>) -> 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<Component> {
&mut self.component
}
}
impl Deref for Entity {
type Target = Box<Component>;
fn deref(&self) -> &Box<Component> {
&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()

View File

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

View File

@ -23,7 +23,8 @@ pub struct ContactList {
mode: ViewMode,
dirty: bool,
view: Option<Entity>,
view: Option<Box<Component>>,
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;
}
}

View File

@ -38,6 +38,7 @@ pub struct Indexer {
entries: Vec<MenuEntry>,
dirty: bool,
cursor: Vec<usize>,
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;
}
}

View File

@ -34,6 +34,7 @@ pub struct Index {
state: IndexState,
content: Box<IndexContent>,
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 {

View File

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

View File

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

View File

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

View File

@ -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<IndexStyle> for Listing {

View File

@ -43,6 +43,7 @@ struct MailboxView {
view: ThreadView,
movement: Option<PageMovement>,
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;
}
}

View File

@ -41,6 +41,7 @@ pub struct PlainListing {
/// If `self.view` exists or not.
unfocused: bool,
view: Option<MailView>,
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;
}
}

View File

@ -44,6 +44,7 @@ pub struct ThreadListing {
unfocused: bool,
initialised: bool,
view: Option<MailView>,
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;
}
}

View File

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

View File

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

View File

@ -27,10 +27,12 @@ use std::process::{Command, Stdio};
pub struct HtmlView {
pager: Pager,
bytes: Vec<u8>,
id: ComponentId,
}
impl HtmlView {
pub fn new(bytes: Vec<u8>, 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;
}
}

View File

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

View File

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

View File

@ -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<Component>,
bottom: Box<Component>,
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<Component>,
bottom: Box<Component>,
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<Component>,
right: Box<Component>,
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<Component>,
right: Box<Component>,
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<PageMovement>,
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<Component>,
status: String,
notifications: VecDeque<String>,
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<Component>) -> 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<Entity>,
children: Vec<Box<Component>>,
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<Component>) {
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<u8>;
@ -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(),
}
}

View File

@ -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<T>,
cursor: usize,
id: ComponentId,
}
impl<T> fmt::Display for ButtonWidget<T>
@ -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

View File

@ -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>-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<StateStdout>,
child: Option<ForkType>,
pub mode: UIMode,
entities: Vec<Entity>,
components: Vec<Box<Component>>,
pub context: Context,
threads: FnvHashMap<thread::ThreadId, (chan::Sender<bool>, 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<Area> = 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<Component>) {
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() {

View File

@ -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<RefreshEvent>),
EnvelopeUpdate(EnvelopeHash),