libnotcurses_sys/widgets/selector/
methods.rs

1use core::{
2    ffi::c_char,
3    ptr::{null, null_mut},
4};
5
6#[cfg(not(feature = "std"))]
7use alloc::string::{String, ToString};
8
9use crate::{
10    c_api, cstring, error, error_ref_mut, error_str,
11    widgets::{NcSelector, NcSelectorBuilder, NcSelectorItem, NcSelectorOptions},
12    NcChannels, NcInput, NcPlane, NcResult, NcString,
13};
14
15impl NcSelector {
16    /// Creates a selector over a `plane`.
17    ///
18    /// The selector will take care of destroying the plane.
19    pub fn new<'a>(plane: &mut NcPlane, options: &NcSelectorOptions) -> NcResult<&'a mut Self> {
20        error_ref_mut![
21            unsafe { c_api::ncselector_create(plane, options) },
22            "ncselector_create"
23        ]
24    }
25
26    /// Starts the builder.
27    pub fn builder() -> NcSelectorBuilder {
28        NcSelectorBuilder::new()
29    }
30
31    /// Offers an input to the selector.
32    ///
33    /// If it's relevant, this function returns true, and the input ought not be
34    /// processed further. If it's irrelevant to the selector, false is returned.
35    ///
36    /// Relevant inputs include:
37    /// - a mouse click on an item.
38    /// - a mouse scrollwheel event.
39    /// - a mouse click on the scrolling arrows.
40    /// - up, down, pgup, or pgdown on an unrolled menu (navigates among items).
41    pub fn offer_input(&mut self, input: impl Into<NcInput>) -> bool {
42        unsafe { c_api::ncselector_offer_input(self, &input.into()) }
43    }
44
45    /// Destroys the `NcSelector`.
46    ///
47    /// Note that this also destroys the [`NcPlane`].
48    //
49    // If 'item' is not NULL, the last selected option will
50    // be strdup()ed and assigned to '*item' (and must be free()d by the caller).
51    pub fn destroy(&mut self) -> NcResult<()> {
52        unsafe { c_api::ncselector_destroy(self, null_mut()) };
53        Ok(())
54    }
55
56    /// Adds an item.
57    //
58    // CHECK whether this works for multiple items.
59    pub fn additem(&mut self, item: NcSelectorItem) -> NcResult<i32> {
60        error![
61            unsafe { c_api::ncselector_additem(self, &item) },
62            "Calling selector.additem", -1
63        ]
64    }
65
66    /// Deletes an item.
67    ///
68    /// *C style function: [ncselector_delitem()][c_api::ncselector_delitem].*
69    pub fn delitem(&mut self, item: &str) -> NcResult<i32> {
70        let cs = cstring![item];
71        error![
72            unsafe { c_api::ncselector_delitem(self, cs.as_ptr()) },
73            "Calling selector.delitem", -1
74        ]
75    }
76
77    /// Returns the selected option if there is one.
78    ///
79    /// *C style function: [ncselector_delitem()][c_api::ncselector_delitem].*
80    pub fn selected(&mut self) -> Option<String> {
81        // MAYBE turn this into a macro (option_str![])
82        let res = unsafe { c_api::ncselector_selected(self) };
83        if res.is_null() {
84            None
85        } else {
86            Some(crate::rstring!(res).to_string())
87        }
88    }
89
90    // NOTE: too unsafe
91    // /// Return a reference to the ncselector's underlying ncplane.
92    // pub fn plane<'a>(&mut self) -> NcResult<&'a mut NcPlane> {
93    //     error_ref_mut![unsafe { c_api::ncselector_plane(self) }, "Calling selector.plane"]
94    // }
95
96    /// Move down in the list. A reference to the newly-selected item is
97    /// returned, or NULL if there are no items in the list.
98    pub fn nextitem(&mut self) -> NcResult<String> {
99        let cstr: *const c_char = unsafe { c_api::ncselector_nextitem(self) };
100        error_str![cstr, "Calling selector.nextitem"]
101    }
102
103    /// Move up in the list. A reference to the newly-selected item is
104    /// returned, or NULL if there are no items in the list.
105    pub fn previtem(&mut self) -> NcResult<String> {
106        let cstr: *const c_char = unsafe { c_api::ncselector_previtem(self) };
107        error_str![cstr, "Calling selector.previtem"]
108    }
109}
110
111impl NcSelectorItem {
112    /// New item
113    pub fn new(option: &NcString, desc: &NcString) -> Self {
114        Self { option: option.as_ptr(), desc: desc.as_ptr() }
115    }
116
117    /// New empty `NcSelectorItem`.
118    pub fn new_empty() -> Self {
119        Self { option: null(), desc: null() }
120    }
121}
122
123/// # `NcMenuOptions` constructors
124impl NcSelectorOptions {
125    /// New `NcSelectorOptions` with just the list of items.
126    pub fn new(items: &[NcSelectorItem]) -> Self {
127        Self {
128            title: null(),
129            secondary: null(),
130            footer: null(),
131            items: items.as_ptr(),
132            defidx: 0,
133            maxdisplay: 0,
134            opchannels: 0,
135            descchannels: 0,
136            titlechannels: 0,
137            footchannels: 0,
138            boxchannels: 0,
139            flags: 0,
140        }
141    }
142
143    /// New `NcSelectorOptions` with all options.
144    pub fn with_all_options(
145        title: Option<&NcString>,
146        secondary: Option<&NcString>,
147        footer: Option<&NcString>,
148        items: &[NcSelectorItem],
149        default: u32,
150        max_display: u32,
151        opchannels: impl Into<NcChannels>,
152        descchannels: impl Into<NcChannels>,
153        titlechannels: impl Into<NcChannels>,
154        footchannels: impl Into<NcChannels>,
155        boxchannels: impl Into<NcChannels>,
156    ) -> Self {
157        assert![!items.is_empty()]; // DEBUG
158
159        let title_ptr = if let Some(s) = title { s.as_ptr() } else { null() };
160        let secondary_ptr = if let Some(s) = secondary { s.as_ptr() } else { null() };
161        let footer_ptr = if let Some(s) = footer { s.as_ptr() } else { null() };
162
163        Self {
164            title: title_ptr,
165            secondary: secondary_ptr,
166            footer: footer_ptr,
167            // initial items and descriptions,
168            items: items.as_ptr(),
169            // default item
170            defidx: default,
171            // maximum number of options to display at once,
172            // 0 to use all available space
173            maxdisplay: max_display,
174            // exhaustive styling options
175            opchannels: opchannels.into().into(),
176            descchannels: descchannels.into().into(),
177            titlechannels: titlechannels.into().into(),
178            footchannels: footchannels.into().into(),
179            boxchannels: boxchannels.into().into(),
180            flags: 0x0,
181        }
182    }
183}