nvdialog_rs/
question_dialog.rs

1/*
2 *  The MIT License (MIT)
3 *
4 *  Copyright (c) 2022-2025 Aggelos Tselios
5 *
6 *  Permission is hereby granted, free of charge, to any person obtaining a copy
7 *  of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 *  furnished to do so, subject to the following conditions:
12 *
13 *  The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25use crate::{cstr, Object};
26use nvdialog_sys::ffi::*;
27use std::ffi::{c_uint, c_void};
28
29/// Represents the buttons that can be displayed on a `QuestionDialog`.
30///
31///
32/// # Example
33/// ```
34/// extern crate nvdialog_rs;
35/// use nvdialog_rs::{
36///     QuestionDialog,
37///     QuestionDialogButtons
38/// }
39///
40/// fn main() {
41///     let dialog = QuestionDialog::new(
42///             "title",
43///             "message",
44///             QuestionDialogButtons::YesNoCancel
45///     );
46///     println!("Reply from dialog: {:?}"), dialog.get_reply());
47/// }
48/// ```
49/// # Members
50/// - `Yes`: Corresponds to `NVD_YES`.
51/// - `YesNo`: Corresponds to `NVD_YES_NO`.
52/// - `YesNoCancel`: Corresponds to `NVD_YES_NO_CANCEL`.
53#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
54pub enum QuestionDialogButtons {
55    Yes = 0x04,
56    YesNo,
57    YesNoCancel,
58}
59
60/// A dialog box for asking a question and getting a response from
61/// the user.
62///
63/// This struct wraps the underlying C API for creating a question dialog
64/// box. It provides a high-level interface for displaying a dialog box,
65/// getting a response from the user, and destroying the dialog box.
66///
67/// # Examples
68///
69/// ```
70/// let question_dialog = QuestionDialog::new(
71///     "Are you sure you want to delete this file?",
72///     "This action cannot be undone.",
73///     QuestionDialogButtons::YesNo,
74/// );
75/// let reply = question_dialog.get_reply();
76///
77/// match result {
78///     Reply::Accepted => {
79///         // The user clicked "Yes". Delete the file...
80///     }
81///     Reply::Rejected => {
82///         // The user clicked "No". Do not delete the file...
83///     }
84///     Reply::Cancelled => {
85///         // The user clicked "Cancel". Do not delete the file...
86///     }
87/// }
88/// ```
89/// ## Safety
90/// This function converts the C enum for the reply (See
91/// [`NvdReply`](https://github.com/tseli0s/nvdialog/blob/master/include))
92/// into the crate's [`Reply`](crate::question_dialog::Reply) type using the
93/// `From<i32>` trait. This is generally safe, however, keep in mind that invalid
94/// or garbage values returned by some other, unrelated unsafe code may actually bypass
95/// the Rust safety rules.
96/// For further information see the documentation of [`Reply`](crate::question_dialog::Reply).
97/// ## FFI
98/// Corresponds to `NvdQuestionBox`.
99pub struct QuestionDialog {
100    raw: *mut NvdQuestionBox,
101    title: String,
102    msg: String,
103    buttons: QuestionDialogButtons,
104}
105
106#[repr(C)]
107#[derive(Debug, Copy, Clone, PartialEq, Eq)]
108/// An enum that holds all possible replies from a dialog.
109/// Can be converted from a `u32` if needed.
110/// # Example
111/// ```rust
112/// extern crate nvdialog_rs;
113/// use nvdialog_rs::{
114///     QuestionDialog,
115///     QuestionDialogButtons
116/// }
117///
118/// fn main() {
119///     let dialog = QuestionDialog::new(
120///             "title",
121///             "message",
122///             &QuestionDialogButtons::YesNoCancel
123///     );
124///     println!("Reply from dialog: {:?}"), dialog.get_reply());
125/// }
126/// ```
127/// # Members
128/// - `Accepted` -> Corresponds to `NVD_REPLY_OK`, returned if user pressed the Okay button.
129/// - `Cancelled` -> Corresponds to `NVD_REPLY_CANCEL`, returned if user pressed the Cancel button.
130/// - `Rejected` -> Corresponds to `NVD_REPLY_NO`, returned if user pressed the No button.
131/// # Errors
132/// In order to work with raw C integers, a conversion is done (See the `From` trait for details). If the
133/// integer given is not valid though, then the return value will always be `NVD_REPLY_CANCEL` to comply
134/// with Rust's safety rules.
135pub enum Reply {
136    Accepted = 0x04,
137    Cancelled,
138    Rejected,
139}
140
141impl QuestionDialog {
142    /// Creates a new `QuestionDialog` with the specified title, message, and
143    /// buttons.
144    ///
145    /// # Arguments
146    /// * `title` - A string slice or reference that contains the title of the dialog box.
147    /// * `msg` - A string slice or reference that contains the message to display in the dialog box.
148    /// * `buttons` - A `QuestionDialogButtons` enum that specifies the buttons to display in the dialog box.
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// let question_dialog = QuestionDialog::new(
154    ///     "Are you sure you want to delete this file?",
155    ///     "This action cannot be undone.",
156    ///     QuestionDialogButtons::YesNo,
157    /// );
158    /// ```
159
160    pub fn new<S: AsRef<str>>(title: S, msg: S, buttons: QuestionDialogButtons) -> Self {
161        let t = cstr!(title.as_ref());
162        let q = cstr!(msg.as_ref());
163        Self {
164            raw: unsafe {
165                nvd_dialog_question_new(t.as_ptr(), q.as_ptr(), buttons.clone() as c_uint)
166            },
167            title: String::from(title.as_ref()),
168            msg: String::from(msg.as_ref()),
169            buttons,
170        }
171    }
172
173    /// Returns the user's reply to the question displayed in the dialog box.
174    ///
175    /// # Examples
176    /// ```
177    /// let question_dialog = QuestionDialog::new(
178    ///     "Are you sure you want to delete this file?",
179    ///     "This action cannot be undone.",
180    ///     QuestionDialogButtons::YesNo,
181    /// );
182    /// let reply = question_dialog.get_reply();
183    /// if reply == Reply::Yes {
184    ///     // Delete the file.
185    /// } else {
186    ///     // Do nothing.
187    /// }
188    /// ```
189    pub fn get_reply(&self) -> Reply {
190        Reply::from(unsafe { nvd_get_reply(self.raw) })
191    }
192}
193
194impl From<u32> for Reply {
195    fn from(value: u32) -> Self {
196        if value == Reply::Accepted as u32 {
197            Reply::Accepted.into()
198        } else if value == Reply::Cancelled as u32 {
199            Reply::Cancelled
200        } else if value == Reply::Rejected as u32 {
201            Reply::Rejected
202        } else {
203            Reply::Cancelled
204        }
205    }
206}
207
208impl Object for QuestionDialog {
209    type NativeType = NvdQuestionBox;
210    type ReturnValue = Reply;
211
212    fn get_raw(&self) -> *mut NvdQuestionBox {
213        self.raw
214    }
215
216    fn show(&self) -> Reply {
217        self.get_reply()
218    }
219
220    fn free(&mut self) {
221        unsafe {
222            nvd_free_object(self.raw as *mut c_void);
223        }
224    }
225}
226
227impl Drop for QuestionDialog {
228    fn drop(&mut self) {
229        self.free();
230    }
231}