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}