Add SliderView

This commit is contained in:
Alexandre Bury 2016-08-13 01:03:40 -07:00
parent 921e4a451e
commit ce6992a8c5
7 changed files with 208 additions and 11 deletions

25
examples/slider.rs Normal file
View File

@ -0,0 +1,25 @@
extern crate cursive;
use cursive::prelude::*;
fn main() {
let mut siv = Cursive::new();
// Let's add a simple slider in a dialog.
// Moving the slider will update the dialog's title.
// And pressing "Enter" will show a new dialog.
siv.add_layer(Dialog::new(SliderView::horizontal(15)
.value(7)
.on_change(|s, v| {
let title = format!("[ {} ]", v);
s.find_id::<Dialog>("dialog").unwrap().set_title(title);
})
.on_enter(|s, v| {
s.pop_layer();
s.add_layer(Dialog::text(format!("Lucky number {}!", v))
.button("Ok", Cursive::quit));
}))
.with_id("dialog"));
siv.run();
}

View File

@ -69,9 +69,10 @@ impl Orientation {
}
/// Creates a new `Vec2` with `value` in `self`'s axis.
pub fn make_vec(&self, value: usize) -> Vec2 {
pub fn make_vec(&self, main_axis: usize, second_axis: usize) -> Vec2 {
let mut result = Vec2::zero();
*self.get_ref(&mut result) = value;
*self.get_ref(&mut result) = main_axis;
*self.swap().get_ref(&mut result) = second_axis;
result
}
}

View File

@ -11,6 +11,6 @@ pub use event::{Event, Key};
pub use view::{Boxable, Identifiable, Selector, View};
pub use views::{BoxView, Button, Checkbox, Dialog, EditView, IdView,
KeyEventView, LinearLayout, ListView, Panel, ProgressBar,
SelectView, TextArea, TextView};
SelectView, SliderView, TextArea, TextView};
pub use vec::Vec2;
pub use menu::MenuTree;

View File

