cursive_core-0.3.5/.cargo_vcs_info.json0000644000000001520000000000100135270ustar { "git": { "sha1": "ce5f8a79c37f7fddeff8a2dc9604c72bec749912" }, "path_in_vcs": "cursive-core" }cursive_core-0.3.5/Cargo.toml0000644000000036240000000000100115340ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "cursive_core" version = "0.3.5" authors = ["Alexandre Bury "] description = "Core components for the Cursive TUI" documentation = "https://docs.rs/cursive" readme = "Readme.md" keywords = [ "ncurses", "TUI", "UI", ] categories = [ "command-line-interface", "gui", ] license = "MIT" repository = "https://github.com/gyscos/cursive" [package.metadata.docs.rs] all-features = true [lib] name = "cursive_core" [dependencies.ahash] version = "0.8" [dependencies.ansi-parser] version = "0.8.0" optional = true [dependencies.crossbeam-channel] version = "0.5" [dependencies.enum-map] version = "2.0" [dependencies.enumset] version = "1.0.4" [dependencies.lazy_static] version = "1" [dependencies.log] version = "0.4" [dependencies.num] version = "0.4" default-features = false [dependencies.owning_ref] version = "0.4" [dependencies.pulldown-cmark] version = "0.9" optional = true default-features = false [dependencies.time] version = "0.3" features = [ "local-offset", "formatting", ] [dependencies.toml] version = "0.5" optional = true [dependencies.unicode-segmentation] version = "1" [dependencies.unicode-width] version = "0.1" [dependencies.xi-unicode] version = "0.3" [features] ansi = ["ansi-parser"] doc-cfg = [] markdown = ["pulldown-cmark"] unstable_scroll = [] [badges.appveyor] repository = "gyscos/cursive" [badges.travis-ci] repository = "gyscos/cursive" cursive_core-0.3.5/Cargo.toml.orig000064400000000000000000000023521046102023000152120ustar 00000000000000[package] authors = ["Alexandre Bury "] categories = ["command-line-interface", "gui"] description = "Core components for the Cursive TUI" documentation = "https://docs.rs/cursive" keywords = ["ncurses", "TUI", "UI"] license = "MIT" name = "cursive_core" readme = "Readme.md" repository = "https://github.com/gyscos/cursive" version = "0.3.5" edition = "2018" [package.metadata.docs.rs] all-features = true [badges.travis-ci] repository = "gyscos/cursive" [badges.appveyor] repository = "gyscos/cursive" [dependencies] enum-map = "2.0" enumset = "1.0.4" log = "0.4" owning_ref = "0.4" unicode-segmentation = "1" unicode-width = "0.1" xi-unicode = "0.3" crossbeam-channel = "0.5" lazy_static = "1" ahash = "0.8" [dependencies.ansi-parser] version = "0.8.0" optional = true [dependencies.time] version = "0.3" features = ["local-offset", "formatting"] [dependencies.toml] optional = true version = "0.5" [dependencies.num] default-features = false version = "0.4" [dependencies.pulldown-cmark] default-features = false optional = true version = "0.9" [features] doc-cfg = [] markdown = ["pulldown-cmark"] ansi = ["ansi-parser"] unstable_scroll = [] # Deprecated feature, remove in next version [lib] name = "cursive_core" cursive_core-0.3.5/Readme.md000064400000000000000000000003301046102023000140340ustar 00000000000000# Cursive-core This crate is where most of cursive is defined, except for the backends. Third-party libraries are encouraged to depend on this instead of `cursive`, as it should have fewer semver breaking updates. cursive_core-0.3.5/src/align.rs000064400000000000000000000066131046102023000145560ustar 00000000000000//! Tools to control view alignment. /// Specifies the alignment along both horizontal and vertical directions. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Align { /// Horizontal alignment policy pub h: HAlign, /// Vertical alignment policy pub v: VAlign, } impl Align { /// Creates a new Align object from the given alignments. pub fn new(h: HAlign, v: VAlign) -> Self { Align { h, v } } /// Creates a top-left alignment. pub fn top_left() -> Self { Align::new(HAlign::Left, VAlign::Top) } /// Creates a top-right alignment. pub fn top_right() -> Self { Align::new(HAlign::Right, VAlign::Top) } /// Creates a top-center alignment. pub fn top_center() -> Self { Align::new(HAlign::Center, VAlign::Top) } /// Creates a bottom-left alignment. pub fn bot_left() -> Self { Align::new(HAlign::Left, VAlign::Bottom) } /// Creates a bottom-right alignment. pub fn bot_right() -> Self { Align::new(HAlign::Right, VAlign::Bottom) } /// Creates a bottom-center alignment. pub fn bot_center() -> Self { Align::new(HAlign::Center, VAlign::Bottom) } /// Creates a center-right alignment. pub fn center_left() -> Self { Align::new(HAlign::Left, VAlign::Center) } /// Creates a center-right alignment. pub fn center_right() -> Self { Align::new(HAlign::Right, VAlign::Center) } /// Creates an alignment centered both horizontally and vertically. pub fn center() -> Self { Align::new(HAlign::Center, VAlign::Center) } } /// Horizontal alignment #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum HAlign { /// Place the element to the left of available space Left, /// Place the element horizontally in the center of available space Center, /// Place the element to the right of available space Right, } /// Vertical alignment #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum VAlign { /// Place the element at the top of available space Top, /// Place the element vertically in the center of available space Center, /// Place the element at the bottom of available space Bottom, } impl HAlign { /// Returns the offset required to position a view. /// /// When drawing a view with size `content` when the available size is /// `container`, printing at the resulting offset will align the view as /// desired. pub fn get_offset(&self, content: usize, container: usize) -> usize { if container < content { 0 } else { match *self { HAlign::Left => 0, HAlign::Center => (container - content) / 2, HAlign::Right => container - content, } } } } impl VAlign { /// Returns the offset required to position a view. /// /// When drawing a view with size `content` when the available size is /// `container`, printing at the resulting offset will align the view as /// desired. pub fn get_offset(&self, content: usize, container: usize) -> usize { if container < content { 0 } else { match *self { VAlign::Top => 0, VAlign::Center => (container - content) / 2, VAlign::Bottom => container - content, } } } } cursive_core-0.3.5/src/backend.rs000064400000000000000000000111211046102023000150410ustar 00000000000000//! Define the backend trait for actual terminal interaction. //! //! Cursive doesn't print anything by itself: it delegates this job to a //! backend library, which handles all actual input and output. //! //! This module defines the [`Backend`] trait, to be implemented by actual //! types, usually using third-party libraries. //! //! [`Backend`]: trait.Backend.html use crate::event::Event; use crate::theme; use crate::Vec2; use unicode_width::UnicodeWidthStr; /// Trait defining the required methods to be a backend. /// /// A backend is the interface between the abstract view tree and the actual /// input/output, like a terminal. /// /// It usually delegates the work to a terminal-handling library like ncurses /// or termion, or it can entirely simulate a terminal and show it as a /// graphical window (`BearLibTerminal`). /// /// When creating a new cursive tree with [`Cursive::new()`][1], you will need to /// provide a backend initializer - usually their `init()` function. /// /// Backends are responsible for handling input and converting it to [`Event`]. /// Input must be non-blocking, it will be polled regularly. /// /// [1]: ../struct.Cursive.html#method.new /// [`Event`]: ../event/enum.Event.html pub trait Backend { /// Polls the backend for any input. /// /// Should return immediately: /// * `None` if no event is currently available. /// * `Some(event)` for each event to process. fn poll_event(&mut self) -> Option; /// Sets the title for the backend. /// /// This usually sets the terminal window title. fn set_title(&mut self, title: String); /// Refresh the screen. /// /// This will be called each frame after drawing has been done. /// /// A backend could, for example, buffer any print command, and apply /// everything when refresh() is called. fn refresh(&mut self); /// Should return `true` if this backend supports colors. fn has_colors(&self) -> bool; /// Returns the screen size. fn screen_size(&self) -> Vec2; /// Main method used for printing fn print_at(&self, pos: Vec2, text: &str); /// Efficient method to print repetitions of the same text. /// /// Usually used to draw horizontal lines/borders. /// /// It is a small optimization to avoid moving the cursor after each step. fn print_at_rep(&self, pos: Vec2, repetitions: usize, text: &str) { if repetitions > 0 { self.print_at(pos, text); let width = text.width(); let mut pos = pos; let mut dupes_left = repetitions - 1; while dupes_left > 0 { pos = pos.saturating_add((width, 0)); self.print_at(pos, text); dupes_left -= 1; } } } /// Clears the screen with the given color. fn clear(&self, color: theme::Color); /// Starts using a new color. /// /// This should return the previously active color. /// /// Any call to `print_at` from now on should use the given color. fn set_color(&self, colors: theme::ColorPair) -> theme::ColorPair; /// Enables the given effect. /// /// Any call to `print_at` from now on should use the given effect. fn set_effect(&self, effect: theme::Effect); /// Disables the given effect. fn unset_effect(&self, effect: theme::Effect); /// Returns a name to identify the backend. /// /// Mostly used for debugging. fn name(&self) -> &str { "unknown" } } /// Dummy backend that does nothing and immediately exits. /// /// Mostly used for testing. pub struct Dummy; impl Dummy { /// Creates a new dummy backend. pub fn init() -> Box where Self: Sized, { Box::new(Dummy) } } impl Backend for Dummy { fn name(&self) -> &str { "dummy" } fn set_title(&mut self, _title: String) {} fn refresh(&mut self) {} fn has_colors(&self) -> bool { false } fn screen_size(&self) -> Vec2 { (1, 1).into() } fn poll_event(&mut self) -> Option { Some(Event::Exit) } fn print_at(&self, _: Vec2, _: &str) {} fn print_at_rep(&self, _pos: Vec2, _repetitions: usize, _text: &str) {} fn clear(&self, _: theme::Color) {} // This sets the Colours and returns the previous colours // to allow you to set them back when you're done. fn set_color(&self, colors: theme::ColorPair) -> theme::ColorPair { // TODO: actually save a stack of colors? colors } fn set_effect(&self, _: theme::Effect) {} fn unset_effect(&self, _: theme::Effect) {} } cursive_core-0.3.5/src/cursive.rs000064400000000000000000000741151046102023000151460ustar 00000000000000use std::any::Any; use std::num::NonZeroU32; #[cfg(feature = "toml")] use std::path::Path; use crossbeam_channel::{self, Receiver, Sender}; use crate::{ backend, cursive_run::CursiveRunner, direction, event::{Event, EventResult}, printer::Printer, theme, view::{self, Finder, IntoBoxedView, Position, View, ViewNotFound}, views::{self, LayerPosition}, Dump, Vec2, }; static DEBUG_VIEW_NAME: &str = "_cursive_debug_view"; type RootView = views::OnEventView>; type BackendCallback = dyn FnOnce(&mut dyn backend::Backend); type Callback = dyn FnOnce(&mut Cursive) + Send; /// Central part of the cursive library. /// /// It initializes ncurses on creation and cleans up on drop. /// To use it, you should populate it with views, layouts, and callbacks, /// then start the event loop with `run()`. /// /// It uses a list of screen, with one screen active at a time. pub struct Cursive { theme: theme::Theme, // The main view root: RootView, menubar: views::Menubar, pub(crate) needs_clear: bool, running: bool, // Handle asynchronous callbacks cb_source: Receiver>, cb_sink: Sender>, last_size: Vec2, // User-provided data. user_data: Box, // Handle auto-refresh when no event is received. fps: Option, // List of callbacks to run on the backend. // The current assumption is that we only add calls here during event processing. pub(crate) backend_calls: Vec>, } /// Identifies a screen in the cursive root. pub type ScreenId = usize; /// Convenient alias to the result of `Cursive::cb_sink`. /// /// # Notes /// /// Callbacks need to be `Send`, which can be limiting in some cases. /// /// In some case [`send_wrapper`] may help you work around that. /// /// [`send_wrapper`]: https://crates.io/crates/send_wrapper pub type CbSink = Sender>; new_default!(Cursive); impl Cursive { /// Creates a new Cursive root, and initialize the back-end. /// /// You probably don't want to use this function directly, unless you're /// using a non-standard backend. Built-in backends have dedicated functions in the /// [`CursiveExt`] trait. /// /// [`CursiveExt`]: https://docs.rs/cursive/0/cursive/trait.CursiveExt.html pub fn new() -> Self { let theme = theme::load_default(); let (cb_sink, cb_source) = crossbeam_channel::unbounded(); let mut cursive = Cursive { theme, root: views::OnEventView::new(views::ScreensView::single_screen( views::StackView::new(), )), menubar: views::Menubar::new(), last_size: Vec2::zero(), needs_clear: true, running: true, cb_source, cb_sink, fps: None, user_data: Box::new(()), backend_calls: Vec::new(), }; cursive.reset_default_callbacks(); cursive } /// Returns the screen size given in the last layout phase. /// /// Note: this will return `(0, 0)` before the first layout phase. pub fn screen_size(&self) -> Vec2 { self.last_size } pub(crate) fn layout(&mut self, size: Vec2) { self.last_size = size; let offset = if self.menubar.autohide { 0 } else { 1 }; let size = size.saturating_sub((0, offset)); self.root.layout(size); } pub(crate) fn draw(&mut self, size: Vec2, backend: &dyn backend::Backend) { let printer = Printer::new(size, &self.theme, backend); let selected = self.menubar.receive_events(); // Print the stackview background before the menubar let offset = if self.menubar.autohide { 0 } else { 1 }; // The printer for the stackview let sv_printer = printer.offset((0, offset)).focused(!selected); self.root.get_inner().draw_bg(&sv_printer); // Draw the currently active screen // If the menubar is active, nothing else can be. // Draw the menubar? if self.menubar.visible() { let printer = printer.focused(self.menubar.receive_events()); printer.with_color(theme::ColorStyle::primary(), |printer| { self.menubar.draw(printer) }); } // finally draw stackview layers // using variables from above self.root.get_inner().draw_fg(&sv_printer); } /// Sets some data to be stored in Cursive. /// /// It can later on be accessed with `Cursive::user_data()` pub fn set_user_data(&mut self, user_data: T) { self.user_data = Box::new(user_data); } /// Attempts to access the user-provided data. /// /// If some data was set previously with the same type, returns a /// reference to it. /// /// If nothing was set or if the type is different, returns `None`. pub fn user_data(&mut self) -> Option<&mut T> { self.user_data.downcast_mut() } /// Attemps to take by value the current user-data. /// /// If successful, this will replace the current user-data with the unit /// type `()`. /// /// If the current user data is not of the requested type, `None` will be /// returned. /// /// # Examples /// /// ```rust /// let mut siv = cursive_core::Cursive::new(); /// /// // Start with a simple `Vec` as user data. /// siv.set_user_data(vec![1i32, 2, 3]); /// assert_eq!(siv.user_data::>(), Some(&mut vec![1i32, 2, 3])); /// /// // Let's mutate the data a bit. /// siv.with_user_data(|numbers: &mut Vec| numbers.push(4)); /// /// // If mutable reference is not enough, we can take the data by value. /// let data: Vec = siv.take_user_data().unwrap(); /// assert_eq!(data, vec![1i32, 2, 3, 4]); /// /// // At this point the user data was removed and is no longer available. /// assert_eq!(siv.user_data::>(), None); /// ``` pub fn take_user_data(&mut self) -> Option { // Start by taking the user data and replacing it with a dummy. let user_data = std::mem::replace(&mut self.user_data, Box::new(())); // Downcast the data to the requested type. // If it works, unbox it. // It if doesn't, take it back. user_data .downcast() .map_err(|user_data| { // If we asked for the wrong type, put it back. self.user_data = user_data; }) .map(|boxed| *boxed) .ok() } /// Runs the given closure on the stored user data, if any. /// /// If no user data was supplied, or if the type is different, nothing /// will be run. /// /// Otherwise, the result will be returned. pub fn with_user_data(&mut self, f: F) -> Option where F: FnOnce(&mut T) -> R, T: Any, { self.user_data().map(f) } /// Sets the title for the terminal window. /// /// Note that not all backends support this. pub fn set_window_title>(&mut self, title: S) { let title = title.into(); self.backend_calls .push(Box::new(move |backend| backend.set_title(title))); } /// Show the debug console. /// /// Currently, this will show logs if [`logger::init()`](crate::logger::init()) was called. pub fn show_debug_console(&mut self) { self.add_layer( views::Dialog::around( views::ScrollView::new(views::NamedView::new( DEBUG_VIEW_NAME, views::DebugView::new(), )) .scroll_x(true), ) .title("Debug console"), ); } /// Show the debug console, or hide it if it's already visible. /// /// # Examples /// /// ```rust /// # use cursive_core::Cursive; /// # let mut siv = Cursive::new(); /// siv.add_global_callback('~', Cursive::toggle_debug_console); /// ``` pub fn toggle_debug_console(&mut self) { if let Some(pos) = self.screen_mut().find_layer_from_name(DEBUG_VIEW_NAME) { self.screen_mut().remove_layer(pos); } else { self.show_debug_console(); } } /// Returns a sink for asynchronous callbacks. /// /// Returns the sender part of a channel, that allows to send /// callbacks to `self` from other threads. /// /// Callbacks will be executed in the order /// of arrival on the next event cycle. /// /// # Notes /// /// Callbacks need to be `Send`, which can be limiting in some cases. /// /// In some case [`send_wrapper`] may help you work around that. /// /// [`send_wrapper`]: https://crates.io/crates/send_wrapper /// /// # Examples /// /// ```rust /// # use cursive_core::*; /// let mut siv = Cursive::new(); /// /// // quit() will be called during the next event cycle /// siv.cb_sink().send(Box::new(|s| s.quit())).unwrap(); /// ``` pub fn cb_sink(&self) -> &CbSink { &self.cb_sink } /// Selects the menubar. pub fn select_menubar(&mut self) { if let Ok(res) = self.menubar.take_focus(direction::Direction::none()) { res.process(self); } } /// Sets the menubar autohide feature. /// /// * When enabled (default), the menu is only visible when selected. /// * When disabled, the menu is always visible and reserves the top row. pub fn set_autohide_menu(&mut self, autohide: bool) { self.menubar.autohide = autohide; } /// Access the menu tree used by the menubar. /// /// This allows to add menu items to the menubar. /// /// # Examples /// /// ```rust /// # use cursive_core::{Cursive, event}; /// # use cursive_core::views::{Dialog}; /// # use cursive_core::traits::*; /// # use cursive_core::menu; /// # /// let mut siv = Cursive::new(); /// /// siv.menubar() /// .add_subtree( /// "File", /// menu::Tree::new() /// .leaf("New", |s| s.add_layer(Dialog::info("New file!"))) /// .subtree( /// "Recent", /// menu::Tree::new().with(|tree| { /// for i in 1..100 { /// tree.add_leaf(format!("Item {}", i), |_| ()) /// } /// }), /// ) /// .delimiter() /// .with(|tree| { /// for i in 1..10 { /// tree.add_leaf(format!("Option {}", i), |_| ()); /// } /// }) /// .delimiter() /// .leaf("Quit", |s| s.quit()), /// ) /// .add_subtree( /// "Help", /// menu::Tree::new() /// .subtree( /// "Help", /// menu::Tree::new() /// .leaf("General", |s| { /// s.add_layer(Dialog::info("Help message!")) /// }) /// .leaf("Online", |s| { /// s.add_layer(Dialog::info("Online help?")) /// }), /// ) /// .leaf("About", |s| { /// s.add_layer(Dialog::info("Cursive v0.0.0")) /// }), /// ); /// /// siv.add_global_callback(event::Key::Esc, |s| s.select_menubar()); /// ``` pub fn menubar(&mut self) -> &mut views::Menubar { &mut self.menubar } /// Returns the currently used theme. pub fn current_theme(&self) -> &theme::Theme { &self.theme } /// Modifies the current theme. /// /// Shortcut to get the [`Cursive::current_theme()`], /// then run [`Cursive::set_theme()`]. pub fn with_theme(&mut self, f: F) { f(&mut self.theme); self.clear(); } /// Sets the current theme. pub fn set_theme(&mut self, theme: theme::Theme) { self.theme = theme; self.clear(); } /// Updates the current theme. pub fn update_theme(&mut self, f: impl FnOnce(&mut theme::Theme)) { // We don't just expose a `current_theme_mut` because we may want to // run some logic _after_ setting the theme. // Though right now, it's only clearing the screen, so... let mut theme = self.theme.clone(); f(&mut theme); self.set_theme(theme); } /// Clears the screen. /// /// Users rarely have to call this directly. pub fn clear(&mut self) { self.needs_clear = true; } /// Loads a theme from the given file. /// /// `filename` must point to a valid toml file. /// /// Must have the `toml` feature enabled. #[cfg(feature = "toml")] pub fn load_theme_file>( &mut self, filename: P, ) -> Result<(), theme::Error> { theme::load_theme_file(filename).map(|theme| self.set_theme(theme)) } /// Loads a theme from the given string content. /// /// Content must be valid toml. /// /// Must have the `toml` feature enabled. #[cfg(feature = "toml")] pub fn load_toml(&mut self, content: &str) -> Result<(), theme::Error> { theme::load_toml(content).map(|theme| self.set_theme(theme)) } /// Sets the refresh rate, in frames per second. /// /// Note that the actual frequency is not guaranteed. /// /// Between 0 and 30. Call with `fps = 0` to disable (default value). pub fn set_fps(&mut self, fps: u32) { self.fps = NonZeroU32::new(fps); } /// Enables or disables automatic refresh of the screen. /// /// This is a shortcut to call `set_fps` with `30` or `0` depending on /// `autorefresh`. pub fn set_autorefresh(&mut self, autorefresh: bool) { self.set_fps(if autorefresh { 30 } else { 0 }); } /// Returns the current refresh rate, if any. /// /// Returns `None` if no auto-refresh is set. Otherwise, returns the rate /// in frames per second. pub fn fps(&self) -> Option { self.fps } /// Returns a reference to the currently active screen. pub fn screen(&self) -> &views::StackView { self.root.get_inner().screen().unwrap() } /// Returns a mutable reference to the currently active screen. pub fn screen_mut(&mut self) -> &mut views::StackView { self.root.get_inner_mut().screen_mut().unwrap() } /// Returns the id of the currently active screen. pub fn active_screen(&self) -> ScreenId { self.root.get_inner().active_screen() } /// Adds a new screen, and returns its ID. pub fn add_screen(&mut self) -> ScreenId { self.root .get_inner_mut() .add_screen(views::StackView::new()) } /// Convenient method to create a new screen, and set it as active. pub fn add_active_screen(&mut self) -> ScreenId { let res = self.add_screen(); self.set_screen(res); res } /// Sets the active screen. Panics if no such screen exist. pub fn set_screen(&mut self, screen_id: ScreenId) { self.root.get_inner_mut().set_active_screen(screen_id); } /// Tries to find the view pointed to by the given selector. /// /// Runs a closure on the view once it's found, and return the /// result. /// /// If the view is not found, or if it is not of the asked type, /// returns None. /// /// # Examples /// /// ```rust /// # use cursive_core::{Cursive, views, view}; /// # use cursive_core::traits::*; /// let mut siv = Cursive::new(); /// /// siv.add_layer(views::TextView::new("Text #1").with_name("text")); /// /// siv.add_global_callback('p', |s| { /// s.call_on( /// &view::Selector::Name("text"), /// |view: &mut views::TextView| { /// view.set_content("Text #2"); /// }, /// ); /// }); /// ``` pub fn call_on( &mut self, sel: &view::Selector<'_>, callback: F, ) -> Option where V: View, F: FnOnce(&mut V) -> R, { self.root.call_on(sel, callback) } /// Tries to find the view identified by the given name. /// /// Convenient method to use `call_on` with a [`view::Selector::Name`]. /// /// # Examples /// /// ```rust /// # use cursive_core::{Cursive, views}; /// # use cursive_core::traits::*; /// let mut siv = Cursive::new(); /// /// siv.add_layer(views::TextView::new("Text #1").with_name("text")); /// /// siv.add_global_callback('p', |s| { /// s.call_on_name("text", |view: &mut views::TextView| { /// view.set_content("Text #2"); /// }); /// }); /// ``` pub fn call_on_name( &mut self, name: &str, callback: F, ) -> Option where V: View, F: FnOnce(&mut V) -> R, { self.call_on(&view::Selector::Name(name), callback) } /// Call the given closure on all views with the given name and the correct type. pub fn call_on_all_named(&mut self, name: &str, callback: F) where V: View, F: FnMut(&mut V), { self.root.call_on_all(&view::Selector::Name(name), callback); } /// Convenient method to find a view wrapped in [`NamedView`]. /// /// This looks for a `NamedView` with the given name, and return /// a [`ViewRef`] to the wrapped view. The `ViewRef` implements /// `DerefMut`, so you can treat it just like a `&mut T`. /// /// # Examples /// /// ```rust /// # use cursive_core::Cursive; /// # use cursive_core::views::{TextView, ViewRef}; /// # let mut siv = Cursive::new(); /// use cursive_core::traits::Nameable; /// /// siv.add_layer(TextView::new("foo").with_name("id")); /// /// // Could be called in a callback /// let mut view: ViewRef = siv.find_name("id").unwrap(); /// view.set_content("bar"); /// ``` /// /// Note that you must specify the exact type for the view you're after; for example, using the /// wrong item type in a `SelectView` will not find anything: /// /// ```rust /// # use cursive_core::Cursive; /// # use cursive_core::views::{SelectView}; /// # let mut siv = Cursive::new(); /// use cursive_core::traits::Nameable; /// /// let select = SelectView::new().item("zero", 0u32).item("one", 1u32); /// siv.add_layer(select.with_name("select")); /// /// // Specifying a wrong type will not return anything. /// assert!(siv.find_name::>("select").is_none()); /// /// // Omitting the type will use the default type, in this case `String`. /// assert!(siv.find_name::("select").is_none()); /// /// // But with the correct type, it works fine. /// assert!(siv.find_name::>("select").is_some()); /// ``` /// /// [`NamedView`]: views::NamedView /// [`ViewRef`]: views::ViewRef pub fn find_name(&mut self, id: &str) -> Option> where V: View, { self.call_on_name(id, views::NamedView::::get_mut) } /// Moves the focus to the view identified by `name`. /// /// Convenient method to call `focus` with a [`view::Selector::Name`]. pub fn focus_name( &mut self, name: &str, ) -> Result { self.focus(&view::Selector::Name(name)) } /// Moves the focus to the view identified by `sel`. pub fn focus( &mut self, sel: &view::Selector<'_>, ) -> Result { self.root.focus_view(sel) } /// Adds a global callback. /// /// Will be triggered on the given key press when no view catches it. /// /// # Examples /// /// ```rust /// # use cursive_core::*; /// let mut siv = Cursive::new(); /// /// siv.add_global_callback('q', |s| s.quit()); /// ``` pub fn add_global_callback>(&mut self, event: E, cb: F) where F: FnMut(&mut Cursive) + 'static, { self.set_on_post_event(event.into(), cb); } /// Registers a callback for ignored events. /// /// This is the same as `add_global_callback`, but can register any `EventTrigger`. pub fn set_on_post_event(&mut self, trigger: E, cb: F) where F: FnMut(&mut Cursive) + 'static, E: Into, { self.root.set_on_event(trigger, crate::immut1!(cb)); } /// Registers a priotity callback. /// /// If an event matches the given trigger, it will not be sent to the view /// tree and will go to the given callback instead. /// /// Note that regular "post-event" callbacks will also be skipped for /// these events. pub fn set_on_pre_event(&mut self, trigger: E, cb: F) where F: FnMut(&mut Cursive) + 'static, E: Into, { self.root.set_on_pre_event(trigger, crate::immut1!(cb)); } /// Registers an inner priority callback. /// /// See [`OnEventView`] for more information. /// /// [`OnEventView`]: crate::views::OnEventView::set_on_pre_event_inner() pub fn set_on_pre_event_inner(&mut self, trigger: E, cb: F) where E: Into, F: Fn(&Event) -> Option + 'static, { self.root .set_on_pre_event_inner(trigger, move |_, event| cb(event)); } /// Registers an inner callback. /// /// See [`OnEventView`] for more information. /// /// [`OnEventView`]: crate::views::OnEventView::set_on_event_inner() pub fn set_on_event_inner(&mut self, trigger: E, cb: F) where E: Into, F: Fn(&Event) -> Option + 'static, { self.root .set_on_event_inner(trigger, move |_, event| cb(event)); } /// Sets the only global callback for the given event. /// /// Any other callback for this event will be removed. /// /// See also [`Cursive::add_global_callback`]. pub fn set_global_callback>(&mut self, event: E, cb: F) where F: FnMut(&mut Cursive) + 'static, { let event = event.into(); self.clear_global_callbacks(event.clone()); self.add_global_callback(event, cb); } /// Fetches the type name of a view in the tree. pub fn debug_name(&mut self, name: &str) -> Option<&'static str> { let mut result = None; self.root.call_on_any( &view::Selector::Name(name), &mut |v: &mut dyn crate::View| result = Some(v.type_name()), ); result } /// Removes any callback tied to the given event. /// /// # Examples /// /// ```rust /// use cursive_core::Cursive; /// let mut siv = Cursive::new(); /// /// siv.add_global_callback('q', |s| s.quit()); /// siv.clear_global_callbacks('q'); /// ``` pub fn clear_global_callbacks(&mut self, event: E) where E: Into, { let event = event.into(); self.root.clear_event(event); } /// This resets the default callbacks. /// /// Currently this mostly includes exiting on Ctrl-C. pub fn reset_default_callbacks(&mut self) { self.set_on_pre_event(Event::CtrlChar('c'), |s| s.quit()); self.set_on_pre_event(Event::Exit, |s| s.quit()); self.set_on_pre_event(Event::WindowResize, |s| s.clear()); } /// Add a layer to the current screen. /// /// # Examples /// /// ```rust /// use cursive_core::{views, Cursive}; /// let mut siv = Cursive::new(); /// /// siv.add_layer(views::TextView::new("Hello world!")); /// ``` pub fn add_layer(&mut self, view: T) where T: IntoBoxedView, { self.screen_mut().add_layer(view); } /// Adds a new full-screen layer to the current screen. /// /// Fullscreen layers have no shadow. pub fn add_fullscreen_layer(&mut self, view: T) where T: IntoBoxedView, { self.screen_mut().add_fullscreen_layer(view); } /// Convenient method to remove a layer from the current screen. pub fn pop_layer(&mut self) -> Option> { self.screen_mut().pop_layer() } /// Convenient stub forwarding layer repositioning. pub fn reposition_layer( &mut self, layer: LayerPosition, position: Position, ) { self.screen_mut().reposition_layer(layer, position); } /// Processes an event. /// /// * If the menubar is active, it will be handled the event. /// * The view tree will be handled the event. /// * If ignored, global_callbacks will be checked for this event. pub fn on_event(&mut self, event: Event) { if let Event::Mouse { event, position, .. } = event { if event.grabs_focus() && !self.menubar.autohide && !self.menubar.has_submenu() && position.y == 0 { self.select_menubar(); } } if self.menubar.receive_events() { self.menubar.on_event(event).process(self); } else { let offset = if self.menubar.autohide { 0 } else { 1 }; let result = View::on_event(&mut self.root, event.relativized((0, offset))); if let EventResult::Consumed(Some(cb)) = result { cb(self); } } } /// Try to process a single callback. /// /// Returns `true` if a callback was processed, `false` if there was /// nothing to process. pub(crate) fn process_callback(&mut self) -> bool { match self.cb_source.try_recv() { Ok(cb) => { cb(self); true } _ => false, } } /// Returns `true` until [`quit(&mut self)`] is called. /// /// [`quit(&mut self)`]: #method.quit pub fn is_running(&self) -> bool { self.running } /// Runs a dummy event loop. /// /// Initializes a dummy backend for the event loop. pub fn run_dummy(&mut self) { self.run_with(backend::Dummy::init) } /// Returns a new runner on the given backend. /// /// Used to manually control the event loop. In most cases, running /// `Cursive::run_with` will be easier. /// /// The runner will borrow `self`; when dropped, it will clear out the /// terminal, and the cursive instance will be ready for another run if /// needed. pub fn runner( &mut self, backend: Box, ) -> CursiveRunner<&mut Self> { CursiveRunner::new(self, backend) } /// Returns a new runner on the given backend. /// /// Used to manually control the event loop. In most cases, running /// `Cursive::run_with` will be easier. /// /// The runner will embed `self`; when dropped, it will clear out the /// terminal, and the cursive instance will be dropped as well. pub fn into_runner( self, backend: Box, ) -> CursiveRunner { CursiveRunner::new(self, backend) } /// Initialize the backend and runs the event loop. /// /// Used for infallible backend initializers. pub fn run_with(&mut self, backend_init: F) where F: FnOnce() -> Box, { self.try_run_with::<(), _>(|| Ok(backend_init())).unwrap(); } /// Initialize the backend and runs the event loop. /// /// Returns an error if initializing the backend fails. pub fn try_run_with(&mut self, backend_init: F) -> Result<(), E> where F: FnOnce() -> Result, E>, { self.running = true; let mut runner = self.runner(backend_init()?); runner.run(); Ok(()) } /// Stops the event loop. pub fn quit(&mut self) { self.running = false; } /// Does not do anything. pub fn noop(&mut self) { // foo } /// Dump the current state of the Cursive root. /// /// *It will clear out this `Cursive` instance* and save everything, including: /// * The view tree /// * Callbacks /// * Menubar /// * User data /// * Callback sink /// /// After calling this, the cursive object will be as if newly created. pub fn dump(&mut self) -> crate::Dump { let (cb_sink, cb_source) = crossbeam_channel::unbounded(); let root = views::OnEventView::new(views::ScreensView::single_screen( views::StackView::new(), )); Dump { cb_sink: std::mem::replace(&mut self.cb_sink, cb_sink), cb_source: std::mem::replace(&mut self.cb_source, cb_source), fps: self.fps.take(), menubar: std::mem::take(&mut self.menubar), root_view: std::mem::replace(&mut self.root, root), theme: std::mem::take(&mut self.theme), user_data: std::mem::replace(&mut self.user_data, Box::new(())), } } /// Restores the state from a previous dump. /// /// This will discard everything from this `Cursive` instance. /// In particular: /// * All current views will be dropped, replaced by the dump. /// * All callbacks will be replaced. /// * Menubar will be replaced. /// * User Data will be replaced. /// * The callback channel will be replaced - any previous call to /// `cb_sink` on this instance will be disconnected. pub fn restore(&mut self, dump: Dump) { self.cb_sink = dump.cb_sink; self.cb_source = dump.cb_source; self.fps = dump.fps; self.menubar = dump.menubar; self.root = dump.root_view; self.theme = dump.theme; self.user_data = dump.user_data; self.clear(); } } cursive_core-0.3.5/src/cursive_run.rs000064400000000000000000000154451046102023000160330ustar 00000000000000use crate::{backend, event::Event, theme, Cursive, Vec2}; use std::borrow::{Borrow, BorrowMut}; use std::time::Duration; // How long we wait between two empty input polls const INPUT_POLL_DELAY_MS: u64 = 30; /// Event loop runner for a cursive instance. /// /// You can get one from `Cursive::runner`, then either call `.run()`, or /// manually `.step()`. /// /// The `C` type is usually either `Cursive` or `&mut Cursive`. pub struct CursiveRunner { siv: C, backend: Box, boring_frame_count: u32, // Last layer sizes of the stack view. // If it changed, clear the screen. last_sizes: Vec, } impl std::ops::Deref for CursiveRunner where C: Borrow, { type Target = Cursive; fn deref(&self) -> &Cursive { self.siv.borrow() } } impl std::ops::DerefMut for CursiveRunner where C: BorrowMut, { fn deref_mut(&mut self) -> &mut Cursive { self.siv.borrow_mut() } } impl CursiveRunner { /// Creates a new cursive runner wrapper. pub fn new(siv: C, backend: Box) -> Self { CursiveRunner { siv, backend, boring_frame_count: 0, last_sizes: Vec::new(), } } /// Returns the size of the screen, in characters. fn screen_size(&self) -> Vec2 { self.backend.screen_size() } /// Clean out the terminal and get back the wrapped object. pub fn into_inner(self) -> C { self.siv } } impl CursiveRunner where C: BorrowMut, { fn layout(&mut self) { let size = self.screen_size(); self.siv.borrow_mut().layout(size); } // Process any backend-requiring calls accumulated by the Cursive root. fn process_pending_backend_calls(&mut self) { let calls = std::mem::take(&mut self.backend_calls); for call in calls { (call)(&mut *self.backend); } } fn draw(&mut self) { let sizes = self.screen().layer_sizes(); if self.last_sizes != sizes { // TODO: Maybe we only need to clear if the _max_ size differs? // Or if the positions change? self.clear(); self.last_sizes = sizes; } if self.needs_clear { self.backend.clear( self.current_theme().palette[theme::PaletteColor::Background], ); self.needs_clear = false; } let size = self.screen_size(); self.siv.borrow_mut().draw(size, &*self.backend); } /// Performs the first half of `Self::step()`. /// /// This is an advanced method for fine-tuned manual stepping; /// you probably want [`run`][1] or [`step`][2]. /// /// This processes any pending event or callback. After calling this, /// you will want to call [`post_events`][3] with the result from this /// function. /// /// Returns `true` if an event or callback was received, /// and `false` otherwise. /// /// [1]: CursiveRunner::run() /// [2]: CursiveRunner::step() /// [3]: CursiveRunner::post_events() pub fn process_events(&mut self) -> bool { // Things are boring if nothing significant happened. let mut boring = true; // First, handle all available input while let Some(event) = self.backend.poll_event() { boring = false; self.on_event(event); self.process_pending_backend_calls(); if !self.is_running() { return true; } } // Then, handle any available callback while self.process_callback() { boring = false; if !self.is_running() { return true; } } !boring } /// Performs the second half of `Self::step()`. /// /// This is an advanced method for fine-tuned manual stepping; /// you probably want [`run`][1] or [`step`][2]. /// /// You should call this after [`process_events`][3]. /// /// [1]: CursiveRunner::run() /// [2]: CursiveRunner::step() /// [3]: CursiveRunner::process_events() pub fn post_events(&mut self, received_something: bool) { let boring = !received_something; // How many times should we try if it's still boring? // Total duration will be INPUT_POLL_DELAY_MS * repeats // So effectively fps = 1000 / INPUT_POLL_DELAY_MS / repeats if !boring || self .fps() .map(|fps| 1000 / INPUT_POLL_DELAY_MS as u32 / fps.get()) .map(|repeats| self.boring_frame_count >= repeats) .unwrap_or(false) { // We deserve to draw something! if boring { // We're only here because of a timeout. self.on_event(Event::Refresh); self.process_pending_backend_calls(); } self.refresh(); } if boring { std::thread::sleep(Duration::from_millis(INPUT_POLL_DELAY_MS)); self.boring_frame_count += 1; } } /// Refresh the screen with the current view tree state. pub fn refresh(&mut self) { self.boring_frame_count = 0; // Do we need to redraw everytime? // Probably, actually. // TODO: Do we need to re-layout everytime? self.layout(); // TODO: Do we need to redraw every view every time? // (Is this getting repetitive? :p) self.draw(); self.backend.refresh(); } /// Return the name of the backend used. /// /// Mostly used for debugging. pub fn backend_name(&self) -> &str { self.backend.name() } /// Performs a single step from the event loop. /// /// Useful if you need tighter control on the event loop. /// Otherwise, [`run(&mut self)`] might be more convenient. /// /// Returns `true` if an input event or callback was received /// during this step, and `false` otherwise. /// /// [`run(&mut self)`]: #method.run pub fn step(&mut self) -> bool { let received_something = self.process_events(); self.post_events(received_something); received_something } /// Runs the event loop. /// /// It will wait for user input (key presses) /// and trigger callbacks accordingly. /// /// Internally, it calls [`step(&mut self)`] until [`quit(&mut self)`] is /// called. /// /// After this function returns, you can call it again and it will start a /// new loop. /// /// [`step(&mut self)`]: #method.step /// [`quit(&mut self)`]: #method.quit pub fn run(&mut self) { self.refresh(); // And the big event loop begins! while self.is_running() { self.step(); } } } cursive_core-0.3.5/src/direction.rs000064400000000000000000000235561046102023000154510ustar 00000000000000//! Direction-related structures. //! //! This module defines two main concepts: [`Orientation`] and [`Direction`]. //! //! [`Orientation`]: enum.Orientation.html //! [`Direction`]: enum.Direction.html //! //! ### Orientation //! //! `Orientation` is a simple enum that can take two values: //! `Horizontal` or `Vertical`. //! //! ### Direction //! //! `Direction` is a bit more complex, and can be of two kinds: //! //! * Absolute direction: left, up, right, or down //! * Relative direction: front or back. Its actual direction depends on the //! orientation. //! //! Usually, "front" refers to the "forward" direction, or the "next" //! element. For example, for a vertical `LinearLayout`, "front" would refer //! to the "down" direction. //! //! This is mostly relevant when referring to change of focus. Hitting the //! `Tab` key would usually cycle focus in the "front" direction, while //! using the arrow keys would use absolute directions instead. use crate::Vec2; use crate::XY; /// Describes a vertical or horizontal orientation for a view. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Orientation { /// Horizontal orientation Horizontal, /// Vertical orientation Vertical, } impl Orientation { /// Returns a `XY(Horizontal, Vertical)`. pub fn pair() -> XY { XY::new(Orientation::Horizontal, Orientation::Vertical) } /// Returns the component of `v` corresponding to this orientation. /// /// (`Horizontal` will return the x value, /// and `Vertical` will return the y value.) pub fn get(self, v: &XY) -> T { v.get(self).clone() } /// Returns the other orientation. #[must_use] pub fn swap(self) -> Self { match self { Orientation::Horizontal => Orientation::Vertical, Orientation::Vertical => Orientation::Horizontal, } } /// Returns a mutable reference to the component of the given vector /// corresponding to this orientation. /// /// # Examples /// ```rust /// # use cursive_core::XY; /// # use cursive_core::direction::Orientation; /// let o = Orientation::Horizontal; /// let mut xy = XY::new(1, 2); /// *o.get_ref(&mut xy) = 42; /// /// assert_eq!(xy, XY::new(42, 2)); /// ``` pub fn get_ref(self, v: &mut XY) -> &mut T { match self { Orientation::Horizontal => &mut v.x, Orientation::Vertical => &mut v.y, } } /// Takes an iterator on sizes, and stack them in the current orientation, /// returning the size of the required bounding box. /// /// * For an horizontal view, returns `(Sum(x), Max(y))`. /// * For a vertical view, returns `(Max(x), Sum(y))`. pub fn stack>(self, iter: T) -> Vec2 { match self { Orientation::Horizontal => { iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(&b)) } Orientation::Vertical => { iter.fold(Vec2::zero(), |a, b| a.stack_vertical(&b)) } } } /// Creates a new `Vec2` with `main_axis` in `self`'s axis, and /// `second_axis` for the other axis. /// /// # Examples /// /// ```rust /// # use cursive_core::direction::Orientation; /// # use cursive_core::Vec2; /// let o = Orientation::Horizontal; /// let vec = o.make_vec(1, 2); /// /// assert_eq!(vec, Vec2::new(1, 2)); /// /// let o = Orientation::Vertical; /// let vec = o.make_vec(1, 2); /// /// assert_eq!(vec, Vec2::new(2, 1)); /// ``` pub fn make_vec(self, main_axis: usize, second_axis: usize) -> Vec2 { let mut result = Vec2::zero(); *self.get_ref(&mut result) = main_axis; *self.swap().get_ref(&mut result) = second_axis; result } } /// Represents a direction, either absolute or orientation-dependent. /// /// * Absolute directions are Up, Down, Left, and Right. /// * Relative directions are Front and Back. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Direction { /// An absolute direction. Abs(Absolute), /// A direction relative to the current orientation. Rel(Relative), } impl Direction { /// Returns the relative direction for the given orientation. /// /// Some combination have no corresponding relative position. For example, /// `Direction::Abs(Up)` means nothing for `Orientation::Horizontal`. pub fn relative(self, orientation: Orientation) -> Option { match self { Direction::Abs(abs) => abs.relative(orientation), Direction::Rel(rel) => Some(rel), } } /// Returns the absolute direction in the given `orientation`. pub fn absolute(self, orientation: Orientation) -> Absolute { match self { Direction::Abs(abs) => abs, Direction::Rel(rel) => rel.absolute(orientation), } } /// Returns the direction opposite `self`. #[must_use] pub fn opposite(self) -> Self { match self { Direction::Abs(abs) => Direction::Abs(abs.opposite()), Direction::Rel(rel) => Direction::Rel(rel.swap()), } } /// Shortcut to create `Direction::Rel(Relative::Back)` pub fn back() -> Self { Direction::Rel(Relative::Back) } /// Shortcut to create `Direction::Rel(Relative::Front)` pub fn front() -> Self { Direction::Rel(Relative::Front) } /// Shortcut to create `Direction::Abs(Absolute::Left)` pub fn left() -> Self { Direction::Abs(Absolute::Left) } /// Shortcut to create `Direction::Abs(Absolute::Right)` pub fn right() -> Self { Direction::Abs(Absolute::Right) } /// Shortcut to create `Direction::Abs(Absolute::Up)` pub fn up() -> Self { Direction::Abs(Absolute::Up) } /// Shortcut to create `Direction::Abs(Absolute::Down)` pub fn down() -> Self { Direction::Abs(Absolute::Down) } /// Shortcut to create `Direction::Abs(Absolute::None)` pub fn none() -> Self { Direction::Abs(Absolute::None) } } /// Direction relative to an orientation. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Relative { // TODO: handle right-to-left? (Arabic, ...) /// Front relative direction. /// /// * Horizontally, this means `Left` /// * Vertically, this means `Up` Front, /// Back relative direction. /// /// * Horizontally, this means `Right` /// * Vertically, this means `Down`. Back, } impl Relative { /// Returns the absolute direction in the given `orientation`. pub fn absolute(self, orientation: Orientation) -> Absolute { match (orientation, self) { (Orientation::Horizontal, Relative::Front) => Absolute::Left, (Orientation::Horizontal, Relative::Back) => Absolute::Right, (Orientation::Vertical, Relative::Front) => Absolute::Up, (Orientation::Vertical, Relative::Back) => Absolute::Down, } } /// Picks one of the two values in a tuple. /// /// First one is `self` is `Front`, second one if `self` is `Back`. pub fn pick(self, (front, back): (T, T)) -> T { match self { Relative::Front => front, Relative::Back => back, } } /// Returns the other relative direction. #[must_use] pub fn swap(self) -> Self { match self { Relative::Front => Relative::Back, Relative::Back => Relative::Front, } } /// Returns the relative position of `a` to `b`. /// /// If `a < b`, it would be `Front`. /// If `a > b`, it would be `Back`. /// If `a == b`, returns `None`. pub fn a_to_b(a: usize, b: usize) -> Option { use std::cmp::Ordering; match a.cmp(&b) { Ordering::Less => Some(Relative::Front), Ordering::Greater => Some(Relative::Back), Ordering::Equal => None, } } } /// Absolute direction (up, down, left, right). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Absolute { /// Left Left, /// Up Up, /// Right Right, /// Down Down, /// No real direction. /// /// Used when the "direction" is accross layers for instance. None, } impl Absolute { /// Returns the relative direction for the given orientation. /// /// Returns `None` when the direction does not apply to the given /// orientation (ex: `Left` and `Vertical`). pub fn relative(self, orientation: Orientation) -> Option { match (orientation, self) { (Orientation::Horizontal, Absolute::Left) | (Orientation::Vertical, Absolute::Up) => Some(Relative::Front), (Orientation::Horizontal, Absolute::Right) | (Orientation::Vertical, Absolute::Down) => Some(Relative::Back), _ => None, } } /// Returns the direction opposite `self`. #[must_use] pub fn opposite(self) -> Self { match self { Absolute::Left => Absolute::Right, Absolute::Right => Absolute::Left, Absolute::Up => Absolute::Down, Absolute::Down => Absolute::Up, Absolute::None => Absolute::None, } } /// Splits this absolute direction into an orientation and relative direction. /// /// For example, `Right` will give `(Horizontal, Back)`. pub fn split(self) -> (Orientation, Relative) { match self { Absolute::Left => (Orientation::Horizontal, Relative::Front), Absolute::Right => (Orientation::Horizontal, Relative::Back), Absolute::Up => (Orientation::Vertical, Relative::Front), Absolute::Down => (Orientation::Vertical, Relative::Back), // TODO: Remove `Absolute::None` Absolute::None => panic!("None direction not supported here"), } } } cursive_core-0.3.5/src/div.rs000064400000000000000000000003461046102023000142430ustar 00000000000000use num::Num; /// Integer division that rounds up. pub fn div_up(p: T, q: T) -> T where T: Num + Clone, { let d = p.clone() / q.clone(); if p % q == T::zero() { d } else { T::one() + d } } cursive_core-0.3.5/src/dump.rs000064400000000000000000000012351046102023000144240ustar 00000000000000use crate::{theme::Theme, views, Cursive}; use crossbeam_channel::{Receiver, Sender}; use std::any::Any; use std::num::NonZeroU32; type Callback = dyn FnOnce(&mut Cursive) + Send; /// Represents a dump of everything from a `Cursive` instance. /// /// See [`Cursive::dump()`](../cursive.html#method.dump) pub struct Dump { pub(crate) cb_sink: Sender>, pub(crate) cb_source: Receiver>, pub(crate) fps: Option, pub(crate) menubar: views::Menubar, pub(crate) root_view: views::OnEventView>, pub(crate) theme: Theme, pub(crate) user_data: Box, } cursive_core-0.3.5/src/event.rs000064400000000000000000000366771046102023000146220ustar 00000000000000//! User-input events and their effects. //! //! * Every user input the application receives is converted to an [`Event`]. //! * Each event is then given to the root, and descends the view tree down to //! the view currently in focus, through the [`on_event`] method. //! * If the view consumes the event, it may return a callback to be //! executed. //! * Otherwise, it ignores the event, and the view parent can in turn //! choose to consume it or not. //! * If no view consumes the event, the [global callback] table is checked. //! //! [`Event`]: enum.Event.html //! [`on_event`]: ../trait.View.html#method.on_event //! [global callback]: ../struct.Cursive.html#method.add_global_callback use crate::Cursive; use crate::Vec2; use std::any::Any; use std::ops::Deref; use std::rc::Rc; /// Callback is a function that can be triggered by an event. /// It has a mutable access to the cursive root. /// /// It is meant to be stored in views. #[derive(Clone)] pub struct Callback(Rc); // TODO: remove the Box when Box -> Rc is possible /// A callback that can be run on `&mut dyn View`. /// /// It is meant to be used as parameter in `View::call_on_any`, and not much else. pub type AnyCb<'a> = &'a mut dyn FnMut(&mut dyn crate::view::View); /// A trigger that only selects some types of events. /// /// It is meant to be stored in views. pub struct EventTrigger { trigger: Box bool>, tag: Box, } trait AnyTag: Any + std::fmt::Debug { fn as_any(&self) -> &dyn Any; } impl AnyTag for T where T: Any + std::fmt::Debug, { fn as_any(&self) -> &dyn Any { self } } impl EventTrigger { /// Create a new `EventTrigger` using the given function as filter. pub fn from_fn(f: F) -> Self where F: 'static + Fn(&Event) -> bool, { EventTrigger::from_fn_and_tag(f, "free function") } /// Create a new `EventTrigger`. pub fn from_fn_and_tag(f: F, tag: T) -> Self where F: 'static + Fn(&Event) -> bool, T: Any + std::fmt::Debug, { let tag = Box::new(tag); let trigger = Box::new(f); EventTrigger { trigger, tag } } /// Check if this trigger has the given tag. /// /// # Examples /// /// ```rust /// use cursive_core::event::{Event, EventTrigger}; /// /// let event = Event::CtrlChar('c'); /// let trigger: EventTrigger = event.clone().into(); /// assert!( /// trigger.has_tag(&event), /// "Trigger does not recognize its own tag." /// ); /// ``` pub fn has_tag(&self, tag: &T) -> bool { (*self.tag) .as_any() .downcast_ref::() .map_or(false, |t| tag == t) } /// Checks if this trigger applies to the given `Event`. pub fn apply(&self, event: &Event) -> bool { (self.trigger)(event) } /// Returns an `EventTrigger` that only accepts arrow keys. /// /// Only bare arrow keys without modifiers (Shift, Ctrl, Alt) will be accepted. pub fn arrows() -> Self { Self::from_fn_and_tag( |e| { matches!( e, Event::Key(Key::Left) | Event::Key(Key::Down) | Event::Key(Key::Up) | Event::Key(Key::Right) ) }, "arrows", ) } /// Returns an `EventTrigger` that only accepts mouse events. pub fn mouse() -> Self { Self::from_fn_and_tag(|e| matches!(e, Event::Mouse { .. }), "mouse") } /// Returns an `EventTrigger` that accepts any event. pub fn any() -> Self { Self::from_fn_and_tag(|_| true, "any") } /// Returns an `EventTrigger` that doesn't accept any event. pub fn none() -> Self { Self::from_fn_and_tag(|_| false, "none") } /// Returns an `EventTrigger` that applies if either `self` or `other` applies. #[must_use] pub fn or(self, other: O) -> Self where O: Into, { let other = other.into(); let self_trigger = self.trigger; let other_trigger = other.trigger; let tag = (self.tag, "or", other.tag); Self::from_fn_and_tag( move |e| self_trigger(e) || other_trigger(e), tag, ) } } impl From for EventTrigger { fn from(event: Event) -> Self { let tag = event.clone(); Self::from_fn_and_tag(move |e| *e == event, tag) } } impl From for EventTrigger { fn from(c: char) -> Self { Self::from(Event::from(c)) } } impl From for EventTrigger { fn from(k: Key) -> Self { Self::from(Event::from(k)) } } impl From for EventTrigger where F: 'static + Fn(&Event) -> bool, { fn from(f: F) -> Self { Self::from_fn(f) } } impl Callback { /// Wraps the given function into a `Callback` object. pub fn from_fn(f: F) -> Self where F: 'static + Fn(&mut Cursive), { Callback(Rc::new(move |siv| { f(siv); })) } /// Wrap a `FnMut` into a `Callback` object. /// /// If this methods tries to call itself, nested calls will be no-ops. pub fn from_fn_mut(f: F) -> Self where F: 'static + FnMut(&mut Cursive), { Self::from_fn(crate::immut1!(f)) } /// Wrap a `FnOnce` into a `Callback` object. /// /// After being called once, the callback will become a no-op. pub fn from_fn_once(f: F) -> Self where F: 'static + FnOnce(&mut Cursive), { Self::from_fn_mut(crate::once1!(f)) } /// Returns a dummy callback that doesn't run anything. pub fn dummy() -> Self { Callback::from_fn(|_| ()) } } impl Deref for Callback { type Target = dyn Fn(&mut Cursive) + 'static; fn deref(&self) -> &Self::Target { &*self.0 } } impl From> for Callback { fn from(f: Rc) -> Self { Callback(f) } } impl From> for Callback { fn from(f: Box) -> Self { Callback(Rc::from(f)) } } /// Answer to an event notification. /// The event can be consumed or ignored. pub enum EventResult { /// The event was ignored. The parent can keep handling it. Ignored, /// The event was consumed. An optional callback to run is attached. Consumed(Option), // TODO: make this a FnOnce? } impl EventResult { /// Convenient method to create `Consumed(Some(f))` pub fn with_cb(f: F) -> Self where F: 'static + Fn(&mut Cursive), { EventResult::Consumed(Some(Callback::from_fn(f))) } /// Convenient method to create `Consumed(Some(f))` /// /// After being called once, the callback will become a no-op. pub fn with_cb_once(f: F) -> Self where F: 'static + FnOnce(&mut Cursive), { EventResult::Consumed(Some(Callback::from_fn_once(f))) } /// Convenient method to create `Consumed(None)` pub fn consumed() -> Self { EventResult::Consumed(None) } /// Returns `true` if `self` is `EventResult::Consumed`. pub fn is_consumed(&self) -> bool { matches!(*self, EventResult::Consumed(_)) } /// Returns `true` if `self` contains a callback. pub fn has_callback(&self) -> bool { matches!(*self, EventResult::Consumed(Some(_))) } /// Process this result if it is a callback. /// /// Does nothing otherwise. pub fn process(self, s: &mut Cursive) { if let EventResult::Consumed(Some(cb)) = self { cb(s); } } /// Returns `self` if it is not `EventResult::Ignored`, otherwise returns `f()`. #[must_use] pub fn or_else(self, f: F) -> Self where F: FnOnce() -> EventResult, { match self { EventResult::Ignored => f(), other => other, } } /// Returns an event result that combines `self` and `other`. #[must_use] pub fn and(self, other: Self) -> Self { match (self, other) { (EventResult::Ignored, result) | (result, EventResult::Ignored) => result, (EventResult::Consumed(None), EventResult::Consumed(cb)) | (EventResult::Consumed(cb), EventResult::Consumed(None)) => { EventResult::Consumed(cb) } ( EventResult::Consumed(Some(cb1)), EventResult::Consumed(Some(cb2)), ) => EventResult::with_cb(move |siv| { (cb1)(siv); (cb2)(siv); }), } } } /// A non-character key on the keyboard #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] pub enum Key { /// Both Enter (or Return) and numpad Enter Enter, /// Tabulation key Tab, /// Backspace key Backspace, /// Escape key Esc, /// Left arrow Left, /// Right arrow Right, /// Up arrow Up, /// Down arrow Down, /// Insert key Ins, /// Delete key Del, /// Home key Home, /// End key End, /// Page Up key PageUp, /// Page Down key PageDown, /// Pause Break key PauseBreak, /// The 5 in the center of the keypad, when numlock is disabled. NumpadCenter, /// F0 key F0, /// F1 key F1, /// F2 key F2, /// F3 key F3, /// F4 key F4, /// F5 key F5, /// F6 key F6, /// F7 key F7, /// F8 key F8, /// F9 key F9, /// F10 key F10, /// F11 key F11, /// F12 key F12, } impl Key { /// Returns the function key corresponding to the given number /// /// 1 -> F1, etc... /// /// # Panics /// /// If `n == 0 || n > 12` pub fn from_f(n: u8) -> Key { match n { 0 => Key::F0, 1 => Key::F1, 2 => Key::F2, 3 => Key::F3, 4 => Key::F4, 5 => Key::F5, 6 => Key::F6, 7 => Key::F7, 8 => Key::F8, 9 => Key::F9, 10 => Key::F10, 11 => Key::F11, 12 => Key::F12, _ => panic!("unknown function key: F{}", n), } } } /// One of the buttons present on the mouse #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] pub enum MouseButton { /// The left button, used for main actions. Left, /// Middle button, probably the wheel. Often pastes text in X11 on linux. Middle, /// The right button, for special actions. Right, /// Fourth button if the mouse supports it. Button4, /// Fifth button if the mouse supports it. Button5, // TODO: handle more buttons? #[doc(hidden)] Other, } /// Represents a possible event sent by the mouse. #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] pub enum MouseEvent { /// A button was pressed. Press(MouseButton), /// A button was released. Release(MouseButton), /// A button is being held. Hold(MouseButton), /// The wheel was moved up. WheelUp, /// The wheel was moved down. WheelDown, } impl MouseEvent { /// Returns the button used by this event, if any. /// /// Returns `None` if `self` is `WheelUp` or `WheelDown`. pub fn button(self) -> Option { match self { MouseEvent::Press(btn) | MouseEvent::Release(btn) | MouseEvent::Hold(btn) => Some(btn), _ => None, } } /// Returns `true` if `self` is an event that can grab focus. /// /// This includes `Press`, `WheelUp` and `WheelDown`. /// /// It does _not_ include `Release` or `Hold`. /// /// It means you should be able to grab a scroll bar, and move the mouse /// away from the view, without actually changing the focus. pub fn grabs_focus(self) -> bool { matches!( self, MouseEvent::Press(_) | MouseEvent::WheelUp | MouseEvent::WheelDown ) } } /// Represents an event as seen by the application. #[derive(PartialEq, Eq, Clone, Hash, Debug)] pub enum Event { /// Event fired when the window is resized. WindowResize, /// Event fired when the view is about to lose focus. FocusLost, /// Event fired regularly when a auto-refresh is set. Refresh, // TODO: have Char(modifier, char) and Key(modifier, key) enums? /// A character was entered (includes numbers, punctuation, ...). Char(char), /// A character was entered with the Ctrl key pressed. CtrlChar(char), /// A character was entered with the Alt key pressed. AltChar(char), /// A non-character key was pressed. Key(Key), /// A non-character key was pressed with the Shift key pressed. Shift(Key), /// A non-character key was pressed with the Alt key pressed. Alt(Key), /// A non-character key was pressed with the Shift and Alt keys pressed. AltShift(Key), /// A non-character key was pressed with the Ctrl key pressed. Ctrl(Key), /// A non-character key was pressed with the Ctrl and Shift keys pressed. CtrlShift(Key), /// A non-character key was pressed with the Ctrl and Alt keys pressed. CtrlAlt(Key), /// A mouse event was sent. Mouse { /// Position of the top-left corner of the view receiving this event. offset: Vec2, /// Position of the mouse when this event was fired. position: Vec2, /// The mouse event itself. event: MouseEvent, }, // TODO: use a backend-dependent type for the unknown values? /// An unknown event was received. Unknown(Vec), // Maybe add a `Custom(Rc)` ? // Having a doc-hidden event prevents people from having exhaustive // matches, allowing us to add events in the future. // // In addition we may not want people to listen to the exit event? #[doc(hidden)] /// The application is about to exit. Exit, } impl Event { /// Returns the position of the mouse, if `self` is a mouse event. pub fn mouse_position(&self) -> Option { if let Event::Mouse { position, .. } = *self { Some(position) } else { None } } /// Returns a mutable reference to the position of the mouse/ /// /// Returns `None` if `self` is not a mouse event. pub fn mouse_position_mut(&mut self) -> Option<&mut Vec2> { if let Event::Mouse { ref mut position, .. } = *self { Some(position) } else { None } } /// Update `self` with the given offset. /// /// If `self` is a mouse event, adds `top_left` to its offset. /// Otherwise, do nothing. pub fn relativize(&mut self, top_left: V) where V: Into, { if let Event::Mouse { ref mut offset, .. } = *self { *offset = *offset + top_left; } } /// Returns a cloned, relativized event. /// /// If `self` is a mouse event, adds `top_left` to its offset. /// Otherwise, returns a simple clone. #[must_use] pub fn relativized(&self, top_left: V) -> Self where V: Into, { let mut result = self.clone(); result.relativize(top_left); result } } impl From for Event { fn from(c: char) -> Event { Event::Char(c) } } impl From for Event { fn from(k: Key) -> Event { Event::Key(k) } } cursive_core-0.3.5/src/lib.rs000064400000000000000000000034371046102023000142330ustar 00000000000000//! # Cursive-core //! //! This library defines the core components for the Cursive TUI. //! //! The main purpose of `cursive-core` is to write third-party libraries to work with Cursive. //! //! If you are building an end-user application, then [`cursive`] is probably what you want. //! //! [`cursive`]: https://docs.rs/cursive #![deny(missing_docs)] #![cfg_attr(feature = "doc-cfg", feature(doc_cfg))] macro_rules! new_default( ($c:ident<$t:ident>) => { impl<$t> Default for $c<$t> { fn default() -> Self { Self::new() } } }; ($c:ident) => { impl Default for $c { fn default() -> Self { Self::new() } } }; ($c:ident<$t:ident: Default>) => { impl <$t> Default for $c<$t> where $t: Default { fn default() -> Self { Self::new($t::default()) } } }; ); /// Re-export crates used in the public API pub mod reexports { pub use ahash; pub use crossbeam_channel; pub use enumset; pub use log; pub use time; #[cfg(feature = "toml")] pub use toml; #[cfg(feature = "ansi")] pub use ansi_parser; } #[macro_use] pub mod utils; #[macro_use] pub mod view; #[macro_use] pub mod views; pub mod align; pub mod backend; pub mod direction; pub mod event; pub mod logger; pub mod menu; pub mod theme; pub mod traits; pub mod vec; mod cursive; mod cursive_run; mod dump; mod printer; mod rect; mod with; mod xy; mod div; pub use self::cursive::{CbSink, Cursive, ScreenId}; pub use self::cursive_run::CursiveRunner; pub use self::dump::Dump; pub use self::printer::Printer; pub use self::rect::Rect; pub use self::vec::Vec2; pub use self::view::View; pub use self::with::With; pub use self::xy::XY; cursive_core-0.3.5/src/logger.rs000064400000000000000000000047441046102023000147460ustar 00000000000000//! Logging utilities. use lazy_static::lazy_static; use std::collections::VecDeque; use std::sync::Mutex; /// Saves all log records in a global deque. /// /// Uses a `DebugView` to access it. pub struct CursiveLogger; static LOGGER: CursiveLogger = CursiveLogger; /// A log record. pub struct Record { /// Log level used for this record pub level: log::Level, /// Time this message was logged pub time: time::OffsetDateTime, /// Message content pub message: String, } lazy_static! { /// Circular buffer for logs. Use it to implement [`DebugView`]. /// /// [`DebugView`]: ../views/struct.DebugView.html pub static ref LOGS: Mutex> = Mutex::new(VecDeque::new()); } /// Log a record in cursive's log queue. pub fn log(record: &log::Record<'_>) { let mut logs = LOGS.lock().unwrap(); // TODO: customize the format? Use colors? Save more info? if logs.len() == logs.capacity() { logs.pop_front(); } logs.push_back(Record { level: record.level(), message: format!("{}", record.args()), time: time::OffsetDateTime::now_local() .unwrap_or_else(|_| time::OffsetDateTime::now_utc()), }); } impl log::Log for CursiveLogger { fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool { true } fn log(&self, record: &log::Record<'_>) { log(record); } fn flush(&self) {} } /// Initialize the Cursive logger. /// /// Make sure this is the only logger your are using. /// /// Use a [`DebugView`](crate::views::DebugView) to see the logs, or use /// [`Cursive::toggle_debug_console()`](crate::Cursive::toggle_debug_console()). pub fn init() { // TODO: Configure the deque size? reserve_logs(1_000); // This will panic if `set_logger` was already called. log::set_logger(&LOGGER).unwrap(); // TODO: read the level from env variable? From argument? log::set_max_level(log::LevelFilter::Trace); } /// Return a logger that stores records in cursive's log queue. /// /// These logs can then be read by a [`DebugView`](crate::views::DebugView). /// /// An easier alternative might be to use [`init()`]. pub fn get_logger() -> CursiveLogger { reserve_logs(1_000); CursiveLogger } /// Adds `n` more entries to cursive's log queue. /// /// Most of the time you don't need to use this directly. /// /// You should call this if you're not using `init()` nor `get_logger()`. pub fn reserve_logs(n: usize) { LOGS.lock().unwrap().reserve(n); } cursive_core-0.3.5/src/menu.rs000064400000000000000000000212561046102023000144300ustar 00000000000000//! Build menu trees. //! //! Menus are a way to arrange many actions in groups of more manageable size. //! //! A menu can be seen as a [`Tree`]. It has a list of children: //! //! * Leaf nodes are made of a label and a callback //! * Sub-trees are made of a label, and another `Tree`. //! * Delimiters are just there to separate groups of related children. //! //! The [menubar] is the main way to show menus. //! //! [`Tree`]: struct.Tree.html //! [menubar]: ../struct.Cursive.html#method.menubar use crate::{event::Callback, Cursive, With}; use std::rc::Rc; /// Root of a menu tree. #[derive(Default, Clone)] pub struct Tree { /// Menu items pub children: Vec, } /// Node in the menu tree. #[derive(Clone)] pub enum Item { /// Actionnable button with a label. Leaf { /// Text displayed for this entry. label: String, /// Callback to run when the entry is selected. cb: Callback, /// Whether this item is enabled. /// /// Disabled items cannot be selected and are displayed grayed out. enabled: bool, }, /// Sub-menu with a label. Subtree { /// Text displayed for this entry. label: String, /// Subtree under this item. tree: Rc, /// Whether this item is enabled. /// /// Disabled items cannot be selected and are displayed grayed out. enabled: bool, }, /// Delimiter without a label. Delimiter, } impl Item { /// Create a new leaf menu item. pub fn leaf(label: S, cb: F) -> Self where S: Into, F: 'static + Fn(&mut Cursive), { let label = label.into(); let cb = Callback::from_fn(cb); let enabled = true; Item::Leaf { label, cb, enabled } } /// Create a new subtree menu item. pub fn subtree(label: S, tree: Tree) -> Self where S: Into, { let label = label.into(); let tree = Rc::new(tree); let enabled = true; Item::Subtree { label, tree, enabled, } } /// Returns the label for this item. /// /// Returns a vertical bar string if `self` is a delimiter. pub fn label(&self) -> &str { match *self { Item::Delimiter => "│", Item::Leaf { ref label, .. } | Item::Subtree { ref label, .. } => { label } } } /// Returns true if this item is enabled. /// /// Only labels and subtrees can be enabled. Delimiters pub fn is_enabled(&self) -> bool { match *self { Item::Leaf { enabled, .. } | Item::Subtree { enabled, .. } => { enabled } Item::Delimiter => false, } } /// Return a disabled version of this item. #[must_use] pub fn disabled(self) -> Self { self.with(Self::disable) } /// Disable this item. /// /// Disabled items cannot be selected and are shown grayed out. /// /// Does not affect delimiters. pub fn disable(&mut self) { if let Item::Leaf { ref mut enabled, .. } | Item::Subtree { ref mut enabled, .. } = self { *enabled = false; } } /// Returns `true` if `self` is a delimiter. pub fn is_delimiter(&self) -> bool { matches!(*self, Item::Delimiter) } /// Returns `true` if `self` is a leaf node. pub fn is_leaf(&self) -> bool { matches!(*self, Item::Leaf { .. }) } /// Returns `true` if `self` is a subtree. pub fn is_subtree(&self) -> bool { matches!(*self, Item::Subtree { .. }) } /// Return a mutable reference to the subtree, if applicable. /// /// Returns `None` if `self` is not a `Item::Subtree`. pub fn as_subtree(&mut self) -> Option<&mut Tree> { match *self { Item::Subtree { ref mut tree, .. } => Some(Rc::make_mut(tree)), _ => None, } } } impl Tree { /// Creates a new, empty tree. pub fn new() -> Self { Self::default() } /// Remove every children from this tree. pub fn clear(&mut self) { self.children.clear(); } /// Inserts an item at the given position. pub fn insert(&mut self, i: usize, item: Item) { self.children.insert(i, item); } /// Inserts a delimiter at the given position. pub fn insert_delimiter(&mut self, i: usize) { self.insert(i, Item::Delimiter); } /// Adds a delimiter to the end of this tree. pub fn add_delimiter(&mut self) { let i = self.children.len(); self.insert_delimiter(i); } /// Adds a delimiter to the end of this tree - chainable variant. #[must_use] pub fn delimiter(self) -> Self { self.with(Self::add_delimiter) } /// Adds a actionnable leaf to the end of this tree. pub fn add_leaf(&mut self, label: S, cb: F) where S: Into, F: 'static + Fn(&mut Cursive), { let i = self.children.len(); self.insert_leaf(i, label, cb); } /// Inserts a leaf at the given position. pub fn insert_leaf(&mut self, i: usize, label: S, cb: F) where S: Into, F: 'static + Fn(&mut Cursive), { let label = label.into(); self.insert( i, Item::Leaf { label, cb: Callback::from_fn(cb), enabled: true, }, ); } /// Adds a actionnable leaf to the end of this tree - chainable variant. #[must_use] pub fn leaf(self, label: S, cb: F) -> Self where S: Into, F: 'static + Fn(&mut Cursive), { self.with(|menu| menu.add_leaf(label, cb)) } /// Inserts a subtree at the given position. pub fn insert_subtree(&mut self, i: usize, label: S, tree: Tree) where S: Into, { let label = label.into(); let tree = Item::Subtree { label, tree: Rc::new(tree), enabled: true, }; self.insert(i, tree); } /// Adds an item to the end of this tree. /// /// Chainable variant. #[must_use] pub fn item(self, item: Item) -> Self { self.with(|s| s.add_item(item)) } /// Adds an item to the end of this tree. pub fn add_item(&mut self, item: Item) { let i = self.children.len(); self.insert(i, item); } /// Adds a submenu to the end of this tree. pub fn add_subtree(&mut self, label: S, tree: Tree) where S: Into, { let i = self.children.len(); self.insert_subtree(i, label, tree); } /// Adds a submenu to the end of this tree - chainable variant. #[must_use] pub fn subtree(self, label: S, tree: Tree) -> Self where S: Into, { self.with(|menu| menu.add_subtree(label, tree)) } /// Looks for the child at the given position. /// /// Returns `None` if `i >= self.len()`. pub fn get_mut(&mut self, i: usize) -> Option<&mut Item> { self.children.get_mut(i) } /// Returns the item at the given position. /// /// Returns `None` if `i > self.len()` or if the item is not a subtree. pub fn get_subtree(&mut self, i: usize) -> Option<&mut Tree> { self.get_mut(i).and_then(Item::as_subtree) } /// Looks for a child with the given label. /// /// Returns `None` if no such label was found. pub fn find_item(&mut self, label: &str) -> Option<&mut Item> { self.children .iter_mut() .find(|child| child.label() == label) } /// Looks for a subtree with the given label. pub fn find_subtree(&mut self, label: &str) -> Option<&mut Tree> { self.children .iter_mut() .filter(|child| child.label() == label) .find_map(Item::as_subtree) } /// Returns the position of a child with the given label. /// /// Returns `None` if no such label was found. pub fn find_position(&mut self, label: &str) -> Option { self.children .iter() .position(|child| child.label() == label) } /// Removes the item at the given position. pub fn remove(&mut self, i: usize) { self.children.remove(i); } /// Returns the number of direct children in this node. /// /// * Includes delimiters. /// * Does not count nested children. pub fn len(&self) -> usize { self.children.len() } /// Returns `true` if this tree has no children. pub fn is_empty(&self) -> bool { self.children.is_empty() } } cursive_core-0.3.5/src/printer.rs000064400000000000000000000526021046102023000151460ustar 00000000000000//! Provide higher-level abstraction to draw things on backends. use crate::backend::Backend; use crate::direction::Orientation; use crate::rect::Rect; use crate::theme::{ BorderStyle, ColorPair, ColorStyle, Effect, PaletteColor, Style, Theme, }; use crate::utils::lines::simple::{prefix, suffix}; use crate::with::With; use crate::Vec2; use enumset::EnumSet; use std::cell::Cell; use std::cmp::min; use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; /// Convenient interface to draw on a subset of the screen. /// /// The printing area is defined by `offset` and `size`.\ /// The content that will be printed is defined by [`Self::content_offset`] /// and [`Self::size`]. /// /// If the printer is asked to print outside of the printing area, /// then the string to be printed shall be truncated without throwing errors. /// Refer to the [`crate::traits::View`] to understand how to change its size. #[derive(Clone)] pub struct Printer<'a, 'b> { /// Offset into the window this printer should start drawing at. /// /// A print request at `x` will really print at `x + offset`. pub offset: Vec2, /// Size of the area we are allowed to draw on. /// /// Anything outside of this should be discarded.\ /// The view being drawn can ingore this, but anything further than that /// will be ignored. pub output_size: Vec2, /// Size allocated to the view. /// /// This should be the same value as the one given in the last call to /// `View::layout`. pub size: Vec2, /// Offset into the view for this printer. /// /// The view being drawn can ignore this, but anything to the top-left of /// this will actually be ignored, so it can be used to skip this part. /// /// A print request `x`, will really print at `x - content_offset`. pub content_offset: Vec2, /// Whether the view to draw is currently focused or not. pub focused: bool, /// Whether the view to draw is currently enabled or not. pub enabled: bool, /// Currently used theme pub theme: &'a Theme, /// Current color pair used by the parent view. current_color: Cell, /// Backend used to actually draw things backend: &'b dyn Backend, } impl<'a, 'b> Printer<'a, 'b> { /// Creates a new printer on the given window. /// /// But nobody needs to know that. #[doc(hidden)] pub fn new>( size: T, theme: &'a Theme, backend: &'b dyn Backend, ) -> Self { let size = size.into(); Printer { offset: Vec2::zero(), content_offset: Vec2::zero(), output_size: size, size, focused: true, enabled: true, theme, backend, current_color: Cell::new(ColorPair::from_256colors(0, 0)), } } /// Clear the screen. /// /// It will discard anything drawn before. /// /// Users rarely need to call this directly. pub fn clear(&self) { self.backend .clear(self.theme.palette[PaletteColor::Background]); } /// Prints some styled text at the given position. pub fn print_styled( &self, start: S, text: crate::utils::span::SpannedStr<'_, Style>, ) where S: Into, { let Vec2 { mut x, y } = start.into(); for span in text.spans() { self.with_style(*span.attr, |printer| { printer.print_with_width((x, y), span.content, |_| span.width); x += span.width; }); } } /// Prints some text at the given position. /// /// # Parameters /// * `start` is the offset used to print the text in the view. /// * `text` is a string to print on the screen. It must be a single line, no line wrapping /// will be done. /// /// # Description /// Prints some text at the given position. /// The text could be truncated if it exceed the [drawing area size](Self::output_size). /// /// # Example /// ```rust /// use cursive::{Printer, Vec2, View, XY}; /// /// pub struct CustomView { /// word: String, /// } /// /// impl CustomView { /// pub fn new() -> Self { /// Self { /// word: String::from("Eh, tu connais Rust?"), /// } /// } /// } /// /// impl View for CustomView { /// fn draw(&self, printer: &Printer<'_, '_>) { /// printer.print(XY::new(0, 0), &self.word); /// } /// } /// ``` // TODO: use &mut self? We don't *need* it, but it may make sense. // We don't want people to start calling prints in parallel? pub fn print>(&self, start: S, text: &str) { self.print_with_width(start, text, UnicodeWidthStr::width); } /// Prints some text, using the given callback to compute width. /// /// Mostly used with [`UnicodeWidthStr::width`]. /// If you already know the width, you can give it as a constant instead. fn print_with_width(&self, start: S, text: &str, width: F) where S: Into, F: FnOnce(&str) -> usize, { // Where we are asked to start printing. Oh boy. It's not that simple. let start = start.into(); // We accept requests between `content_offset` and // `content_offset + output_size`. if !start.strictly_lt(self.output_size + self.content_offset) { return; } // If start < content_offset, part of the text will not be visible. // This is the part of the text that's hidden: // (It should always be smaller than the content offset) let hidden_part = self.content_offset.saturating_sub(start); if hidden_part.y > 0 { // Since we are printing a single line, there's nothing we can do. return; } let mut text_width = width(text); // If we're waaaay too far left, just give up. if hidden_part.x > text_width { return; } let mut text = text; let mut start = start; if hidden_part.x > 0 { // We have to drop hidden_part.x width from the start of the string. // prefix() may be too short if there's a double-width character. // So instead, keep the suffix and drop the prefix. // TODO: use a different prefix method that is *at least* the width // (and not *at most*) let tail = suffix(text.graphemes(true), text_width - hidden_part.x, ""); let skipped_len = text.len() - tail.length; let skipped_width = text_width - tail.width; assert_eq!(text[..skipped_len].width(), skipped_width); // This should be equal most of the time, except when there's a double // character preventing us from splitting perfectly. assert!(skipped_width >= hidden_part.x); // Drop part of the text, and move the cursor correspondingly. text = &text[skipped_len..]; start = start + (skipped_width, 0); text_width -= skipped_width; } assert!(start.fits(self.content_offset)); // What we did before should guarantee that this won't overflow. start = start - self.content_offset; // Do we have enough room for the entire line? let room = self.output_size.x - start.x; if room < text_width { // Drop the end of the text if it's too long // We want the number of CHARACTERS, not bytes. // (Actually we want the "width" of the string, see unicode-width) let prefix_len = prefix(text.graphemes(true), room, "").length; text = &text[..prefix_len]; assert!(text.width() <= room); } let start = start + self.offset; self.backend.print_at(start, text); } /// Prints a vertical line using the given character. pub fn print_vline>( &self, start: T, height: usize, c: &str, ) { let start = start.into(); // Here again, we can abort if we're trying to print too far right or // too low. if !start.strictly_lt(self.output_size + self.content_offset) { return; } // hidden_part describes how far to the top left of the viewport we are. let hidden_part = self.content_offset.saturating_sub(start); if hidden_part.x > 0 || hidden_part.y >= height { // We're printing a single column, so we can't do much here. return; } // Skip `hidden_part` let start = start + hidden_part; assert!(start.fits(self.content_offset)); let height = height - hidden_part.y; // What we did before ensures this won't overflow. let start = start - self.content_offset; // Don't go overboard let height = min(height, self.output_size.y - start.y); let start = start + self.offset; for y in 0..height { self.backend.print_at(start + (0, y), c); } } /// Prints a line using the given character. pub fn print_line>( &self, orientation: Orientation, start: T, length: usize, c: &str, ) { match orientation { Orientation::Vertical => self.print_vline(start, length, c), Orientation::Horizontal => self.print_hline(start, length, c), } } /// Prints a horizontal line using the given character. pub fn print_hline>(&self, start: T, width: usize, c: &str) { let start = start.into(); // Nothing to be done if the start if too far to the bottom/right if !start.strictly_lt(self.output_size + self.content_offset) { return; } let hidden_part = self.content_offset.saturating_sub(start); if hidden_part.y > 0 || hidden_part.x >= width { // We're printing a single line, so we can't do much here. return; } // Skip `hidden_part` let start = start + hidden_part; assert!(start.fits(self.content_offset)); let width = width - hidden_part.x; // Don't go too far let start = start - self.content_offset; // Don't write too much if we're close to the end let repetitions = min(width, self.output_size.x - start.x) / c.width(); let start = start + self.offset; self.backend.print_at_rep(start, repetitions, c); } /// Returns the color currently used by the parent view. pub fn current_color(&self) -> ColorPair { self.current_color.get() } /// Call the given closure with a colored printer, /// that will apply the given color on prints. /// /// # Examples /// /// ```rust /// # use cursive_core::Printer; /// # use cursive_core::theme; /// # use cursive_core::backend; /// # let b = backend::Dummy::init(); /// # let t = theme::load_default(); /// # let printer = Printer::new((6,4), &t, &*b); /// printer.with_color(theme::ColorStyle::highlight(), |printer| { /// printer.print((0, 0), "This text is highlighted!"); /// }); /// ``` pub fn with_color(&self, c: ColorStyle, f: F) where F: FnOnce(&Printer), { let old = self.current_color.get(); let new = c.resolve(&self.theme.palette, old); self.current_color.set(new); self.backend.set_color(new); f(self); self.backend.set_color(old); self.current_color.set(old) } /// Call the given closure with a styled printer, /// that will apply the given style on prints. pub fn with_style(&self, style: T, f: F) where F: FnOnce(&Printer), T: Into