You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
6.8 KiB
219 lines
6.8 KiB
use crate::ui::{
|
|
component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad},
|
|
display::{self, Color, Font},
|
|
geometry::{Point, Rect},
|
|
};
|
|
|
|
use super::{
|
|
super::{
|
|
component::{ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos},
|
|
theme::{BUTTON_HEIGHT, TITLE_AREA_HEIGHT},
|
|
},
|
|
theme::WHITE,
|
|
ReturnToC,
|
|
};
|
|
|
|
const ALERT_AREA_START: i16 = 39;
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub enum ConfirmMsg {
|
|
Cancel = 1,
|
|
Confirm = 2,
|
|
}
|
|
|
|
impl ReturnToC for ConfirmMsg {
|
|
fn return_to_c(self) -> u32 {
|
|
self as u32
|
|
}
|
|
}
|
|
|
|
pub struct Confirm<'a> {
|
|
bg: Pad,
|
|
bg_color: Color,
|
|
title: &'static str,
|
|
message: Child<Label<&'a str>>,
|
|
alert: Option<Label<&'a str>>,
|
|
info_title: Option<&'static str>,
|
|
info_text: Option<Label<&'a str>>,
|
|
button_text: &'static str,
|
|
buttons: ButtonController<&'static str>,
|
|
/// Whether we are on the info screen (optional extra screen)
|
|
showing_info_screen: bool,
|
|
two_btn_confirm: bool,
|
|
}
|
|
|
|
impl<'a> Confirm<'a> {
|
|
pub fn new(
|
|
bg_color: Color,
|
|
title: &'static str,
|
|
message: Label<&'a str>,
|
|
alert: Option<Label<&'a str>>,
|
|
button_text: &'static str,
|
|
two_btn_confirm: bool,
|
|
) -> Self {
|
|
let btn_layout =
|
|
Self::get_button_layout_general(false, button_text, false, two_btn_confirm);
|
|
Self {
|
|
bg: Pad::with_background(bg_color).with_clear(),
|
|
bg_color,
|
|
title,
|
|
message: Child::new(message),
|
|
alert,
|
|
info_title: None,
|
|
info_text: None,
|
|
button_text,
|
|
buttons: ButtonController::new(btn_layout),
|
|
showing_info_screen: false,
|
|
two_btn_confirm,
|
|
}
|
|
}
|
|
|
|
/// Adding optional info screen
|
|
pub fn with_info_screen(mut self, info_title: &'static str, info_text: Label<&'a str>) -> Self {
|
|
self.info_title = Some(info_title);
|
|
self.info_text = Some(info_text);
|
|
self.buttons = ButtonController::new(self.get_button_layout());
|
|
self
|
|
}
|
|
|
|
fn has_info_screen(&self) -> bool {
|
|
self.info_title.is_some()
|
|
}
|
|
|
|
fn get_button_layout(&self) -> ButtonLayout<&'static str> {
|
|
Self::get_button_layout_general(
|
|
self.showing_info_screen,
|
|
self.button_text,
|
|
self.has_info_screen(),
|
|
self.two_btn_confirm,
|
|
)
|
|
}
|
|
|
|
/// Not relying on self here, to call it in constructor.
|
|
fn get_button_layout_general(
|
|
showing_info_screen: bool,
|
|
button_text: &'static str,
|
|
has_info_screen: bool,
|
|
two_btn_confirm: bool,
|
|
) -> ButtonLayout<&'static str> {
|
|
if showing_info_screen {
|
|
ButtonLayout::arrow_none_none()
|
|
} else if has_info_screen {
|
|
ButtonLayout::cancel_armed_info(button_text)
|
|
} else if two_btn_confirm {
|
|
ButtonLayout::cancel_armed_none(button_text)
|
|
} else {
|
|
ButtonLayout::cancel_none_text(button_text)
|
|
}
|
|
}
|
|
|
|
/// Reflecting the current page in the buttons.
|
|
fn update_buttons(&mut self) {
|
|
let btn_layout = self.get_button_layout();
|
|
self.buttons.set(btn_layout);
|
|
}
|
|
|
|
fn update_everything(&mut self, ctx: &mut EventCtx) {
|
|
self.bg.clear();
|
|
self.update_buttons();
|
|
self.info_text.request_complete_repaint(ctx);
|
|
self.message.request_complete_repaint(ctx);
|
|
self.alert.request_complete_repaint(ctx);
|
|
self.buttons.request_complete_repaint(ctx);
|
|
self.request_complete_repaint(ctx);
|
|
}
|
|
}
|
|
|
|
impl<'a> Component for Confirm<'a> {
|
|
type Msg = ConfirmMsg;
|
|
|
|
fn place(&mut self, bounds: Rect) -> Rect {
|
|
self.bg.place(bounds);
|
|
|
|
// Divide the screen into areas
|
|
let (_title_area, minus_title) = bounds.split_top(TITLE_AREA_HEIGHT);
|
|
let (between_title_and_buttons, button_area) = minus_title.split_bottom(BUTTON_HEIGHT);
|
|
|
|
// Texts for the main screen
|
|
let (message_area, alert_area) = if self.alert.is_some() {
|
|
between_title_and_buttons.split_top(ALERT_AREA_START - TITLE_AREA_HEIGHT)
|
|
} else {
|
|
(between_title_and_buttons, Rect::zero())
|
|
};
|
|
self.message.place(message_area);
|
|
self.alert.place(alert_area);
|
|
|
|
// Text for the info screen
|
|
self.info_text.place(between_title_and_buttons);
|
|
|
|
self.buttons.place(button_area);
|
|
|
|
bounds
|
|
}
|
|
|
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
|
let msg = self.buttons.event(ctx, event);
|
|
if self.showing_info_screen {
|
|
// Showing the info screen currently - going back with the left button
|
|
if let Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) = msg {
|
|
self.showing_info_screen = false;
|
|
self.update_everything(ctx);
|
|
};
|
|
None
|
|
} else if self.has_info_screen() {
|
|
// Being on the "main" screen but with an info screen available on the right
|
|
match msg {
|
|
Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) => Some(ConfirmMsg::Cancel),
|
|
Some(ButtonControllerMsg::Triggered(ButtonPos::Middle)) => {
|
|
Some(ConfirmMsg::Confirm)
|
|
}
|
|
Some(ButtonControllerMsg::Triggered(ButtonPos::Right)) => {
|
|
self.showing_info_screen = true;
|
|
self.update_everything(ctx);
|
|
None
|
|
}
|
|
_ => None,
|
|
}
|
|
} else if self.two_btn_confirm {
|
|
match msg {
|
|
Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) => Some(ConfirmMsg::Cancel),
|
|
Some(ButtonControllerMsg::Triggered(ButtonPos::Middle)) => {
|
|
Some(ConfirmMsg::Confirm)
|
|
}
|
|
_ => None,
|
|
}
|
|
} else {
|
|
// There is just one main screen without info screen
|
|
match msg {
|
|
Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) => Some(ConfirmMsg::Cancel),
|
|
Some(ButtonControllerMsg::Triggered(ButtonPos::Right)) => Some(ConfirmMsg::Confirm),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn paint(&mut self) {
|
|
self.bg.paint();
|
|
|
|
let display_top_left = |text: &str| {
|
|
display::text_top_left(Point::zero(), text, Font::BOLD, WHITE, self.bg_color);
|
|
};
|
|
|
|
// We are either on the info screen or on the "main" screen
|
|
if self.showing_info_screen {
|
|
display_top_left(unwrap!(self.info_title));
|
|
self.info_text.paint();
|
|
} else {
|
|
display_top_left(self.title);
|
|
self.message.paint();
|
|
self.alert.paint();
|
|
}
|
|
self.buttons.paint();
|
|
}
|
|
|
|
#[cfg(feature = "ui_bounds")]
|
|
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
|
self.buttons.bounds(sink);
|
|
}
|
|
}
|