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
//! Base elements required to build views. //! //! 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::required_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 `required_size` should be an actually viable size, //! no matter what the request is. This means calling `View::layout()` with //! a size returned by `required_size` is **never** an error. #[macro_use] mod view_wrapper; // Essentials components mod position; mod size_cache; mod size_constraint; mod view_path; // Helper bases mod scroll; mod identifiable; mod boxable; pub use self::boxable::Boxable; pub use self::identifiable::Identifiable; pub use self::position::{Offset, Position}; pub use self::scroll::{ScrollBase, ScrollStrategy}; pub use self::size_cache::SizeCache; pub use self::size_constraint::SizeConstraint; pub use self::view_path::ViewPath; pub use self::view_wrapper::ViewWrapper; use Printer; use direction::Direction; use event::{Event, EventResult}; use vec::Vec2; use views::IdView; use std::any::Any; /// 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. /// /// Default implementation always return `(1,1)`. fn required_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 `required_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); /// Runs a closure on the view identified by the given selector. /// /// See [`Finder::call_on`] for a nicer interface, implemented for all views. /// /// [`Finder::call_on`]: trait.Finder.html#method.call_on /// /// If the selector doesn't find a match, the closure will not be run. /// /// Default implementation is a no-op. fn call_on_any<'a>(&mut self, _: &Selector, _: Box<FnMut(&mut Any) + 'a>) { // TODO: FnMut -> FnOnce once it works } /// Moves the focus to the view identified by the given selector. /// /// Returns `Ok(())` if the view was found and selected. /// /// Default implementation simply returns `Err(())`. fn focus_view(&mut self, &Selector) -> Result<(), ()> { Err(()) } /// 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. /// /// Default implementation always return `false`. fn take_focus(&mut self, source: Direction) -> bool { let _ = source; false } } /// Provides `call_on<V: View>` to views. /// /// This trait is mostly a wrapper around [`View::call_on_any`]. /// /// It provides a nicer interface to find a view when you know its type. /// /// [`View::call_on_any`]: ./trait.View.html#method.call_on_any pub trait Finder { /// Tries to find the view pointed to by the given selector. /// /// If the view is not found, or if it is not of the asked type, /// it returns None. fn call_on<V, F, R>(&mut self, sel: &Selector, callback: F) -> Option<R> where V: View + Any, F: FnOnce(&mut V) -> R; /// Convenient method to use `call_on` with a `view::Selector::Id`. fn find_id<V, F, R>(&mut self, id: &str, callback: F) -> Option<R> where V: View + Any, F: FnOnce(&mut V) -> R { self.call_on(&Selector::Id(id), callback) } } impl<T: View> Finder for T { fn call_on<V, F, R>(&mut self, sel: &Selector, callback: F) -> Option<R> where V: View + Any, F: FnOnce(&mut V) -> R { let mut result = None; { let result_ref = &mut result; let mut callback = Some(callback); let callback = |v: &mut Any| if let Some(callback) = callback.take() { if v.is::<V>() { *result_ref = v.downcast_mut::<V>().map(|v| callback(v)); } else if v.is::<IdView<V>>() { *result_ref = v.downcast_mut::<IdView<V>>() .and_then(|v| v.with_view_mut(callback)); } }; self.call_on_any(sel, Box::new(callback)); } result } } /// 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), }