1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! Defines various views to use when creating the layout.
//!
//! Views are the main building blocks of your UI.
//!
//! A view can delegate part or all of its responsabilities to child views,
//! forming a view tree. The root of this tree is a `StackView` handled
//! directly by the `Cursive` element.
//!
//! # Layout
//!
//! The layout phase is when the size and location of each view is computed.
//!
//! Each view is given an area of the screen by the `View::layout()` method.
//! With this, the view is free to plan its content, including calling
//! `View::layout()` on its own children.
//!
//! In order to determine how much space should be given each child, parents
//! can use `View::get_min_size()` on them.
//!
//!
//! ### Contracts
//!
//! When building new Views, you should respect these contracts:
//!
//! * By default, `View::layout()` should be called before any call to
//!   `View::draw()` with the same available size. The only exceptions is
//!   when both following conditions are met:
//!     * The available size has not changed since the last call to
//!       `View::layout()`
//!     * `View::needs_relayout()` returns `false`
//!
//!   In this case, it is safe to omit the call to `View::layout()`.
//!
//! * The value returned by `get_min_size` should be an actually viable size,
//!   no matter what the request is. This means calling `View::layout()` with
//!   a size returned by `get_min_size` is **never** an error.

#[macro_use]
mod view_wrapper;

// Essentials components
mod position;
mod view_path;

// Helper bases
mod scroll;

// Views
mod box_view;
mod button;
mod checkbox;
mod dialog;
mod edit_view;
mod full_view;
mod id_view;
mod key_event_view;
mod linear_layout;
mod list_view;
mod menubar;
mod menu_popup;
mod shadow_view;
mod select_view;
mod sized_view;
mod stack_view;
mod text_view;
mod tracked_view;


use std::any::Any;

use XY;
use direction::Direction;
use event::{Event, EventResult};
use vec::Vec2;
use Printer;

pub use self::position::{Offset, Position};

pub use self::scroll::ScrollBase;

pub use self::id_view::IdView;
pub use self::box_view::BoxView;
pub use self::button::Button;
pub use self::checkbox::Checkbox;
pub use self::dialog::Dialog;
pub use self::edit_view::EditView;
pub use self::full_view::FullView;
pub use self::key_event_view::KeyEventView;
pub use self::linear_layout::LinearLayout;
pub use self::list_view::ListView;
pub use self::menubar::Menubar;
pub use self::menu_popup::MenuPopup;
pub use self::view_path::ViewPath;
pub use self::select_view::SelectView;
pub use self::shadow_view::ShadowView;
pub use self::stack_view::StackView;
pub use self::text_view::TextView;
pub use self::tracked_view::TrackedView;
pub use self::sized_view::SizedView;
pub use self::view_wrapper::ViewWrapper;


/// Main trait defining a view behaviour.
pub trait View {
    /// Called when a key was pressed. Default implementation just ignores it.
    fn on_event(&mut self, Event) -> EventResult {
        EventResult::Ignored
    }

    /// Returns the minimum size the view requires with the given restrictions.
    ///
    /// If the view is flexible (it has multiple size options), it can try
    /// to return one that fits the given `constraint`.
    /// It's also fine to ignore it and return a fixed value.
    fn get_min_size(&mut self, constraint: Vec2) -> Vec2 {
        let _ = constraint;
        Vec2::new(1, 1)
    }

    /// Returns `true` if the view content changed since last layout phase.
    ///
    /// This is mostly an optimisation for views where the layout phase is
    /// expensive.
    ///
    /// * Views can ignore it and always return true (default implementation).
    ///   They will always be assumed to have changed.
    /// * View Groups can ignore it and always re-layout their children.
    ///     * If they call `get_min_size` or `layout` with stable parameters,
    ///       the children may cache the result themselves and speed up the
    ///       process anyway.
    fn needs_relayout(&self) -> bool {
        true
    }

    /// Called once the size for this view has been decided,
    ///
    /// View groups should propagate the information to their children.
    fn layout(&mut self, Vec2) {}

    /// Draws the view with the given printer (includes bounds) and focus.
    fn draw(&self, printer: &Printer);

    /// Finds the view pointed to by the given path.
    ///
    /// Returns None if the path doesn't lead to a view.
    fn find(&mut self, &Selector) -> Option<&mut Any> {
        None
    }

    /// This view is offered focus. Will it take it?
    ///
    /// `source` indicates where the focus comes from.
    /// When the source is unclear, `Front` is usually used.
    fn take_focus(&mut self, source: Direction) -> bool {
        let _ = source;
        false
    }
}

/// Cache around a one-dimensional layout result
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct SizeCache {
    /// Cached value
    pub value: usize,
    /// `true` if the last size was constrained.
    ///
    /// If unconstrained, any request larger than this value
    /// would return the same size.
    pub constrained: bool,
}

impl SizeCache {
    /// Creates a new sized cache
    pub fn new(value: usize, constrained: bool) -> Self {
        SizeCache {
            value: value,
            constrained: constrained,
        }
    }

    /// Returns `true` if `self` is still valid for the given `request`.
    pub fn accept(self, request: usize) -> bool {
        if request < self.value {
            false
        } else if request == self.value {
            true
        } else {
            !self.constrained
        }
    }

    /// Creates a new bi-dimensional cache.
    ///
    /// * `size` must fit inside `req`.
    /// * for each dimension, `constrained = (size == req)`
    fn build(size: Vec2, req: Vec2) -> XY<Self> {
        XY::new(SizeCache::new(size.x, size.x >= req.x),
                SizeCache::new(size.y, size.y >= req.y))
    }
}


/// Selects a single view (if any) in the tree.
pub enum Selector<'a> {
    /// Selects a view from its ID.
    Id(&'a str),
    /// Selects a view from its path.
    Path(&'a ViewPath),
}