Skip to content

Commit

Permalink
[Fixes gyscos#763] Introduced labelled checkboxes
Browse files Browse the repository at this point in the history
  • Loading branch information
michalfita committed Oct 25, 2023
1 parent 3b0e329 commit 04c5ab0
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 3 deletions.
33 changes: 30 additions & 3 deletions cursive-core/src/views/checkbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
event::{Event, EventResult, Key, MouseButton, MouseEvent},
theme::PaletteStyle,
view::{CannotFocus, View},
Cursive, Printer, Vec2, With,
Cursive, Printer, Vec2, With, utils::markup::StyledString,
};
use std::rc::Rc;

Expand All @@ -25,19 +25,32 @@ pub struct Checkbox {
enabled: bool,

on_change: Option<Rc<Callback>>,

label: StyledString,
}

new_default!(Checkbox);

impl Checkbox {
impl_enabled!(self.enabled);

/// Creates a new, unchecked checkbox.
/// Creates a new, unlabelled, unchecked checkbox.
pub fn new() -> Self {
Checkbox {
checked: false,
enabled: true,
on_change: None,
label: StyledString::new(),
}
}

/// Creates a new, labelled, unchecked checkbox.
pub fn labelled(label: StyledString) -> Self {
Checkbox {
checked: false,
enabled: true,
on_change: None,
label
}
}

Expand Down Expand Up @@ -134,12 +147,26 @@ impl Checkbox {
if self.checked {
printer.print((1, 0), "X");
}

if !self.label.is_empty() {
// We want the space to be highlighted if focused
printer.print((3, 0), " ");
printer.print_styled((4, 0), &self.label);
}
}

fn req_size(&self) -> Vec2 {
if self.label.is_empty() {
Vec2::new(3, 1)
} else {
Vec2::new(3 + 1 + self.label.width(), 1)
}
}
}

impl View for Checkbox {
fn required_size(&mut self, _: Vec2) -> Vec2 {
Vec2::new(3, 1)
self.req_size()
}

fn take_focus(&mut self, _: Direction) -> Result<EventResult, CannotFocus> {
Expand Down
4 changes: 4 additions & 0 deletions cursive/examples/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,7 @@ A larger example showing an implementation of minesweeper.
## [`window_title`](./window_title.rs)

This shows how to change the terminal window title.

## [`checkbox`](./checkbox.rs)

This shows how to use `Checkbox`.
81 changes: 81 additions & 0 deletions cursive/examples/checkbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use std::{cell::RefCell, collections::HashSet, fmt::Display, rc::Rc};

use cursive::views::{Checkbox, Dialog, DummyView, LinearLayout};

// This example uses checkboxes.
#[derive(Debug, PartialEq, Eq, Hash)]
enum Toppings {
ChocolateSprinkles,
CrushedAlmonds,
StrawberrySauce,
}

impl Display for Toppings {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Toppings::ChocolateSprinkles => write!(f, "Chocolate Sprinkles"),
Toppings::CrushedAlmonds => write!(f, "Crushed Almonds"),
Toppings::StrawberrySauce => write!(f, "Strawberry Sauce"),
}
}
}

fn main() {
let mut siv = cursive::default();

// TODO: placeholder for MultiChoiceGroup.

// Application wide container w/toppings choices.
let toppings: Rc<RefCell<HashSet<Toppings>>> = Rc::new(RefCell::new(HashSet::new()));

siv.add_layer(
Dialog::new()
.title("Make your selections")
.content(
LinearLayout::vertical()
.child(Checkbox::labelled("Chocolate Sprinkles".into()).on_change({
let toppings = toppings.clone();
move |_, checked| {
if checked {
toppings.borrow_mut().insert(Toppings::ChocolateSprinkles);
} else {
toppings.borrow_mut().remove(&Toppings::ChocolateSprinkles);
}
}
}))
.child(Checkbox::labelled("Crushed Almonds".into()).on_change({
let toppings = toppings.clone();
move |_, checked| {
if checked {
toppings.borrow_mut().insert(Toppings::CrushedAlmonds);
} else {
toppings.borrow_mut().remove(&Toppings::CrushedAlmonds);
}
}
}))
.child(Checkbox::labelled("Strawberry Sauce".into()).on_change({
let toppings = toppings.clone();
move |_, checked| {
if checked {
toppings.borrow_mut().insert(Toppings::StrawberrySauce);
} else {
toppings.borrow_mut().remove(&Toppings::StrawberrySauce);
}
}
})),
)
.button("Ok", move |s| {
s.pop_layer();
let toppings = toppings
.borrow()
.iter()
.map(|t| t.to_string())
.collect::<Vec<String>>()
.join(", ");
let text = format!("Toppings: {toppings}");
s.add_layer(Dialog::text(text).button("Ok", |s| s.quit()));
}),
);

siv.run();
}

0 comments on commit 04c5ab0

Please sign in to comment.