@ -102,6 +102,7 @@ impl Dialog {
}
/// Sets the horizontal alignment for the buttons, if any.
///
/// Only works if the buttons are as a row at the bottom of the dialog.
pub fn h_align(mut self, h: HAlign) -> Self {
self.align.h = h;
@ -110,6 +111,7 @@ impl Dialog {
}
/// Sets the vertical alignment for the buttons, if any.
///
/// Only works if the buttons are as a column to the right of the dialog.
pub fn v_align(mut self, v: VAlign) -> Self {
self.align.v = v;
@ -123,10 +125,15 @@ impl Dialog {
}
/// Sets the title of the dialog.
///
/// If not empty, it will be visible at the top.
pub fn title<S: Into<String>>(mut self, label: S) -> Self {
pub fn title<S: Into<String>>(self, label: S) -> Self {
self.with(|s| s.set_title(label))
}
/// Sets the title of the dialog.
pub fn set_title<S: Into<String>>(&mut self, label: S) {
self.title = label.into();
self
}
/// Sets the padding in the dialog (around content and buttons).

View File

@ -137,9 +137,9 @@ impl EditView {
///
/// `callback` will be called with the view
/// content and the current cursor position.
pub fn on_edit<F: Fn(&mut Cursive, &str, usize) + 'static>(mut self,
callback: F)
-> Self {
pub fn on_edit<F>(mut self, callback: F) -> Self
where F: Fn(&mut Cursive, &str, usize) + 'static
{
self.on_edit = Some(Rc::new(callback));
self
}
@ -147,9 +147,9 @@ impl EditView {
/// Sets a callback to be called when `<Enter>` is pressed.
///
/// `callback` will be given the content of the view.
pub fn on_submit<F: Fn(&mut Cursive, &str) + 'static>(mut self,
callback: F)
-> Self {
pub fn on_submit<F>(mut self, callback: F) -> Self
where F: Fn(&mut Cursive, &str) + 'static
{
self.on_submit = Some(Rc::new(callback));
self
}

View File

@ -15,6 +15,7 @@ mod menu_popup;
mod panel;
mod progress_bar;
mod select_view;
mod slider_view;
mod shadow_view;
mod sized_view;
mod stack_view;
@ -37,6 +38,7 @@ pub use self::menu_popup::MenuPopup;
pub use self::panel::Panel;
pub use self::progress_bar::{Counter, ProgressBar};
pub use self::select_view::SelectView;
pub use self::slider_view::SliderView;
pub use self::shadow_view::ShadowView;
pub use self::sized_view::SizedView;
pub use self::stack_view::StackView;

162
src/views/slider_view.rs Normal file
View File

@ -0,0 +1,162 @@
use std::rc::Rc;
use With;
use {Cursive, Printer};
use theme::ColorStyle;
use event::{Callback, Event, EventResult, Key};
use vec::Vec2;
use view::View;
use direction::{Direction, Orientation};
/// A horizontal or vertical slider.
pub struct SliderView {
orientation: Orientation,
on_change: Option<Rc<Fn(&mut Cursive, usize)>>,
on_enter: Option<Rc<Fn(&mut Cursive, usize)>>,
value: usize,
max_value: usize,
}
impl SliderView {
/// Creates a new `SliderView` in the given orientation.
///
/// The view will have a fixed length of `max_value`,
/// with one tick per block.
pub fn new(orientation: Orientation, max_value: usize) -> Self {
SliderView {
orientation: orientation,
value: 0,
max_value: max_value,
on_change: None,
on_enter: None,
}
}
/// Creates a new vertical `SliderView`.
pub fn vertical(max_value: usize) -> Self {
Self::new(Orientation::Vertical, max_value)
}
/// Creates a new horizontal `SliderView`.
pub fn horizontal(max_value: usize) -> Self {
Self::new(Orientation::Horizontal, max_value)
}
/// Sets the current value.
///
/// Returns an event result with a possible callback,
/// if `on_change` was set..
pub fn set_value(&mut self, value: usize) -> EventResult {
self.value = value;
self.get_change_result()
}
/// Sets the current value.
///
/// Chainable variant.
pub fn value(self, value: usize) -> Self {
self.with(|s| {
s.set_value(value);
})
}
/// Sets a callback to be called when the slider is moved.
pub fn on_change<F>(mut self, callback: F) -> Self
where F: Fn(&mut Cursive, usize) + 'static
{
self.on_change = Some(Rc::new(callback));
self
}
/// Sets a callback to be called when the <Enter> key is pressed.
pub fn on_enter<F>(mut self, callback: F) -> Self
where F: Fn(&mut Cursive, usize) + 'static
{
self.on_enter = Some(Rc::new(callback));
self
}
fn get_change_result(&self) -> EventResult {
EventResult::Consumed(self.on_change.clone().map(|cb| {
let value = self.value;
Callback::from_fn(move |s| {
cb(s, value);
})
}))
}
fn slide_plus(&mut self) -> EventResult {
if self.value + 1 < self.max_value {
self.value += 1;
self.get_change_result()
} else {
EventResult::Ignored
}
}
fn slide_minus(&mut self) -> EventResult {
if self.value > 0 {
self.value -= 1;
self.get_change_result()
} else {
EventResult::Ignored
}
}
}
impl View for SliderView {
fn draw(&self, printer: &Printer) {
match self.orientation {
Orientation::Vertical => {
printer.print_vline((0, 0), self.max_value, "|")
}
Orientation::Horizontal => {
printer.print_hline((0, 0), self.max_value, "-")
}
}
let color = if printer.focused {
ColorStyle::Highlight
} else {
ColorStyle::HighlightInactive
};
printer.with_color(color, |printer| {
printer.print(self.orientation.make_vec(self.value, 0), " ");
});
}
fn get_min_size(&mut self, _: Vec2) -> Vec2 {
self.orientation.make_vec(self.max_value, 1)
}
fn on_event(&mut self, event: Event) -> EventResult {
match event {
Event::Key(Key::Left) if self.orientation ==
Orientation::Horizontal => {
self.slide_minus()
}
Event::Key(Key::Right) if self.orientation ==
Orientation::Horizontal => {
self.slide_plus()
}
Event::Key(Key::Up) if self.orientation ==
Orientation::Vertical => self.slide_minus(),
Event::Key(Key::Down) if self.orientation ==
Orientation::Vertical => {
self.slide_plus()
}
Event::Key(Key::Enter) if self.on_enter.is_some() => {
let value = self.value;
let cb = self.on_enter.clone().unwrap();
EventResult::with_cb(move |s| {
cb(s, value);
})
}
_ => EventResult::Ignored,
}
}
fn take_focus(&mut self, _: Direction) -> bool {
true
}
}