toast/
lib.rs

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//! This crate provides a simplified mechanism for producing native modal
//! dialog toast messages to the underlying system.
//!
//! Dialogs are formed by creating basic builders which will ultimately create
//! and show the modal dialog until the user responds to the message.
//!
//! This has been achieved through the [`boxer_sys`] Crate, which wraps the
//! original C++ Boxer library in C bindings.
#![deny(missing_docs)]
#![deny(unused_results)]
#![deny(rust_2018_idioms)]

use boxer_sys as c;

/// The button that was selected by the user
pub enum Selection {
  /// The user selected OK from an [`Buttons::OK`] or [`Buttons::OKCancel`]
  OK,
  /// The user selected Cancel from an [`Buttons::OKCancel`]
  Cancel,
  /// The user selected "Yes" from a [`Buttons::YesNo`]
  Yes,
  /// The user selected "No" from a [`Buttons::YesNo`]
  No,
  /// The user selected "Quit" from a [`Buttons::Quit`]
  Quit,
  /// The user did not select a button
  None,
  /// An internal error occurred.
  Error,
}

impl Selection {
  fn from_c(value: c::BoxerSelection) -> Self {
    match value {
      c::BoxerSelection_OKSelection => Self::OK,
      c::BoxerSelection_CancelSelection => Self::Cancel,
      c::BoxerSelection_YesSelection => Self::Yes,
      c::BoxerSelection_NoSelection => Self::No,
      c::BoxerSelection_QuitSelection => Self::Quit,
      c::BoxerSelection_NoneSelection => Self::None,
      c::BoxerSelection_ErrorSelection => Self::Error,
      _ => Self::Error,
    }
  }
}

/// The buttons to present to the user.
pub enum Buttons {
  /// A typical dialog with just an "OK" button.
  OK,
  /// A typical dialog with "OK" or "Cancel" buttons.
  OKCancel,
  /// A question dialog box with "Yes" or "No" buttons.
  YesNo,
  /// A dialog that only offers a "quit" button
  Quit,
}

impl Buttons {
  fn to_c(&self) -> c::BoxerButtons {
    match self {
      Self::OK => c::BoxerButtons_OKButtons,
      Self::OKCancel => c::BoxerButtons_OKCancelButtons,
      Self::YesNo => c::BoxerButtons_YesNoButtons,
      Self::Quit => c::BoxerButtons_QuitButtons,
    }
  }
}

/// The style that the window should be created in.
enum Style {
  Info,
  Warning,
  Error,
  Question,
}

impl Style {
  fn to_c(&self) -> c::BoxerStyle {
    match self {
      Self::Info => c::BoxerStyle_InfoStyle,
      Self::Warning => c::BoxerStyle_WarningStyle,
      Self::Error => c::BoxerStyle_ErrorStyle,
      Self::Question => c::BoxerStyle_QuestionStyle,
    }
  }
}

use std::ffi::CStr;

/// The main builder class for creating a dialog window.
pub struct ToastBuilder<'a, 'b> {
  style: Style,
  title: &'a CStr,
  message: &'b CStr,
  buttons: Buttons,
}

impl<'a, 'b> ToastBuilder<'a, 'b> {
  /// Create an info toast builder
  pub fn info() -> Self {
    Self::from_style(Style::Info)
  }

  /// Creates a warning toast builder
  pub fn warning() -> Self {
    Self::from_style(Style::Warning)
  }

  /// Creates an error toast builder
  pub fn error() -> Self {
    Self::from_style(Style::Error)
  }

  /// Creates a question toast builder
  pub fn question() -> Self {
    Self::from_style(Style::Question)
  }

  /// Sets the message string to display to the user.
  ///
  /// # Parameters
  ///
  /// * `message` - the message [`CStr`]` to display.
  pub fn message_cstr(mut self, message: &'b CStr) -> Self {
    self.message = message;
    self
  }

  /// Sets the title string to display to the user.
  ///
  /// # Parameters
  ///
  /// * `title` - the title [`CStr`]` to use.
  pub fn title_cstr(mut self, title: &'a CStr) -> Self {
    self.title = title;
    self
  }

  /// Sets the button style to display to the user.
  ///
  /// # Parameters
  ///
  /// * `buttons` - the buttons to display to the user.
  pub fn buttons(mut self, buttons: Buttons) -> Self {
    self.buttons = buttons;
    self
  }

  /// Show the window
  pub fn show(self) -> Selection {
    Selection::from_c(unsafe {
      Self::show_cstr(self.message, self.title, self.style, self.buttons)
    })
  }

  /// Creates a builder with sensible defaults in the given style.
  ///
  /// # Parameters
  ///
  /// * `s` - the style to create a builder for
  fn from_style(s: Style) -> Self {
    Self {
      style: s,
      title: astd::ffi::cstr!("untitled"),
      message: astd::ffi::cstr!(""),
      buttons: Buttons::OK,
    }
  }

  /// Shows the underlying message to the user.
  unsafe fn show_cstr(message: &CStr, title: &CStr, style: Style, buttons: Buttons) -> u32 {
    c::boxer_show(
      message.as_ptr(),
      title.as_ptr(),
      style.to_c(),
      buttons.to_c(),
    )
  }
}