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.
136 lines
3.7 KiB
136 lines
3.7 KiB
use crate::{
|
|
strutil::TString,
|
|
time::{Duration, Instant},
|
|
ui::{
|
|
component::{Component, Event, EventCtx},
|
|
event::ButtonEvent,
|
|
geometry::Rect,
|
|
},
|
|
};
|
|
|
|
use super::{
|
|
loader::{Loader, DEFAULT_DURATION_MS},
|
|
theme, ButtonContent, ButtonDetails, ButtonPos, LoaderMsg, LoaderStyleSheet,
|
|
};
|
|
|
|
pub enum HoldToConfirmMsg {
|
|
Confirmed,
|
|
FailedToConfirm,
|
|
}
|
|
|
|
pub struct HoldToConfirm {
|
|
pos: ButtonPos,
|
|
loader: Loader,
|
|
text_width: i16,
|
|
}
|
|
|
|
impl HoldToConfirm {
|
|
pub fn text<T: Into<TString<'static>>>(
|
|
pos: ButtonPos,
|
|
text: T,
|
|
styles: LoaderStyleSheet,
|
|
duration: Duration,
|
|
) -> Self {
|
|
let text = text.into();
|
|
let text_width = text.map(|t| styles.normal.font.visible_text_width(t));
|
|
Self {
|
|
pos,
|
|
loader: Loader::text(text, styles).with_growing_duration(duration),
|
|
text_width,
|
|
}
|
|
}
|
|
|
|
pub fn from_button_details(pos: ButtonPos, btn_details: ButtonDetails) -> Self {
|
|
let duration = btn_details
|
|
.duration
|
|
.unwrap_or_else(|| Duration::from_millis(DEFAULT_DURATION_MS));
|
|
match btn_details.content {
|
|
ButtonContent::Text(text) => {
|
|
Self::text(pos, text, LoaderStyleSheet::default_loader(), duration)
|
|
}
|
|
ButtonContent::Icon(_) => panic!("Icon is not supported"),
|
|
}
|
|
}
|
|
|
|
/// Updating the text of the component and re-placing it.
|
|
pub fn set_text<T: Into<TString<'static>>>(&mut self, text: T, button_area: Rect) {
|
|
let text = text.into();
|
|
self.text_width = self.loader.get_text_width(&text);
|
|
self.loader.set_text(text);
|
|
self.place(button_area);
|
|
}
|
|
|
|
pub fn reset(&mut self) {
|
|
self.loader.reset();
|
|
}
|
|
|
|
pub fn set_duration(&mut self, duration: Duration) {
|
|
self.loader.set_duration(duration);
|
|
}
|
|
|
|
pub fn get_duration(&self) -> Duration {
|
|
self.loader.get_duration()
|
|
}
|
|
|
|
pub fn get_text(&self) -> TString<'static> {
|
|
self.loader.get_text()
|
|
}
|
|
|
|
fn placement(&mut self, area: Rect, pos: ButtonPos) -> Rect {
|
|
let button_width = self.text_width + 2 * theme::BUTTON_OUTLINE;
|
|
match pos {
|
|
ButtonPos::Left => area.split_left(button_width).0,
|
|
ButtonPos::Right => area.split_right(button_width).1,
|
|
ButtonPos::Middle => area.split_center(button_width).1,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Component for HoldToConfirm {
|
|
type Msg = HoldToConfirmMsg;
|
|
|
|
fn place(&mut self, bounds: Rect) -> Rect {
|
|
let loader_area = self.placement(bounds, self.pos);
|
|
self.loader.place(loader_area)
|
|
}
|
|
|
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
|
match event {
|
|
Event::Button(ButtonEvent::HoldStarted) => {
|
|
self.loader.start_growing(ctx, Instant::now());
|
|
}
|
|
Event::Button(ButtonEvent::HoldEnded) => {
|
|
if self.loader.is_animating() {
|
|
self.loader.start_shrinking(ctx, Instant::now());
|
|
}
|
|
}
|
|
_ => {}
|
|
};
|
|
|
|
let msg = self.loader.event(ctx, event);
|
|
|
|
if let Some(LoaderMsg::GrownCompletely) = msg {
|
|
return Some(HoldToConfirmMsg::Confirmed);
|
|
}
|
|
if let Some(LoaderMsg::ShrunkCompletely) = msg {
|
|
return Some(HoldToConfirmMsg::FailedToConfirm);
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
fn paint(&mut self) {
|
|
self.loader.paint();
|
|
}
|
|
}
|
|
|
|
// DEBUG-ONLY SECTION BELOW
|
|
|
|
#[cfg(feature = "ui_debug")]
|
|
impl crate::trace::Trace for HoldToConfirm {
|
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
|
t.component("HoldToConfirm");
|
|
t.child("loader", &self.loader);
|
|
}
|
|
}
|