use core::mem; use crate::{ error::Error, maybe_trace::MaybeTrace, strutil::TString, translations::TR, ui::{ component::{ base::Never, painter, Child, Component, ComponentExt, Empty, Event, EventCtx, Label, Split, }, display::loader::{loader_circular_uncompress, LoaderDimensions}, geometry::{Insets, Rect}, util::animation_disabled, }, }; use super::{theme, Frame}; const RECTANGLE_HEIGHT: i16 = 56; const LABEL_TOP: i16 = 135; const LOADER_OUTER: i16 = 39; const LOADER_INNER: i16 = 28; const LOADER_OFFSET: i16 = -34; const LOADER_SPEED: u16 = 5; pub struct CoinJoinProgress { value: u16, indeterminate: bool, content: Child>>, // Label is not a child since circular loader paints large black rectangle which overlaps it. // To work around this, draw label every time loader is drawn. label: Label<'static>, } impl CoinJoinProgress { pub fn new( text: TString<'static>, indeterminate: bool, ) -> Result + MaybeTrace>, Error> { let style = theme::label_coinjoin_progress(); let label = Label::centered(TR::coinjoin__title_do_not_disconnect.into(), style) .vertically_centered(); let bg = painter::rect_painter(style.background_color, theme::BG); let inner = (bg, label); CoinJoinProgress::with_background(text, inner, indeterminate) } } impl CoinJoinProgress where U: Component, { pub fn with_background( text: TString<'static>, inner: U, indeterminate: bool, ) -> Result { Ok(Self { value: 0, indeterminate, content: Frame::centered( theme::label_title(), TR::coinjoin__title_progress.into(), Split::bottom(RECTANGLE_HEIGHT, 0, Empty, inner), ) .into_child(), label: Label::centered(text, theme::TEXT_NORMAL), }) } } impl Component for CoinJoinProgress where U: Component, { type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { self.content.place(bounds); let label_bounds = bounds.inset(Insets::top(LABEL_TOP)); self.label.place(label_bounds); bounds } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { self.content.event(ctx, event); self.label.event(ctx, event); match event { _ if animation_disabled() => { return None; } Event::Attach if self.indeterminate => { ctx.request_anim_frame(); } Event::Timer(EventCtx::ANIM_FRAME_TIMER) => { self.value = (self.value + LOADER_SPEED) % 1000; ctx.request_anim_frame(); ctx.request_paint(); } Event::Progress(new_value, _new_description) => { if mem::replace(&mut self.value, new_value) != new_value { ctx.request_paint(); } } _ => {} } None } fn paint(&mut self) { self.content.paint(); loader_circular_uncompress( LoaderDimensions::new(LOADER_OUTER, LOADER_INNER), LOADER_OFFSET, theme::FG, theme::BG, self.value, self.indeterminate, None, ); self.label.paint(); } } #[cfg(feature = "ui_debug")] impl crate::trace::Trace for CoinJoinProgress where U: Component + crate::trace::Trace, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("CoinJoinProgress"); t.child("label", &self.label); t.child("content", &self.content); } }