1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//! Check button widget - displays text/image with an on/off widget. 
//! Executes a command when clicked.
//!
//! * also see the Tk [manual](https://tcl.tk/man/tcl/TkCmd/ttk_checkbutton.htm)
//!
//! ## Examples
//!
//! The check button works like a regular [button](crate::button), except that it 
//! has a _state_. 
//!
//! The simplest check button has some text and a command - the command 
//! accepts a single boolean, which is the _new_ state of the button:
//!
//! ```ignore
//! let button_1 = rstk::make_check_button(&root);
//! button_1.text("Button label");
//! button_1.command(|value| { println!("button_1 now {}", value); });
//! ```
//! 
//! Check buttons can also display images, with or without text, and how these 
//! are displayed can be controlled using 
//! [compound](widget::TkLabelOptions::compound). In the following example, a 
//! check button with both an image and text is set to show the image below the 
//! text:
//!
//! ```ignore
//! let button_3 = rstk::make_check_button(&root);
//! button_3.image(&read_image("tcllogo.gif"));
//! button_3.text("Tcl Logo");
//! button_3.command(|state| { println!("Clicked button_3"); });
//! button_3.compound(rstk::Compound::Bottom);
//! ```
//! 
//! The check button's state can be changed or checked using the 
//! [selected](TkCheckButton::selected) and 
//! [is_selected](TkCheckButton::is_selected) methods.
//!

use super::grid;
use super::pack;
use super::widget;
use super::wish;

/// Refers to a check-button widget
#[derive(Clone, Debug, PartialEq)]
pub struct TkCheckButton {
    pub id: String,
    var: String,
}

/// Creates an instance of a check-button widget in given parent.
pub fn make_check_button(parent: &impl widget::TkWidget) -> TkCheckButton {
    let id = wish::next_wid(parent.id());
    let var = format!("::cb{}", wish::current_id());
    let msg = format!("set {} 0 ; ttk::checkbutton {} -variable {}", var, id, var);
    wish::tell_wish(&msg);

    TkCheckButton { id, var }
}

impl widget::TkWidget for TkCheckButton {
    /// Returns the widget's id reference - used within tk
    fn id(&self) -> &str {
        &self.id
    }
}
impl grid::TkGridLayout for TkCheckButton {}
impl pack::TkPackLayout for TkCheckButton {}
impl widget::TkLabelOptions for TkCheckButton {}

impl TkCheckButton {
    /// Sets the function to be called when the button is clicked.
    /// This function takes one boolean parameter, which is the _new_ state
    /// of the check button.
    pub fn command(&self, command: impl Fn(bool) + Send + 'static) {
        wish::add_callback1_bool(&self.id, wish::mk_callback1_bool(command));
        let msg = format!(
            "{} configure -command {{ puts cb1b-{}-${} ; flush stdout }}",
            self.id, self.id, self.var
        );
        wish::tell_wish(&msg);
    }

    /// Toggles the button's state and calls the button's command, 
    /// as if it were clicked.
    pub fn invoke(&self) {
        let msg = format!("{} invoke", self.id);
        wish::tell_wish(&msg);
    }

    /// Returns true/false if button is selected (checked) or not.
    pub fn is_selected(&self) -> bool {
        let msg = format!("puts ${} ; flush stdout", self.var);
        let result = wish::ask_wish(&msg);
        result == "1"
    }

    /// Sets the selected (checked) state.
    pub fn selected(&self, value: bool) {
        let msg = format!("set {} {}", self.var, if value { "1" } else { "0" });
        wish::tell_wish(&msg);
    }

    /// Sets the state of the button, usually normal (clickable) 
    /// or disabled (unclickable).
    pub fn state(&self, value: widget::State) {
        widget::configure(&self.id, "state", &value.to_string());
    }
}