use anyhow::Result;
use crate::Installer;
#[doc(hidden)]
pub type InstallCallback = Box<dyn FnOnce(&mut Installer) -> Result<()> + Send + 'static>;
#[doc(hidden)]
pub type OnEnterCallback = Box<dyn Fn(&mut Installer) -> Result<()> + 'static>;
#[doc(hidden)]
pub type OnBeforeLeaveCallback = Box<dyn Fn(&mut Installer) -> Result<bool> + 'static>;
#[doc(hidden)]
pub type SkipIfCallback = Box<dyn Fn(&Installer) -> bool + 'static>;
#[doc(hidden)]
pub struct WizardConfig {
pub title: String,
pub pages: Vec<ConfiguredPage>,
pub buttons: ButtonLabels,
}
#[doc(hidden)]
pub struct ConfiguredPage {
pub page: WizardPage,
pub on_enter: Option<OnEnterCallback>,
pub on_before_leave: Option<OnBeforeLeaveCallback>,
pub skip_if: Option<SkipIfCallback>,
}
impl ConfiguredPage {
pub fn new(page: WizardPage) -> Self {
Self {
page,
on_enter: None,
on_before_leave: None,
skip_if: None,
}
}
}
pub struct ButtonLabels {
pub back: String,
pub next: String,
pub install: String,
pub uninstall: String,
pub finish: String,
pub cancel: String,
}
impl Default for ButtonLabels {
fn default() -> Self {
Self {
back: "< Back".into(),
next: "Next >".into(),
install: "Install".into(),
uninstall: "Uninstall".into(),
finish: "Finish".into(),
cancel: "Cancel".into(),
}
}
}
#[doc(hidden)]
pub enum WizardPage {
Welcome {
title: String,
message: String,
widgets: Vec<CustomWidget>,
},
License {
heading: String,
text: String,
accept_label: String,
},
Components {
heading: String,
label: String,
},
Install {
callback: InstallCallback,
is_uninstall: bool,
show_log: bool,
},
Finish {
title: String,
message: String,
widgets: Vec<CustomWidget>,
},
Error {
title: String,
message: String,
},
Custom {
heading: String,
label: String,
widgets: Vec<CustomWidget>,
},
}
#[doc(hidden)]
#[derive(Clone, Debug)]
pub enum CustomWidget {
Text {
key: String,
label: String,
password: bool,
},
Multiline {
key: String,
label: String,
rows: u32,
},
Number { key: String, label: String },
Checkbox { key: String, label: String },
Dropdown {
key: String,
label: String,
choices: Vec<(String, String)>,
},
Radio {
key: String,
label: String,
choices: Vec<(String, String)>,
},
FilePicker {
key: String,
label: String,
filters: Vec<(String, String)>,
},
DirPicker { key: String, label: String },
}
pub struct CustomPageBuilder {
pub(crate) widgets: Vec<CustomWidget>,
}
impl CustomPageBuilder {
pub(crate) fn new() -> Self {
Self {
widgets: Vec::new(),
}
}
pub fn text(&mut self, key: impl AsRef<str>, label: impl AsRef<str>) -> &mut Self {
self.widgets.push(CustomWidget::Text {
key: key.as_ref().into(),
label: label.as_ref().into(),
password: false,
});
self
}
pub fn password(&mut self, key: impl AsRef<str>, label: impl AsRef<str>) -> &mut Self {
self.widgets.push(CustomWidget::Text {
key: key.as_ref().into(),
label: label.as_ref().into(),
password: true,
});
self
}
pub fn checkbox(&mut self, key: impl AsRef<str>, label: impl AsRef<str>) -> &mut Self {
self.widgets.push(CustomWidget::Checkbox {
key: key.as_ref().into(),
label: label.as_ref().into(),
});
self
}
pub fn dropdown(
&mut self,
key: impl AsRef<str>,
label: impl AsRef<str>,
choices: &[(&str, &str)],
) -> &mut Self {
self.widgets.push(CustomWidget::Dropdown {
key: key.as_ref().into(),
label: label.as_ref().into(),
choices: choices
.iter()
.map(|(v, d)| ((*v).into(), (*d).into()))
.collect(),
});
self
}
pub fn radio(
&mut self,
key: impl AsRef<str>,
label: impl AsRef<str>,
choices: &[(&str, &str)],
) -> &mut Self {
self.widgets.push(CustomWidget::Radio {
key: key.as_ref().into(),
label: label.as_ref().into(),
choices: choices
.iter()
.map(|(v, d)| ((*v).into(), (*d).into()))
.collect(),
});
self
}
pub fn number(&mut self, key: impl AsRef<str>, label: impl AsRef<str>) -> &mut Self {
self.widgets.push(CustomWidget::Number {
key: key.as_ref().into(),
label: label.as_ref().into(),
});
self
}
pub fn multiline(
&mut self,
key: impl AsRef<str>,
label: impl AsRef<str>,
rows: u32,
) -> &mut Self {
self.widgets.push(CustomWidget::Multiline {
key: key.as_ref().into(),
label: label.as_ref().into(),
rows,
});
self
}
pub fn file_picker(
&mut self,
key: impl AsRef<str>,
label: impl AsRef<str>,
filters: &[(&str, &str)],
) -> &mut Self {
self.widgets.push(CustomWidget::FilePicker {
key: key.as_ref().into(),
label: label.as_ref().into(),
filters: filters
.iter()
.map(|(d, p)| ((*d).into(), (*p).into()))
.collect(),
});
self
}
pub fn dir_picker(&mut self, key: impl AsRef<str>, label: impl AsRef<str>) -> &mut Self {
self.widgets.push(CustomWidget::DirPicker {
key: key.as_ref().into(),
label: label.as_ref().into(),
});
self
}
}
pub(crate) enum GuiMessage {
SetStatus(String),
SetProgress(f64),
Log(String),
Finished(Result<()>),
}
pub(crate) struct ChannelSink {
tx: std::sync::mpsc::Sender<GuiMessage>,
}
impl ChannelSink {
pub(crate) fn new(tx: std::sync::mpsc::Sender<GuiMessage>) -> Self {
Self { tx }
}
}
impl crate::ProgressSink for ChannelSink {
fn set_status(&self, status: &str) {
let _ = self.tx.send(GuiMessage::SetStatus(status.to_string()));
}
fn set_progress(&self, fraction: f64) {
let _ = self.tx.send(GuiMessage::SetProgress(fraction));
}
fn log(&self, message: &str) {
let _ = self.tx.send(GuiMessage::Log(message.to_string()));
}
}