mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Merge branch 'master' into scroll
This commit is contained in:
commit
5e1956b737
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ tags
|
|||||||
.ctags
|
.ctags
|
||||||
*.bk
|
*.bk
|
||||||
TODO.txt
|
TODO.txt
|
||||||
|
*.rustfmt
|
||||||
|
@ -17,7 +17,6 @@ repository = "gyscos/Cursive"
|
|||||||
enum-map = "0.2.24"
|
enum-map = "0.2.24"
|
||||||
enumset = "0.3.3"
|
enumset = "0.3.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
num = "0.1"
|
|
||||||
owning_ref = "0.3.3"
|
owning_ref = "0.3.3"
|
||||||
toml = "0.4"
|
toml = "0.4"
|
||||||
unicode-segmentation = "1.0"
|
unicode-segmentation = "1.0"
|
||||||
@ -25,6 +24,10 @@ unicode-width = "0.1"
|
|||||||
xi-unicode = "0.1.0"
|
xi-unicode = "0.1.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
|
||||||
|
[dependencies.num]
|
||||||
|
default-features = false
|
||||||
|
version = "0.1"
|
||||||
|
|
||||||
[dependencies.maplit]
|
[dependencies.maplit]
|
||||||
optional = true
|
optional = true
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::{Cursive, Printer};
|
|
||||||
use cursive::theme::{Color, ColorStyle};
|
use cursive::theme::{Color, ColorStyle};
|
||||||
use cursive::view::Boxable;
|
use cursive::view::Boxable;
|
||||||
use cursive::views::Canvas;
|
use cursive::views::Canvas;
|
||||||
|
use cursive::{Cursive, Printer};
|
||||||
|
|
||||||
// This example will draw a colored square with a gradient.
|
// This example will draw a colored square with a gradient.
|
||||||
//
|
//
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::views::{Dialog, TextView};
|
use cursive::views::{Dialog, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Creates the cursive root - required for every application.
|
// Creates the cursive root - required for every application.
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::traits::*;
|
use cursive::traits::*;
|
||||||
use cursive::views::{Dialog, EditView, TextView};
|
use cursive::views::{Dialog, EditView, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut siv = Cursive::default();
|
let mut siv = Cursive::default();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::views::TextView;
|
use cursive::views::TextView;
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut siv = Cursive::default();
|
let mut siv = Cursive::default();
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::align::HAlign;
|
use cursive::align::HAlign;
|
||||||
use cursive::traits::*;
|
use cursive::traits::*;
|
||||||
use cursive::views::{Dialog, DummyView, LinearLayout, TextView};
|
use cursive::views::{Dialog, DummyView, LinearLayout, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
// This example uses a LinearLayout to stick multiple views next to each other.
|
// This example uses a LinearLayout to stick multiple views next to each other.
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::traits::*;
|
use cursive::traits::*;
|
||||||
use cursive::views::{Checkbox, Dialog, EditView, LinearLayout, ListView,
|
use cursive::views::{
|
||||||
SelectView, TextView};
|
Checkbox, Dialog, EditView, LinearLayout, ListView, SelectView, TextView,
|
||||||
|
};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
// This example uses a ListView.
|
// This example uses a ListView.
|
||||||
//
|
//
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::{Cursive, Printer};
|
|
||||||
use cursive::traits::*;
|
use cursive::traits::*;
|
||||||
use cursive::vec::Vec2;
|
use cursive::vec::Vec2;
|
||||||
|
use cursive::{Cursive, Printer};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::align::HAlign;
|
use cursive::align::HAlign;
|
||||||
use cursive::view::Boxable;
|
|
||||||
use cursive::views::{Dialog, Panel, TextView};
|
use cursive::views::{Dialog, Panel, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Read some long text from a file.
|
// Read some long text from a file.
|
||||||
@ -18,11 +17,10 @@ fn main() {
|
|||||||
// and will adapt to the terminal size.
|
// and will adapt to the terminal size.
|
||||||
siv.add_fullscreen_layer(
|
siv.add_fullscreen_layer(
|
||||||
Dialog::around(Panel::new(TextView::new(content)))
|
Dialog::around(Panel::new(TextView::new(content)))
|
||||||
|
.title("Unicode and wide-character support")
|
||||||
// This is the alignment for the button
|
// This is the alignment for the button
|
||||||
.h_align(HAlign::Center)
|
.h_align(HAlign::Center)
|
||||||
.button("Quit", |s| s.quit())
|
.button("Quit", |s| s.quit()),
|
||||||
.title("Unicode and wide-character support")
|
|
||||||
.full_screen(),
|
|
||||||
);
|
);
|
||||||
// Show a popup on top of the view.
|
// Show a popup on top of the view.
|
||||||
siv.add_layer(Dialog::info(
|
siv.add_layer(Dialog::info(
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::theme::BaseColor;
|
use cursive::theme::BaseColor;
|
||||||
use cursive::theme::Color;
|
use cursive::theme::Color;
|
||||||
use cursive::theme::Effect;
|
use cursive::theme::Effect;
|
||||||
use cursive::theme::Style;
|
use cursive::theme::Style;
|
||||||
use cursive::utils::markup::StyledString;
|
use cursive::utils::markup::StyledString;
|
||||||
use cursive::views::{Dialog, TextView};
|
use cursive::views::{Dialog, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut siv = Cursive::default();
|
let mut siv = Cursive::default();
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::event::Key;
|
use cursive::event::Key;
|
||||||
use cursive::menu::MenuTree;
|
use cursive::menu::MenuTree;
|
||||||
use cursive::traits::*;
|
use cursive::traits::*;
|
||||||
use cursive::views::Dialog;
|
use cursive::views::Dialog;
|
||||||
|
use cursive::Cursive;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
// This examples shows how to configure and use a menubar at the top of the
|
// This examples shows how to configure and use a menubar at the top of the
|
||||||
|
@ -3,13 +3,13 @@ extern crate rand;
|
|||||||
|
|
||||||
mod game;
|
mod game;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::Printer;
|
|
||||||
use cursive::direction::Direction;
|
use cursive::direction::Direction;
|
||||||
use cursive::event::{Event, EventResult, MouseButton, MouseEvent};
|
use cursive::event::{Event, EventResult, MouseButton, MouseEvent};
|
||||||
use cursive::theme::{BaseColor, Color, ColorStyle};
|
use cursive::theme::{BaseColor, Color, ColorStyle};
|
||||||
use cursive::vec::Vec2;
|
use cursive::vec::Vec2;
|
||||||
use cursive::views::{Button, Dialog, LinearLayout, Panel, SelectView};
|
use cursive::views::{Button, Dialog, LinearLayout, Panel, SelectView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
use cursive::Printer;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut siv = Cursive::default();
|
let mut siv = Cursive::default();
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::traits::*;
|
use cursive::traits::*;
|
||||||
use cursive::view::{Offset, Position};
|
use cursive::view::{Offset, Position};
|
||||||
use cursive::views::{Dialog, OnEventView, TextView};
|
use cursive::views::{Dialog, OnEventView, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
// This example modifies a view after creation.
|
// This example modifies a view after creation.
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::view::Position;
|
use cursive::view::Position;
|
||||||
use cursive::views::LayerPosition;
|
use cursive::views::LayerPosition;
|
||||||
use cursive::views::TextView;
|
use cursive::views::TextView;
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
/// Moves top layer by the specified amount
|
/// Moves top layer by the specified amount
|
||||||
fn move_top(c: &mut Cursive, x_in: isize, y_in: isize) {
|
fn move_top(c: &mut Cursive, x_in: isize, y_in: isize) {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::traits::*;
|
use cursive::traits::*;
|
||||||
use cursive::views::{Button, Dialog, LinearLayout, ProgressBar, TextView};
|
|
||||||
use cursive::utils::Counter;
|
use cursive::utils::Counter;
|
||||||
|
use cursive::views::{Button, Dialog, LinearLayout, ProgressBar, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::views::{Dialog, DummyView, LinearLayout, RadioGroup};
|
use cursive::views::{Dialog, DummyView, LinearLayout, RadioGroup};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
// This example uses radio buttons.
|
// This example uses radio buttons.
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::view::{Boxable, Identifiable};
|
use cursive::view::{Boxable, Identifiable};
|
||||||
use cursive::views::{Dialog, EditView, LinearLayout, TextView};
|
use cursive::views::{Dialog, EditView, LinearLayout, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
// This example shows a way to access multiple views at the same time.
|
// This example shows a way to access multiple views at the same time.
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::align::HAlign;
|
use cursive::align::HAlign;
|
||||||
use cursive::event::EventResult;
|
use cursive::event::EventResult;
|
||||||
use cursive::traits::*;
|
use cursive::traits::*;
|
||||||
use cursive::views::{Dialog, OnEventView, SelectView, TextView};
|
use cursive::views::{Dialog, OnEventView, SelectView, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
// We'll use a SelectView here.
|
// We'll use a SelectView here.
|
||||||
//
|
//
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::traits::*;
|
use cursive::traits::*;
|
||||||
use cursive::views::{Dialog, SliderView};
|
use cursive::views::{Dialog, SliderView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut siv = Cursive::default();
|
let mut siv = Cursive::default();
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::theme::{Color, PaletteColor, Theme};
|
use cursive::theme::{Color, PaletteColor, Theme};
|
||||||
use cursive::views::TextView;
|
use cursive::views::TextView;
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
// This example sets the background color to the terminal default.
|
// This example sets the background color to the terminal default.
|
||||||
//
|
//
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::event::{Event, Key};
|
use cursive::event::{Event, Key};
|
||||||
use cursive::traits::*;
|
use cursive::traits::*;
|
||||||
use cursive::views::{Dialog, EditView, OnEventView, TextArea};
|
use cursive::views::{Dialog, EditView, OnEventView, TextArea};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut siv = Cursive::default();
|
let mut siv = Cursive::default();
|
||||||
@ -37,10 +37,10 @@ fn main() {
|
|||||||
.min_width(10),
|
.min_width(10),
|
||||||
)
|
)
|
||||||
.button("Ok", |s| {
|
.button("Ok", |s| {
|
||||||
let text = s.call_on_id(
|
let text =
|
||||||
"edit",
|
s.call_on_id("edit", |view: &mut EditView| {
|
||||||
|view: &mut EditView| view.get_content(),
|
view.get_content()
|
||||||
).unwrap();
|
}).unwrap();
|
||||||
find(s, &text);
|
find(s, &text);
|
||||||
})
|
})
|
||||||
.dismiss_button("Cancel"),
|
.dismiss_button("Cancel"),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::views::{Dialog, TextView};
|
use cursive::views::{Dialog, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut siv = Cursive::default();
|
let mut siv = Cursive::default();
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::theme::{BaseColor, BorderStyle, Color, ColorStyle};
|
use cursive::theme::{BaseColor, BorderStyle, Color, ColorStyle};
|
||||||
use cursive::views::{Dialog, EditView, LinearLayout, TextView};
|
use cursive::views::{Dialog, EditView, LinearLayout, TextView};
|
||||||
|
use cursive::Cursive;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut siv = Cursive::default();
|
let mut siv = Cursive::default();
|
||||||
|
@ -3,10 +3,10 @@ extern crate pretty_bytes;
|
|||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use cursive::Cursive;
|
|
||||||
use cursive::traits::{Boxable, With};
|
use cursive::traits::{Boxable, With};
|
||||||
use cursive::utils;
|
use cursive::utils;
|
||||||
use cursive::views::{Canvas, Dialog, LinearLayout, ProgressBar};
|
use cursive::views::{Canvas, Dialog, LinearLayout, ProgressBar};
|
||||||
|
use cursive::Cursive;
|
||||||
use pretty_bytes::converter::convert;
|
use pretty_bytes::converter::convert;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
@ -31,10 +31,7 @@ fn main() {
|
|||||||
let meta = std::fs::metadata(&source).unwrap();
|
let meta = std::fs::metadata(&source).unwrap();
|
||||||
// If possible, read the file size to have a progress bar.
|
// If possible, read the file size to have a progress bar.
|
||||||
let len = meta.len();
|
let len = meta.len();
|
||||||
(
|
(Some(source), if len > 0 { Some(len) } else { None })
|
||||||
Some(source),
|
|
||||||
if len > 0 { Some(len) } else { None },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
None => (None, None),
|
None => (None, None),
|
||||||
};
|
};
|
||||||
@ -68,9 +65,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// When we're done, shut down the application
|
// When we're done, shut down the application
|
||||||
cb_sink
|
cb_sink.send(Box::new(|s: &mut Cursive| s.quit())).unwrap();
|
||||||
.send(Box::new(|s: &mut Cursive| s.quit()))
|
|
||||||
.unwrap();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add a single view: progress status
|
// Add a single view: progress status
|
||||||
|
@ -156,7 +156,8 @@ impl Backend {
|
|||||||
let _alt = (mevent.bstate & ncurses::BUTTON_ALT as mmask_t) != 0;
|
let _alt = (mevent.bstate & ncurses::BUTTON_ALT as mmask_t) != 0;
|
||||||
let _ctrl = (mevent.bstate & ncurses::BUTTON_CTRL as mmask_t) != 0;
|
let _ctrl = (mevent.bstate & ncurses::BUTTON_CTRL as mmask_t) != 0;
|
||||||
|
|
||||||
mevent.bstate &= !(ncurses::BUTTON_SHIFT | ncurses::BUTTON_ALT
|
mevent.bstate &= !(ncurses::BUTTON_SHIFT
|
||||||
|
| ncurses::BUTTON_ALT
|
||||||
| ncurses::BUTTON_CTRL)
|
| ncurses::BUTTON_CTRL)
|
||||||
as mmask_t;
|
as mmask_t;
|
||||||
|
|
||||||
|
@ -117,7 +117,8 @@ impl Backend {
|
|||||||
let _alt = (mevent.bstate & pancurses::BUTTON_ALT as mmask_t) != 0;
|
let _alt = (mevent.bstate & pancurses::BUTTON_ALT as mmask_t) != 0;
|
||||||
let _ctrl = (mevent.bstate & pancurses::BUTTON_CTRL as mmask_t) != 0;
|
let _ctrl = (mevent.bstate & pancurses::BUTTON_CTRL as mmask_t) != 0;
|
||||||
|
|
||||||
mevent.bstate &= !(pancurses::BUTTON_SHIFT | pancurses::BUTTON_ALT
|
mevent.bstate &= !(pancurses::BUTTON_SHIFT
|
||||||
|
| pancurses::BUTTON_ALT
|
||||||
| pancurses::BUTTON_CTRL) as mmask_t;
|
| pancurses::BUTTON_CTRL) as mmask_t;
|
||||||
|
|
||||||
let make_event = |event| Event::Mouse {
|
let make_event = |event| Event::Mouse {
|
||||||
|
@ -105,7 +105,7 @@ impl Cursive {
|
|||||||
pub fn new(backend: Box<backend::Backend>) -> Self {
|
pub fn new(backend: Box<backend::Backend>) -> Self {
|
||||||
let theme = theme::load_default();
|
let theme = theme::load_default();
|
||||||
// theme.activate(&mut backend);
|
// theme.activate(&mut backend);
|
||||||
// let theme = theme::load_theme("assets/style.toml").unwrap();
|
// let theme = theme::load_toml("assets/style.toml").unwrap();
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
@ -283,8 +283,8 @@ impl Cursive {
|
|||||||
/// Loads a theme from the given string content.
|
/// Loads a theme from the given string content.
|
||||||
///
|
///
|
||||||
/// Content must be valid toml.
|
/// Content must be valid toml.
|
||||||
pub fn load_theme(&mut self, content: &str) -> Result<(), theme::Error> {
|
pub fn load_toml(&mut self, content: &str) -> Result<(), theme::Error> {
|
||||||
self.set_theme(try!(theme::load_theme(content)));
|
self.set_theme(try!(theme::load_toml(content)));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,7 +670,8 @@ impl Cursive {
|
|||||||
event, position, ..
|
event, position, ..
|
||||||
} = event
|
} = event
|
||||||
{
|
{
|
||||||
if event.grabs_focus() && !self.menubar.autohide
|
if event.grabs_focus()
|
||||||
|
&& !self.menubar.autohide
|
||||||
&& !self.menubar.has_submenu()
|
&& !self.menubar.has_submenu()
|
||||||
&& position.y == 0
|
&& position.y == 0
|
||||||
{
|
{
|
||||||
|
@ -17,7 +17,7 @@ use with::With;
|
|||||||
///
|
///
|
||||||
/// The part of the content it will print is defined by `content_offset`
|
/// The part of the content it will print is defined by `content_offset`
|
||||||
/// and `size`.
|
/// and `size`.
|
||||||
pub struct Printer<'a> {
|
pub struct Printer<'a, 'b> {
|
||||||
/// Offset into the window this printer should start drawing at.
|
/// Offset into the window this printer should start drawing at.
|
||||||
///
|
///
|
||||||
/// A print request at `x` will really print at `x + offset`.
|
/// A print request at `x` will really print at `x + offset`.
|
||||||
@ -46,10 +46,10 @@ pub struct Printer<'a> {
|
|||||||
pub theme: &'a Theme,
|
pub theme: &'a Theme,
|
||||||
|
|
||||||
/// Backend used to actually draw things
|
/// Backend used to actually draw things
|
||||||
backend: &'a Backend,
|
backend: &'b Backend,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Clone for Printer<'a> {
|
impl<'a, 'b> Clone for Printer<'a, 'b> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Printer {
|
Printer {
|
||||||
offset: self.offset,
|
offset: self.offset,
|
||||||
@ -63,13 +63,13 @@ impl<'a> Clone for Printer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Printer<'a> {
|
impl<'a, 'b> Printer<'a, 'b> {
|
||||||
/// Creates a new printer on the given window.
|
/// Creates a new printer on the given window.
|
||||||
///
|
///
|
||||||
/// But nobody needs to know that.
|
/// But nobody needs to know that.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn new<T: Into<Vec2>>(
|
pub fn new<T: Into<Vec2>>(
|
||||||
size: T, theme: &'a Theme, backend: &'a Backend,
|
size: T, theme: &'a Theme, backend: &'b Backend,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let size = size.into();
|
let size = size.into();
|
||||||
Printer {
|
Printer {
|
||||||
@ -298,6 +298,24 @@ impl<'a> Printer<'a> {
|
|||||||
self.backend.unset_effect(effect);
|
self.backend.unset_effect(effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Call the given closure with a modified printer
|
||||||
|
/// that will apply the given theme on prints.
|
||||||
|
pub fn with_theme<F>(&self, theme: &Theme, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&Printer),
|
||||||
|
{
|
||||||
|
let new_printer = Printer {
|
||||||
|
offset: self.offset,
|
||||||
|
size: self.size,
|
||||||
|
focused: self.focused,
|
||||||
|
theme: theme,
|
||||||
|
backend: self.backend,
|
||||||
|
output_size: self.output_size,
|
||||||
|
content_offset: self.content_offset,
|
||||||
|
};
|
||||||
|
f(&new_printer);
|
||||||
|
}
|
||||||
|
|
||||||
/// Call the given closure with a modified printer
|
/// Call the given closure with a modified printer
|
||||||
/// that will apply each given effect on prints.
|
/// that will apply each given effect on prints.
|
||||||
pub fn with_effects<F>(&self, effects: EnumSet<Effect>, f: F)
|
pub fn with_effects<F>(&self, effects: EnumSet<Effect>, f: F)
|
||||||
|
@ -114,6 +114,13 @@ impl Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a string into a color.
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
/// * `"red"` becomes `Color::Dark(BaseColor::Red)`
|
||||||
|
/// * `"light green"` becomes `Color::Light(BaseColor::Green)`
|
||||||
|
/// * `"default"` becomes `Color::TerminalDefault`
|
||||||
|
/// * `"#123456"` becomes `Color::Rgb(0x12, 0x34, 0x56)`
|
||||||
pub(crate) fn parse(value: &str) -> Option<Self> {
|
pub(crate) fn parse(value: &str) -> Option<Self> {
|
||||||
Some(match value {
|
Some(match value {
|
||||||
"black" => Color::Dark(BaseColor::Black),
|
"black" => Color::Dark(BaseColor::Black),
|
||||||
@ -149,11 +156,15 @@ impl Color {
|
|||||||
let r = load_hex(&value[0..l]) * multiplier;
|
let r = load_hex(&value[0..l]) * multiplier;
|
||||||
let g = load_hex(&value[l..2 * l]) * multiplier;
|
let g = load_hex(&value[l..2 * l]) * multiplier;
|
||||||
let b = load_hex(&value[2 * l..3 * l]) * multiplier;
|
let b = load_hex(&value[2 * l..3 * l]) * multiplier;
|
||||||
|
|
||||||
Some(Color::Rgb(r as u8, g as u8, b as u8))
|
Some(Color::Rgb(r as u8, g as u8, b as u8))
|
||||||
} else if value.len() == 3 {
|
} else if value.len() == 3 {
|
||||||
// RGB values between 0 and 5 maybe?
|
// RGB values between 0 and 5 maybe?
|
||||||
|
// Like 050 for green
|
||||||
let rgb: Vec<_> =
|
let rgb: Vec<_> =
|
||||||
value.chars().map(|c| c as i16 - '0' as i16).collect();
|
value.chars().map(|c| c as i16 - '0' as i16).collect();
|
||||||
|
|
||||||
|
assert_eq!(rgb.len(), 3);
|
||||||
if rgb.iter().all(|&i| i >= 0 && i < 6) {
|
if rgb.iter().all(|&i| i >= 0 && i < 6) {
|
||||||
Some(Color::RgbLowRes(
|
Some(Color::RgbLowRes(
|
||||||
rgb[0] as u8,
|
rgb[0] as u8,
|
||||||
|
@ -169,7 +169,7 @@ pub use self::color::{BaseColor, Color};
|
|||||||
pub use self::color_pair::ColorPair;
|
pub use self::color_pair::ColorPair;
|
||||||
pub use self::color_style::{ColorStyle, ColorType};
|
pub use self::color_style::{ColorStyle, ColorType};
|
||||||
pub use self::effect::Effect;
|
pub use self::effect::Effect;
|
||||||
pub use self::palette::{default_palette, Palette, PaletteColor};
|
pub use self::palette::{Palette, PaletteColor};
|
||||||
pub use self::style::Style;
|
pub use self::style::Style;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
@ -193,13 +193,13 @@ impl Default for Theme {
|
|||||||
Theme {
|
Theme {
|
||||||
shadow: true,
|
shadow: true,
|
||||||
borders: BorderStyle::Simple,
|
borders: BorderStyle::Simple,
|
||||||
palette: default_palette(),
|
palette: Palette::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
fn load(&mut self, table: &toml::value::Table) {
|
fn load_toml(&mut self, table: &toml::value::Table) {
|
||||||
if let Some(&toml::Value::Boolean(shadow)) = table.get("shadow") {
|
if let Some(&toml::Value::Boolean(shadow)) = table.get("shadow") {
|
||||||
self.shadow = shadow;
|
self.shadow = shadow;
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ impl Theme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(&toml::Value::Table(ref table)) = table.get("colors") {
|
if let Some(&toml::Value::Table(ref table)) = table.get("colors") {
|
||||||
palette::load_table(&mut self.palette, table);
|
palette::load_toml(&mut self.palette, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,15 +244,15 @@ pub fn load_theme_file<P: AsRef<Path>>(filename: P) -> Result<Theme, Error> {
|
|||||||
content
|
content
|
||||||
};
|
};
|
||||||
|
|
||||||
load_theme(&content)
|
load_toml(&content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a theme string and sets it as active.
|
/// Loads a theme string and sets it as active.
|
||||||
pub fn load_theme(content: &str) -> Result<Theme, Error> {
|
pub fn load_toml(content: &str) -> Result<Theme, Error> {
|
||||||
let table = toml::de::from_str(content)?;
|
let table = toml::de::from_str(content)?;
|
||||||
|
|
||||||
let mut theme = Theme::default();
|
let mut theme = Theme::default();
|
||||||
theme.load(&table);
|
theme.load_toml(&table);
|
||||||
|
|
||||||
Ok(theme)
|
Ok(theme)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@ use super::Color;
|
|||||||
use enum_map::EnumMap;
|
use enum_map::EnumMap;
|
||||||
use toml;
|
use toml;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
/// Color configuration for the application.
|
/// Color configuration for the application.
|
||||||
///
|
///
|
||||||
/// Assign each color role an actual color.
|
/// Assign each color role an actual color.
|
||||||
@ -11,17 +14,122 @@ use toml;
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use cursive::theme;
|
/// # use cursive::theme::Palette;
|
||||||
/// use cursive::theme::PaletteColor::*;
|
/// use cursive::theme::PaletteColor::*;
|
||||||
/// use cursive::theme::Color::*;
|
/// use cursive::theme::Color::*;
|
||||||
/// use cursive::theme::BaseColor::*;
|
/// use cursive::theme::BaseColor::*;
|
||||||
///
|
///
|
||||||
/// let mut palette = theme::default_palette();
|
/// let mut palette = Palette::default();
|
||||||
///
|
///
|
||||||
/// assert_eq!(palette[Background], Dark(Blue));
|
/// assert_eq!(palette[Background], Dark(Blue));
|
||||||
/// palette[Shadow] = Light(Red);
|
/// palette[Shadow] = Light(Red);
|
||||||
/// ```
|
/// ```
|
||||||
pub type Palette = EnumMap<PaletteColor, Color>;
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
|
pub struct Palette {
|
||||||
|
basic: EnumMap<PaletteColor, Color>,
|
||||||
|
custom: HashMap<String, PaletteNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A node in the palette tree.
|
||||||
|
///
|
||||||
|
/// This describes a value attached to a custom keyword in the palette.
|
||||||
|
///
|
||||||
|
/// This can either be a color, or a nested namespace with its own mapping.
|
||||||
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
|
pub enum PaletteNode {
|
||||||
|
/// A single color.
|
||||||
|
Color(Color),
|
||||||
|
/// A group of values bundled in the same namespace.
|
||||||
|
///
|
||||||
|
/// Namespaces can be merged in the palette with `Palette::merge`.
|
||||||
|
Namespace(HashMap<String, PaletteNode>),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic usage: only use basic colors
|
||||||
|
impl Index<PaletteColor> for Palette {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn index(&self, palette_color: PaletteColor) -> &Color {
|
||||||
|
&self.basic[palette_color]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can alter existing color if needed (but why?...)
|
||||||
|
impl IndexMut<PaletteColor> for Palette {
|
||||||
|
fn index_mut(&mut self, palette_color: PaletteColor) -> &mut Color {
|
||||||
|
&mut self.basic[palette_color]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Palette {
|
||||||
|
/// Returns a custom color from this palette.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the given key was not found.
|
||||||
|
pub fn custom<'a>(&'a self, key: &str) -> Option<&'a Color> {
|
||||||
|
self.custom.get(key).and_then(|node| {
|
||||||
|
if let &PaletteNode::Color(ref color) = node {
|
||||||
|
Some(color)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new palette where the given namespace has been merged.
|
||||||
|
///
|
||||||
|
/// All values in the namespace will override previous values.
|
||||||
|
pub fn merge(&self, namespace: &str) -> Palette {
|
||||||
|
let mut result = self.clone();
|
||||||
|
|
||||||
|
if let Some(&PaletteNode::Namespace(ref palette)) =
|
||||||
|
self.custom.get(namespace)
|
||||||
|
{
|
||||||
|
// Merge `result` and `palette`
|
||||||
|
for (key, value) in palette.iter() {
|
||||||
|
match *value {
|
||||||
|
PaletteNode::Color(color) => result.set_color(key, color),
|
||||||
|
PaletteNode::Namespace(ref map) => {
|
||||||
|
result.add_namespace(key, map.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the color for the given key.
|
||||||
|
///
|
||||||
|
/// This will update either the basic palette or the custom values.
|
||||||
|
pub fn set_color(&mut self, key: &str, color: Color) {
|
||||||
|
use theme::PaletteColor::*;
|
||||||
|
|
||||||
|
match key {
|
||||||
|
"background" => self.basic[Background] = color,
|
||||||
|
"shadow" => self.basic[Shadow] = color,
|
||||||
|
"view" => self.basic[View] = color,
|
||||||
|
"primary" => self.basic[Primary] = color,
|
||||||
|
"secondary" => self.basic[Secondary] = color,
|
||||||
|
"tertiary" => self.basic[Tertiary] = color,
|
||||||
|
"title_primary" => self.basic[TitlePrimary] = color,
|
||||||
|
"title_secondary" => self.basic[TitleSecondary] = color,
|
||||||
|
"highlight" => self.basic[Highlight] = color,
|
||||||
|
"highlight_inactive" => self.basic[HighlightInactive] = color,
|
||||||
|
other => {
|
||||||
|
self.custom
|
||||||
|
.insert(other.to_string(), PaletteNode::Color(color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a color namespace to this palette.
|
||||||
|
pub fn add_namespace(
|
||||||
|
&mut self, key: &str, namespace: HashMap<String, PaletteNode>,
|
||||||
|
) {
|
||||||
|
self.custom
|
||||||
|
.insert(key.to_string(), PaletteNode::Namespace(namespace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the default palette for a cursive application.
|
/// Returns the default palette for a cursive application.
|
||||||
///
|
///
|
||||||
@ -35,57 +143,84 @@ pub type Palette = EnumMap<PaletteColor, Color>;
|
|||||||
/// * `TitleSecondary` => `Dark(Yellow)`
|
/// * `TitleSecondary` => `Dark(Yellow)`
|
||||||
/// * `Highlight` => `Dark(Red)`
|
/// * `Highlight` => `Dark(Red)`
|
||||||
/// * `HighlightInactive` => `Dark(Blue)`
|
/// * `HighlightInactive` => `Dark(Blue)`
|
||||||
pub fn default_palette() -> Palette {
|
impl Default for Palette {
|
||||||
use self::PaletteColor::*;
|
fn default() -> Palette {
|
||||||
use theme::BaseColor::*;
|
use self::PaletteColor::*;
|
||||||
use theme::Color::*;
|
use theme::BaseColor::*;
|
||||||
|
use theme::Color::*;
|
||||||
|
|
||||||
enum_map!{
|
Palette {
|
||||||
Background => Dark(Blue),
|
basic: enum_map!{
|
||||||
Shadow => Dark(Black),
|
Background => Dark(Blue),
|
||||||
View => Dark(White),
|
Shadow => Dark(Black),
|
||||||
Primary => Dark(Black),
|
View => Dark(White),
|
||||||
Secondary => Dark(Blue),
|
Primary => Dark(Black),
|
||||||
Tertiary => Dark(White),
|
Secondary => Dark(Blue),
|
||||||
TitlePrimary => Dark(Red),
|
Tertiary => Dark(White),
|
||||||
TitleSecondary => Dark(Yellow),
|
TitlePrimary => Dark(Red),
|
||||||
Highlight => Dark(Red),
|
TitleSecondary => Dark(Yellow),
|
||||||
HighlightInactive => Dark(Blue),
|
Highlight => Dark(Red),
|
||||||
|
HighlightInactive => Dark(Blue),
|
||||||
|
},
|
||||||
|
custom: HashMap::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Iterate over a toml
|
||||||
|
fn iterate_toml<'a>(
|
||||||
|
table: &'a toml::value::Table,
|
||||||
|
) -> impl Iterator<Item = (&'a str, PaletteNode)> + 'a {
|
||||||
|
table.iter().flat_map(|(key, value)| {
|
||||||
|
let node = match value {
|
||||||
|
toml::Value::Table(table) => {
|
||||||
|
// This should define a new namespace
|
||||||
|
// Treat basic colors as simple string.
|
||||||
|
// We'll convert them back in the merge method.
|
||||||
|
let map = iterate_toml(table)
|
||||||
|
.map(|(key, value)| (key.to_string(), value))
|
||||||
|
.collect();
|
||||||
|
// Should we only return something if it's non-empty?
|
||||||
|
Some(PaletteNode::Namespace(map))
|
||||||
|
}
|
||||||
|
toml::Value::Array(colors) => {
|
||||||
|
// This should be a list of colors - just pick the first valid one.
|
||||||
|
colors
|
||||||
|
.iter()
|
||||||
|
.flat_map(toml::Value::as_str)
|
||||||
|
.flat_map(Color::parse)
|
||||||
|
.map(PaletteNode::Color)
|
||||||
|
.next()
|
||||||
|
}
|
||||||
|
toml::Value::String(color) => {
|
||||||
|
// This describe a new color - easy!
|
||||||
|
Color::parse(color).map(PaletteNode::Color)
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
// Other - error?
|
||||||
|
debug!(
|
||||||
|
"Found unexpected value in theme: {} = {:?}",
|
||||||
|
key, other
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
node.map(|node| (key.as_str(), node))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Fills `palette` with the colors from the given `table`.
|
/// Fills `palette` with the colors from the given `table`.
|
||||||
pub(crate) fn load_table(palette: &mut Palette, table: &toml::value::Table) {
|
pub(crate) fn load_toml(palette: &mut Palette, table: &toml::value::Table) {
|
||||||
// TODO: use serde for that?
|
// TODO: use serde for that?
|
||||||
// Problem: toml-rs doesn't do well with Enums...
|
// Problem: toml-rs doesn't do well with Enums...
|
||||||
load_color(
|
|
||||||
&mut palette[PaletteColor::Background],
|
for (key, value) in iterate_toml(table) {
|
||||||
table.get("background"),
|
match value {
|
||||||
);
|
PaletteNode::Color(color) => palette.set_color(key, color),
|
||||||
load_color(&mut palette[PaletteColor::Shadow], table.get("shadow"));
|
PaletteNode::Namespace(map) => palette.add_namespace(key, map),
|
||||||
load_color(&mut palette[PaletteColor::View], table.get("view"));
|
}
|
||||||
load_color(&mut palette[PaletteColor::Primary], table.get("primary"));
|
}
|
||||||
load_color(
|
|
||||||
&mut palette[PaletteColor::Secondary],
|
|
||||||
table.get("secondary"),
|
|
||||||
);
|
|
||||||
load_color(&mut palette[PaletteColor::Tertiary], table.get("tertiary"));
|
|
||||||
load_color(
|
|
||||||
&mut palette[PaletteColor::TitlePrimary],
|
|
||||||
table.get("title_primary"),
|
|
||||||
);
|
|
||||||
load_color(
|
|
||||||
&mut palette[PaletteColor::TitleSecondary],
|
|
||||||
table.get("title_secondary"),
|
|
||||||
);
|
|
||||||
load_color(
|
|
||||||
&mut palette[PaletteColor::Highlight],
|
|
||||||
table.get("highlight"),
|
|
||||||
);
|
|
||||||
load_color(
|
|
||||||
&mut palette[PaletteColor::HighlightInactive],
|
|
||||||
table.get("highlight_inactive"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Color entry in a palette.
|
/// Color entry in a palette.
|
||||||
@ -121,25 +256,3 @@ impl PaletteColor {
|
|||||||
palette[self]
|
palette[self]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses `value` and fills `target` if it's a valid color.
|
|
||||||
fn load_color(target: &mut Color, value: Option<&toml::Value>) -> bool {
|
|
||||||
if let Some(value) = value {
|
|
||||||
match *value {
|
|
||||||
toml::Value::String(ref value) => {
|
|
||||||
if let Some(color) = Color::parse(value) {
|
|
||||||
*target = color;
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
toml::Value::Array(ref array) => {
|
|
||||||
array.iter().any(|item| load_color(target, Some(item)))
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -61,18 +61,19 @@ where
|
|||||||
// `current_width` is the width of everything
|
// `current_width` is the width of everything
|
||||||
// before the next token, including any space.
|
// before the next token, including any space.
|
||||||
let mut current_width = 0;
|
let mut current_width = 0;
|
||||||
let sum: usize = iter.take_while(|token| {
|
let sum: usize =
|
||||||
let width = token.width();
|
iter.take_while(|token| {
|
||||||
if current_width + width > available_width {
|
let width = token.width();
|
||||||
false
|
if current_width + width > available_width {
|
||||||
} else {
|
false
|
||||||
// Include the delimiter after this token.
|
} else {
|
||||||
current_width += width;
|
// Include the delimiter after this token.
|
||||||
current_width += delimiter_width;
|
current_width += width;
|
||||||
true
|
current_width += delimiter_width;
|
||||||
}
|
true
|
||||||
}).map(|token| token.len() + delimiter_len)
|
}
|
||||||
.sum();
|
}).map(|token| token.len() + delimiter_len)
|
||||||
|
.sum();
|
||||||
|
|
||||||
// We counted delimiter once too many times,
|
// We counted delimiter once too many times,
|
||||||
// but only if the iterator was non empty.
|
// but only if the iterator was non empty.
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
use super::segment::Segment;
|
use super::segment::Segment;
|
||||||
|
|
||||||
/// Non-splittable piece of text.
|
/// Non-splittable piece of text.
|
||||||
|
///
|
||||||
|
/// It is made of a list of segments of text.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
|
/// Total width of this chunk.
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
|
|
||||||
|
/// This is the segments this chunk contains.
|
||||||
pub segments: Vec<Segment>,
|
pub segments: Vec<Segment>,
|
||||||
|
|
||||||
|
/// Hard stops are non-optional line breaks (newlines).
|
||||||
pub hard_stop: bool,
|
pub hard_stop: bool,
|
||||||
|
|
||||||
|
/// If a chunk of text ends in a space, it can be compressed a bit.
|
||||||
|
///
|
||||||
|
/// (We can omit the space if it would result in a perfect fit.)
|
||||||
pub ends_with_space: bool,
|
pub ends_with_space: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ where
|
|||||||
end,
|
end,
|
||||||
}],
|
}],
|
||||||
hard_stop: false,
|
hard_stop: false,
|
||||||
ends_with_space: false,
|
ends_with_space: false, // should we?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -147,7 +147,19 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let width = chunks.iter().map(|c| c.width).sum();
|
// We can know text was wrapped if the stop was optional,
|
||||||
|
// and there's more coming.
|
||||||
|
let text_wrap = !chunks.last().map(|c| c.hard_stop).unwrap_or(true)
|
||||||
|
&& self.iter.peek().is_some();
|
||||||
|
|
||||||
|
// If we had to break a line in two, then at least pretent we're
|
||||||
|
// taking the full width.
|
||||||
|
let width = if text_wrap {
|
||||||
|
self.width
|
||||||
|
} else {
|
||||||
|
chunks.iter().map(|c| c.width).sum()
|
||||||
|
};
|
||||||
|
|
||||||
assert!(width <= self.width);
|
assert!(width <= self.width);
|
||||||
|
|
||||||
// Concatenate all segments
|
// Concatenate all segments
|
||||||
|
@ -73,10 +73,12 @@ impl<'a> Iterator for Parser<'a> {
|
|||||||
self.stack.push(Style::from(Effect::Italic))
|
self.stack.push(Style::from(Effect::Italic))
|
||||||
}
|
}
|
||||||
Tag::Header(level) => {
|
Tag::Header(level) => {
|
||||||
return Some(self.literal(format!(
|
return Some(
|
||||||
"{} ",
|
self.literal(format!(
|
||||||
header(level as usize)
|
"{} ",
|
||||||
)))
|
header(level as usize)
|
||||||
|
)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Tag::Rule => return Some(self.literal("---")),
|
Tag::Rule => return Some(self.literal("---")),
|
||||||
Tag::BlockQuote => return Some(self.literal("> ")),
|
Tag::BlockQuote => return Some(self.literal("> ")),
|
||||||
|
@ -46,7 +46,8 @@ impl<T: View> Finder for T {
|
|||||||
*result_ref =
|
*result_ref =
|
||||||
v.downcast_mut::<V>().map(|v| callback(v));
|
v.downcast_mut::<V>().map(|v| callback(v));
|
||||||
} else if v.is::<IdView<V>>() {
|
} else if v.is::<IdView<V>>() {
|
||||||
*result_ref = v.downcast_mut::<IdView<V>>()
|
*result_ref = v
|
||||||
|
.downcast_mut::<IdView<V>>()
|
||||||
.and_then(|v| v.with_view_mut(callback));
|
.and_then(|v| v.with_view_mut(callback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,8 @@ impl<T: View> ViewWrapper for BoxView<T> {
|
|||||||
let req = self.size.zip_map(req, SizeConstraint::available);
|
let req = self.size.zip_map(req, SizeConstraint::available);
|
||||||
let child_size = self.view.required_size(req);
|
let child_size = self.view.required_size(req);
|
||||||
|
|
||||||
let result = self.size
|
let result = self
|
||||||
|
.size
|
||||||
.zip_map(child_size.zip(req), SizeConstraint::result);
|
.zip_map(child_size.zip(req), SizeConstraint::result);
|
||||||
|
|
||||||
debug!("{:?}", result);
|
debug!("{:?}", result);
|
||||||
|
@ -340,7 +340,8 @@ impl Dialog {
|
|||||||
// Current horizontal position of the next button we'll draw.
|
// Current horizontal position of the next button we'll draw.
|
||||||
|
|
||||||
// Sum of the sizes + len-1 for margins
|
// Sum of the sizes + len-1 for margins
|
||||||
let width = self.buttons
|
let width = self
|
||||||
|
.buttons
|
||||||
.iter()
|
.iter()
|
||||||
.map(|button| button.button.size.x)
|
.map(|button| button.button.size.x)
|
||||||
.sum::<usize>()
|
.sum::<usize>()
|
||||||
@ -350,7 +351,8 @@ impl Dialog {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut offset = overhead.left
|
let mut offset = overhead.left
|
||||||
+ self.align
|
+ self
|
||||||
|
.align
|
||||||
.h
|
.h
|
||||||
.get_offset(width, printer.size.x - overhead.horizontal());
|
.get_offset(width, printer.size.x - overhead.horizontal());
|
||||||
|
|
||||||
@ -381,7 +383,8 @@ impl Dialog {
|
|||||||
|
|
||||||
fn draw_content(&self, printer: &Printer, buttons_height: usize) {
|
fn draw_content(&self, printer: &Printer, buttons_height: usize) {
|
||||||
// What do we have left?
|
// What do we have left?
|
||||||
let taken = Vec2::new(0, buttons_height) + self.borders.combined()
|
let taken = Vec2::new(0, buttons_height)
|
||||||
|
+ self.borders.combined()
|
||||||
+ self.padding.combined();
|
+ self.padding.combined();
|
||||||
|
|
||||||
let inner_size = match printer.size.checked_sub(taken) {
|
let inner_size = match printer.size.checked_sub(taken) {
|
||||||
@ -403,7 +406,8 @@ impl Dialog {
|
|||||||
}
|
}
|
||||||
let spacing = 3; //minimum distance to borders
|
let spacing = 3; //minimum distance to borders
|
||||||
let x = spacing
|
let x = spacing
|
||||||
+ self.title_position
|
+ self
|
||||||
|
.title_position
|
||||||
.get_offset(len, printer.size.x - 2 * spacing);
|
.get_offset(len, printer.size.x - 2 * spacing);
|
||||||
printer.with_high_border(false, |printer| {
|
printer.with_high_border(false, |printer| {
|
||||||
printer.print((x - 2, 0), "┤ ");
|
printer.print((x - 2, 0), "┤ ");
|
||||||
|
@ -616,15 +616,20 @@ impl View for EditView {
|
|||||||
return EventResult::Consumed(Some(self.insert(ch)));
|
return EventResult::Consumed(Some(self.insert(ch)));
|
||||||
}
|
}
|
||||||
// TODO: handle ctrl-key?
|
// TODO: handle ctrl-key?
|
||||||
Event::Key(Key::Home) => self.cursor = 0,
|
Event::Key(Key::Home) => self.set_cursor(0),
|
||||||
Event::Key(Key::End) => self.cursor = self.content.len(),
|
Event::Key(Key::End) => {
|
||||||
|
// When possible, NLL to the rescue!
|
||||||
|
let len = self.content.len();
|
||||||
|
self.set_cursor(len);
|
||||||
|
}
|
||||||
Event::Key(Key::Left) if self.cursor > 0 => {
|
Event::Key(Key::Left) if self.cursor > 0 => {
|
||||||
let len = self.content[..self.cursor]
|
let len = self.content[..self.cursor]
|
||||||
.graphemes(true)
|
.graphemes(true)
|
||||||
.last()
|
.last()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.len();
|
.len();
|
||||||
self.cursor -= len;
|
let cursor = self.cursor - len;
|
||||||
|
self.set_cursor(cursor);
|
||||||
}
|
}
|
||||||
Event::Key(Key::Right) if self.cursor < self.content.len() => {
|
Event::Key(Key::Right) if self.cursor < self.content.len() => {
|
||||||
let len = self.content[self.cursor..]
|
let len = self.content[self.cursor..]
|
||||||
@ -632,7 +637,8 @@ impl View for EditView {
|
|||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.len();
|
.len();
|
||||||
self.cursor += len;
|
let cursor = self.cursor + len;
|
||||||
|
self.set_cursor(cursor);
|
||||||
}
|
}
|
||||||
Event::Key(Key::Backspace) if self.cursor > 0 => {
|
Event::Key(Key::Backspace) if self.cursor > 0 => {
|
||||||
let len = self.content[..self.cursor]
|
let len = self.content[..self.cursor]
|
||||||
|
94
src/views/hideable_view.rs
Normal file
94
src/views/hideable_view.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use view::{Selector, View, ViewWrapper};
|
||||||
|
use With;
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
/// Wrapper around another view that can be hidden at will.
|
||||||
|
///
|
||||||
|
/// By default, it simply forwards all calls to the inner view.
|
||||||
|
///
|
||||||
|
/// When hidden (with `HideableView::hide()`), it will appear as a zero-sized
|
||||||
|
/// invisible view, will not take focus and will not accept input.
|
||||||
|
///
|
||||||
|
/// It can be made visible again with `HideableView::unhide()`.
|
||||||
|
pub struct HideableView<V> {
|
||||||
|
view: V,
|
||||||
|
visible: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V> HideableView<V> {
|
||||||
|
/// Creates a new HideableView around `view`.
|
||||||
|
///
|
||||||
|
/// It will be visible by default.
|
||||||
|
pub fn new(view: V) -> Self {
|
||||||
|
HideableView {
|
||||||
|
view,
|
||||||
|
visible: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the visibility for this view.
|
||||||
|
pub fn set_visible(&mut self, visible: bool) {
|
||||||
|
self.visible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the visibility for this view to `false`.
|
||||||
|
pub fn hide(&mut self) {
|
||||||
|
self.set_visible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the visibility for this view to `true`.
|
||||||
|
pub fn unhide(&mut self) {
|
||||||
|
self.set_visible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the visibility for this view to `false`.
|
||||||
|
///
|
||||||
|
/// Chainable variant.
|
||||||
|
pub fn hidden(self) -> Self {
|
||||||
|
self.with(Self::hide)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_getters!(self.view: V);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View> ViewWrapper for HideableView<V> {
|
||||||
|
type V = V;
|
||||||
|
|
||||||
|
fn with_view<F, R>(&self, f: F) -> Option<R>
|
||||||
|
where
|
||||||
|
F: FnOnce(&Self::V) -> R,
|
||||||
|
{
|
||||||
|
if self.visible {
|
||||||
|
Some(f(&self.view))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_view_mut<F, R>(&mut self, f: F) -> Option<R>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self::V) -> R,
|
||||||
|
{
|
||||||
|
if self.visible {
|
||||||
|
Some(f(&mut self.view))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_call_on_any<'a>(
|
||||||
|
&mut self, selector: &Selector, callback: Box<FnMut(&mut Any) + 'a>,
|
||||||
|
) {
|
||||||
|
// We always run callbacks, even when invisible.
|
||||||
|
self.view.call_on_any(selector, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_inner(self) -> Result<Self::V, Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
Self::V: Sized,
|
||||||
|
{
|
||||||
|
Ok(self.view)
|
||||||
|
}
|
||||||
|
}
|
@ -92,7 +92,8 @@ impl<T: View + 'static> ViewWrapper for IdView<T> {
|
|||||||
fn wrap_focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
|
fn wrap_focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
|
||||||
match selector {
|
match selector {
|
||||||
&Selector::Id(id) if id == self.id => Ok(()),
|
&Selector::Id(id) if id == self.id => Ok(()),
|
||||||
s => self.view
|
s => self
|
||||||
|
.view
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
.and_then(|mut v| v.deref_mut().focus_view(s)),
|
.and_then(|mut v| v.deref_mut().focus_view(s)),
|
||||||
|
@ -195,7 +195,8 @@ impl LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn children_are_sleeping(&self) -> bool {
|
fn children_are_sleeping(&self) -> bool {
|
||||||
!self.children
|
!self
|
||||||
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
.map(Child::as_view)
|
.map(Child::as_view)
|
||||||
.any(View::needs_relayout)
|
.any(View::needs_relayout)
|
||||||
@ -357,7 +358,8 @@ impl View for LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First, make a naive scenario: everything will work fine.
|
// First, make a naive scenario: everything will work fine.
|
||||||
let ideal_sizes: Vec<Vec2> = self.children
|
let ideal_sizes: Vec<Vec2> = self
|
||||||
|
.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|c| c.required_size(req))
|
.map(|c| c.required_size(req))
|
||||||
.collect();
|
.collect();
|
||||||
@ -381,7 +383,8 @@ impl View for LinearLayout {
|
|||||||
|
|
||||||
// See how they like it that way.
|
// See how they like it that way.
|
||||||
// This is, hopefully, the absolute minimum these views will accept.
|
// This is, hopefully, the absolute minimum these views will accept.
|
||||||
let min_sizes: Vec<Vec2> = self.children
|
let min_sizes: Vec<Vec2> = self
|
||||||
|
.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|c| c.required_size(budget_req))
|
.map(|c| c.required_size(budget_req))
|
||||||
.collect();
|
.collect();
|
||||||
@ -461,7 +464,8 @@ impl View for LinearLayout {
|
|||||||
|
|
||||||
// Let's ask everyone one last time. Everyone should be happy.
|
// Let's ask everyone one last time. Everyone should be happy.
|
||||||
// (But they may ask more on the other axis.)
|
// (But they may ask more on the other axis.)
|
||||||
let final_sizes: Vec<Vec2> = self.children
|
let final_sizes: Vec<Vec2> = self
|
||||||
|
.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, c)| c.required_size(final_lengths[i]))
|
.map(|(i, c)| c.required_size(final_lengths[i]))
|
||||||
@ -481,10 +485,9 @@ impl View for LinearLayout {
|
|||||||
// In what order will we iterate on the children?
|
// In what order will we iterate on the children?
|
||||||
let rel = source.relative(self.orientation);
|
let rel = source.relative(self.orientation);
|
||||||
// We activate from_focus only if coming from the "sides".
|
// We activate from_focus only if coming from the "sides".
|
||||||
let i = if let Some(i) = self.iter_mut(
|
let i = if let Some(i) = self
|
||||||
rel.is_none(),
|
.iter_mut(rel.is_none(), rel.unwrap_or(direction::Relative::Front))
|
||||||
rel.unwrap_or(direction::Relative::Front),
|
.filter_map(|p| try_focus(p, source))
|
||||||
).filter_map(|p| try_focus(p, source))
|
|
||||||
.next()
|
.next()
|
||||||
{
|
{
|
||||||
// ... we can't update `self.focus` here,
|
// ... we can't update `self.focus` here,
|
||||||
|
@ -283,14 +283,16 @@ impl View for ListView {
|
|||||||
|
|
||||||
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
||||||
// We'll show 2 columns: the labels, and the views.
|
// We'll show 2 columns: the labels, and the views.
|
||||||
let label_width = self.children
|
let label_width = self
|
||||||
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
.map(ListChild::label)
|
.map(ListChild::label)
|
||||||
.map(UnicodeWidthStr::width)
|
.map(UnicodeWidthStr::width)
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let view_size = self.children
|
let view_size = self
|
||||||
|
.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.filter_map(ListChild::view)
|
.filter_map(ListChild::view)
|
||||||
.map(|v| v.required_size(req).x)
|
.map(|v| v.required_size(req).x)
|
||||||
@ -310,7 +312,8 @@ impl View for ListView {
|
|||||||
self.scrollbase.set_heights(size.y, self.children.len());
|
self.scrollbase.set_heights(size.y, self.children.len());
|
||||||
|
|
||||||
// We'll show 2 columns: the labels, and the views.
|
// We'll show 2 columns: the labels, and the views.
|
||||||
let label_width = self.children
|
let label_width = self
|
||||||
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
.map(ListChild::label)
|
.map(ListChild::label)
|
||||||
.map(UnicodeWidthStr::width)
|
.map(UnicodeWidthStr::width)
|
||||||
@ -320,7 +323,8 @@ impl View for ListView {
|
|||||||
let spacing = 1;
|
let spacing = 1;
|
||||||
let scrollbar_width = if self.children.len() > size.y { 2 } else { 0 };
|
let scrollbar_width = if self.children.len() > size.y { 2 } else { 0 };
|
||||||
|
|
||||||
let available = size.x
|
let available = size
|
||||||
|
.x
|
||||||
.saturating_sub(label_width + spacing + scrollbar_width);
|
.saturating_sub(label_width + spacing + scrollbar_width);
|
||||||
|
|
||||||
debug!("Available: {}", available);
|
debug!("Available: {}", available);
|
||||||
@ -439,10 +443,9 @@ impl View for ListView {
|
|||||||
|
|
||||||
fn take_focus(&mut self, source: direction::Direction) -> bool {
|
fn take_focus(&mut self, source: direction::Direction) -> bool {
|
||||||
let rel = source.relative(direction::Orientation::Vertical);
|
let rel = source.relative(direction::Orientation::Vertical);
|
||||||
let i = if let Some(i) = self.iter_mut(
|
let i = if let Some(i) = self
|
||||||
rel.is_none(),
|
.iter_mut(rel.is_none(), rel.unwrap_or(direction::Relative::Front))
|
||||||
rel.unwrap_or(direction::Relative::Front),
|
.filter_map(|p| try_focus(p, source))
|
||||||
).filter_map(|p| try_focus(p, source))
|
|
||||||
.next()
|
.next()
|
||||||
{
|
{
|
||||||
i
|
i
|
||||||
@ -464,7 +467,8 @@ impl View for ListView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
|
fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
|
||||||
if let Some(i) = self.children
|
if let Some(i) = self
|
||||||
|
.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, v)| v.view().map(|v| (i, v)))
|
.filter_map(|(i, v)| v.view().map(|v| (i, v)))
|
||||||
|
@ -138,7 +138,8 @@ impl MenuPopup {
|
|||||||
|
|
||||||
fn make_subtree_cb(&self, tree: &Rc<MenuTree>) -> EventResult {
|
fn make_subtree_cb(&self, tree: &Rc<MenuTree>) -> EventResult {
|
||||||
let tree = Rc::clone(tree);
|
let tree = Rc::clone(tree);
|
||||||
let max_width = 4 + self.menu
|
let max_width = 4 + self
|
||||||
|
.menu
|
||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
.map(Self::item_width)
|
.map(Self::item_width)
|
||||||
@ -249,7 +250,8 @@ impl View for MenuPopup {
|
|||||||
|
|
||||||
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
||||||
// We can't really shrink our items here, so it's not flexible.
|
// We can't really shrink our items here, so it's not flexible.
|
||||||
let w = 4 + self.menu
|
let w = 4 + self
|
||||||
|
.menu
|
||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
.map(Self::item_width)
|
.map(Self::item_width)
|
||||||
|
@ -342,7 +342,8 @@ impl View for Menubar {
|
|||||||
.checked_sub(offset)
|
.checked_sub(offset)
|
||||||
.and_then(|pos| self.child_at(pos.x))
|
.and_then(|pos| self.child_at(pos.x))
|
||||||
{
|
{
|
||||||
if self.focus == child && btn == MouseButton::Left
|
if self.focus == child
|
||||||
|
&& btn == MouseButton::Left
|
||||||
&& self.root.children[child].is_leaf()
|
&& self.root.children[child].is_leaf()
|
||||||
{
|
{
|
||||||
return self.select_child(false);
|
return self.select_child(false);
|
||||||
@ -372,7 +373,8 @@ impl View for Menubar {
|
|||||||
// We add 2 to the length of every label for marin.
|
// We add 2 to the length of every label for marin.
|
||||||
// Also, we add 1 at the beginning.
|
// Also, we add 1 at the beginning.
|
||||||
// (See the `draw()` method)
|
// (See the `draw()` method)
|
||||||
let width = self.root
|
let width = self
|
||||||
|
.root
|
||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| item.label().len() + 2)
|
.map(|item| item.label().len() + 2)
|
||||||
|
@ -42,6 +42,7 @@ mod checkbox;
|
|||||||
mod dialog;
|
mod dialog;
|
||||||
mod dummy;
|
mod dummy;
|
||||||
mod edit_view;
|
mod edit_view;
|
||||||
|
mod hideable_view;
|
||||||
mod id_view;
|
mod id_view;
|
||||||
mod layer;
|
mod layer;
|
||||||
mod linear_layout;
|
mod linear_layout;
|
||||||
@ -70,6 +71,7 @@ pub use self::checkbox::Checkbox;
|
|||||||
pub use self::dialog::{Dialog, DialogFocus};
|
pub use self::dialog::{Dialog, DialogFocus};
|
||||||
pub use self::dummy::DummyView;
|
pub use self::dummy::DummyView;
|
||||||
pub use self::edit_view::EditView;
|
pub use self::edit_view::EditView;
|
||||||
|
pub use self::hideable_view::HideableView;
|
||||||
pub use self::id_view::{IdView, ViewRef};
|
pub use self::id_view::{IdView, ViewRef};
|
||||||
pub use self::layer::Layer;
|
pub use self::layer::Layer;
|
||||||
pub use self::linear_layout::LinearLayout;
|
pub use self::linear_layout::LinearLayout;
|
||||||
|
@ -527,7 +527,8 @@ impl<T: 'static> SelectView<T> {
|
|||||||
// the list when we reach the end.
|
// the list when we reach the end.
|
||||||
// This is achieved by chaining twice the iterator
|
// This is achieved by chaining twice the iterator
|
||||||
let iter = self.items.iter().chain(self.items.iter());
|
let iter = self.items.iter().chain(self.items.iter());
|
||||||
if let Some((i, _)) = iter.enumerate()
|
if let Some((i, _)) = iter
|
||||||
|
.enumerate()
|
||||||
.skip(self.focus() + 1)
|
.skip(self.focus() + 1)
|
||||||
.find(|&(_, item)| item.label.starts_with(c))
|
.find(|&(_, item)| item.label.starts_with(c))
|
||||||
{
|
{
|
||||||
@ -735,7 +736,8 @@ impl<T: 'static> View for SelectView<T> {
|
|||||||
// Items here are not compressible.
|
// Items here are not compressible.
|
||||||
// So no matter what the horizontal requirements are,
|
// So no matter what the horizontal requirements are,
|
||||||
// we'll still return our longest item.
|
// we'll still return our longest item.
|
||||||
let w = self.items
|
let w = self
|
||||||
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| item.label.width())
|
.map(|item| item.label.width())
|
||||||
.max()
|
.max()
|
||||||
|
@ -56,8 +56,10 @@ impl Placement {
|
|||||||
enum ChildWrapper<T: View> {
|
enum ChildWrapper<T: View> {
|
||||||
// Some views include a shadow around.
|
// Some views include a shadow around.
|
||||||
Shadow(ShadowView<Layer<T>>),
|
Shadow(ShadowView<Layer<T>>),
|
||||||
|
// Some include a background.
|
||||||
|
Backfilled(Layer<T>),
|
||||||
// Some views don't (fullscreen views mostly)
|
// Some views don't (fullscreen views mostly)
|
||||||
Plain(Layer<T>),
|
Plain(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: View> ChildWrapper<T> {
|
impl<T: View> ChildWrapper<T> {
|
||||||
@ -67,7 +69,11 @@ impl<T: View> ChildWrapper<T> {
|
|||||||
ChildWrapper::Shadow(shadow) => {
|
ChildWrapper::Shadow(shadow) => {
|
||||||
shadow.into_inner().ok().unwrap().into_inner().ok().unwrap()
|
shadow.into_inner().ok().unwrap().into_inner().ok().unwrap()
|
||||||
}
|
}
|
||||||
ChildWrapper::Plain(layer) => layer.into_inner().ok().unwrap(),
|
// Layer::into_inner can never fail.
|
||||||
|
ChildWrapper::Backfilled(background) => {
|
||||||
|
background.into_inner().ok().unwrap()
|
||||||
|
}
|
||||||
|
ChildWrapper::Plain(layer) => layer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,7 +83,8 @@ impl<T: View> ChildWrapper<T> {
|
|||||||
pub fn get_inner(&self) -> &View {
|
pub fn get_inner(&self) -> &View {
|
||||||
match *self {
|
match *self {
|
||||||
ChildWrapper::Shadow(ref shadow) => shadow.get_inner().get_inner(),
|
ChildWrapper::Shadow(ref shadow) => shadow.get_inner().get_inner(),
|
||||||
ChildWrapper::Plain(ref layer) => layer.get_inner(),
|
ChildWrapper::Backfilled(ref background) => background.get_inner(),
|
||||||
|
ChildWrapper::Plain(ref layer) => layer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +94,10 @@ impl<T: View> ChildWrapper<T> {
|
|||||||
ChildWrapper::Shadow(ref mut shadow) => {
|
ChildWrapper::Shadow(ref mut shadow) => {
|
||||||
shadow.get_inner_mut().get_inner_mut()
|
shadow.get_inner_mut().get_inner_mut()
|
||||||
}
|
}
|
||||||
ChildWrapper::Plain(ref mut layer) => layer.get_inner_mut(),
|
ChildWrapper::Backfilled(ref mut background) => {
|
||||||
|
background.get_inner_mut()
|
||||||
|
}
|
||||||
|
ChildWrapper::Plain(ref mut layer) => layer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,6 +107,7 @@ impl<T: View> View for ChildWrapper<T> {
|
|||||||
fn draw(&self, printer: &Printer) {
|
fn draw(&self, printer: &Printer) {
|
||||||
match *self {
|
match *self {
|
||||||
ChildWrapper::Shadow(ref v) => v.draw(printer),
|
ChildWrapper::Shadow(ref v) => v.draw(printer),
|
||||||
|
ChildWrapper::Backfilled(ref v) => v.draw(printer),
|
||||||
ChildWrapper::Plain(ref v) => v.draw(printer),
|
ChildWrapper::Plain(ref v) => v.draw(printer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,6 +115,7 @@ impl<T: View> View for ChildWrapper<T> {
|
|||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
match *self {
|
match *self {
|
||||||
ChildWrapper::Shadow(ref mut v) => v.on_event(event),
|
ChildWrapper::Shadow(ref mut v) => v.on_event(event),
|
||||||
|
ChildWrapper::Backfilled(ref mut v) => v.on_event(event),
|
||||||
ChildWrapper::Plain(ref mut v) => v.on_event(event),
|
ChildWrapper::Plain(ref mut v) => v.on_event(event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,6 +123,7 @@ impl<T: View> View for ChildWrapper<T> {
|
|||||||
fn layout(&mut self, size: Vec2) {
|
fn layout(&mut self, size: Vec2) {
|
||||||
match *self {
|
match *self {
|
||||||
ChildWrapper::Shadow(ref mut v) => v.layout(size),
|
ChildWrapper::Shadow(ref mut v) => v.layout(size),
|
||||||
|
ChildWrapper::Backfilled(ref mut v) => v.layout(size),
|
||||||
ChildWrapper::Plain(ref mut v) => v.layout(size),
|
ChildWrapper::Plain(ref mut v) => v.layout(size),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,6 +131,7 @@ impl<T: View> View for ChildWrapper<T> {
|
|||||||
fn required_size(&mut self, size: Vec2) -> Vec2 {
|
fn required_size(&mut self, size: Vec2) -> Vec2 {
|
||||||
match *self {
|
match *self {
|
||||||
ChildWrapper::Shadow(ref mut v) => v.required_size(size),
|
ChildWrapper::Shadow(ref mut v) => v.required_size(size),
|
||||||
|
ChildWrapper::Backfilled(ref mut v) => v.required_size(size),
|
||||||
ChildWrapper::Plain(ref mut v) => v.required_size(size),
|
ChildWrapper::Plain(ref mut v) => v.required_size(size),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,6 +139,7 @@ impl<T: View> View for ChildWrapper<T> {
|
|||||||
fn take_focus(&mut self, source: Direction) -> bool {
|
fn take_focus(&mut self, source: Direction) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
ChildWrapper::Shadow(ref mut v) => v.take_focus(source),
|
ChildWrapper::Shadow(ref mut v) => v.take_focus(source),
|
||||||
|
ChildWrapper::Backfilled(ref mut v) => v.take_focus(source),
|
||||||
ChildWrapper::Plain(ref mut v) => v.take_focus(source),
|
ChildWrapper::Plain(ref mut v) => v.take_focus(source),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,6 +149,9 @@ impl<T: View> View for ChildWrapper<T> {
|
|||||||
ChildWrapper::Shadow(ref mut v) => {
|
ChildWrapper::Shadow(ref mut v) => {
|
||||||
v.call_on_any(selector, callback)
|
v.call_on_any(selector, callback)
|
||||||
}
|
}
|
||||||
|
ChildWrapper::Backfilled(ref mut v) => {
|
||||||
|
v.call_on_any(selector, callback)
|
||||||
|
}
|
||||||
ChildWrapper::Plain(ref mut v) => {
|
ChildWrapper::Plain(ref mut v) => {
|
||||||
v.call_on_any(selector, callback)
|
v.call_on_any(selector, callback)
|
||||||
}
|
}
|
||||||
@ -143,6 +161,7 @@ impl<T: View> View for ChildWrapper<T> {
|
|||||||
fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
|
fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
|
||||||
match *self {
|
match *self {
|
||||||
ChildWrapper::Shadow(ref mut v) => v.focus_view(selector),
|
ChildWrapper::Shadow(ref mut v) => v.focus_view(selector),
|
||||||
|
ChildWrapper::Backfilled(ref mut v) => v.focus_view(selector),
|
||||||
ChildWrapper::Plain(ref mut v) => v.focus_view(selector),
|
ChildWrapper::Plain(ref mut v) => v.focus_view(selector),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +200,7 @@ impl StackView {
|
|||||||
{
|
{
|
||||||
let boxed = ViewBox::boxed(view);
|
let boxed = ViewBox::boxed(view);
|
||||||
self.layers.push(Child {
|
self.layers.push(Child {
|
||||||
view: ChildWrapper::Plain(Layer::new(boxed)),
|
view: ChildWrapper::Backfilled(Layer::new(boxed)),
|
||||||
size: Vec2::zero(),
|
size: Vec2::zero(),
|
||||||
placement: Placement::Fullscreen,
|
placement: Placement::Fullscreen,
|
||||||
virgin: true,
|
virgin: true,
|
||||||
@ -267,6 +286,16 @@ impl StackView {
|
|||||||
self.with(|s| s.add_fullscreen_layer(view))
|
self.with(|s| s.add_fullscreen_layer(view))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a new transparent layer on top of the stack.
|
||||||
|
///
|
||||||
|
/// Chainable variant.
|
||||||
|
pub fn transparent_layer<T>(self, view: T) -> Self
|
||||||
|
where
|
||||||
|
T: IntoBoxedView,
|
||||||
|
{
|
||||||
|
self.with(|s| s.add_transparent_layer(view))
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a view on top of the stack.
|
/// Adds a view on top of the stack.
|
||||||
pub fn add_layer_at<T>(&mut self, position: Position, view: T)
|
pub fn add_layer_at<T>(&mut self, position: Position, view: T)
|
||||||
where
|
where
|
||||||
@ -286,6 +315,28 @@ impl StackView {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a transparent view on top of the stack in the center of the screen.
|
||||||
|
pub fn add_transparent_layer<T>(&mut self, view: T)
|
||||||
|
where
|
||||||
|
T: IntoBoxedView,
|
||||||
|
{
|
||||||
|
self.add_transparent_layer_at(Position::center(), view);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a transparent view on top of the stack.
|
||||||
|
pub fn add_transparent_layer_at<T>(&mut self, position: Position, view: T)
|
||||||
|
where
|
||||||
|
T: IntoBoxedView,
|
||||||
|
{
|
||||||
|
let boxed = ViewBox::boxed(view);
|
||||||
|
self.layers.push(Child {
|
||||||
|
view: ChildWrapper::Plain(boxed),
|
||||||
|
size: Vec2::new(0, 0),
|
||||||
|
placement: Placement::Floating(position),
|
||||||
|
virgin: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds a view on top of the stack.
|
/// Adds a view on top of the stack.
|
||||||
///
|
///
|
||||||
/// Chainable variant.
|
/// Chainable variant.
|
||||||
@ -612,4 +663,52 @@ mod tests {
|
|||||||
|
|
||||||
assert!(stack.pop_layer().is_none());
|
assert!(stack.pop_layer().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get() {
|
||||||
|
let mut stack = StackView::new()
|
||||||
|
.layer(TextView::new("1"))
|
||||||
|
.layer(TextView::new("2"));
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
stack
|
||||||
|
.get(LayerPosition::FromFront(0))
|
||||||
|
.unwrap()
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<ViewBox>()
|
||||||
|
.unwrap()
|
||||||
|
.with_view(|v| v.as_any().is::<TextView>())
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stack
|
||||||
|
.get(LayerPosition::FromBack(0))
|
||||||
|
.unwrap()
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<ViewBox>()
|
||||||
|
.unwrap()
|
||||||
|
.with_view(|v| v.as_any().is::<TextView>())
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stack
|
||||||
|
.get_mut(LayerPosition::FromFront(0))
|
||||||
|
.unwrap()
|
||||||
|
.as_any_mut()
|
||||||
|
.downcast_mut::<ViewBox>()
|
||||||
|
.unwrap()
|
||||||
|
.with_view_mut(|v| v.as_any_mut().is::<TextView>())
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stack
|
||||||
|
.get_mut(LayerPosition::FromBack(0))
|
||||||
|
.unwrap()
|
||||||
|
.as_any_mut()
|
||||||
|
.downcast_mut::<ViewBox>()
|
||||||
|
.unwrap()
|
||||||
|
.with_view_mut(|v| v.as_any_mut().is::<TextView>())
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,7 +442,8 @@ impl View for TextArea {
|
|||||||
debug!("{:?}", self.rows);
|
debug!("{:?}", self.rows);
|
||||||
let scroll_width = if self.rows.len() > constraint.y { 1 } else { 0 };
|
let scroll_width = if self.rows.len() > constraint.y { 1 } else { 0 };
|
||||||
Vec2::new(
|
Vec2::new(
|
||||||
scroll_width + 1
|
scroll_width
|
||||||
|
+ 1
|
||||||
+ self.rows.iter().map(|r| r.width).max().unwrap_or(1),
|
+ self.rows.iter().map(|r| r.width).max().unwrap_or(1),
|
||||||
self.rows.len(),
|
self.rows.len(),
|
||||||
)
|
)
|
||||||
|
@ -421,7 +421,8 @@ impl TextView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Desired width, including the scrollbar_width.
|
// Desired width, including the scrollbar_width.
|
||||||
self.width = self.rows
|
self.width = self
|
||||||
|
.rows
|
||||||
.iter()
|
.iter()
|
||||||
.map(|row| row.width)
|
.map(|row| row.width)
|
||||||
.max()
|
.max()
|
||||||
|
Loading…
Reference in New Issue
Block a user