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.
trezor-firmware/core/embed/rust/src/ui/component/maybe.rs

146 lines
3.4 KiB

use crate::ui::{
component::{Component, ComponentExt, Event, EventCtx, Pad},
display::{self, Color},
geometry::Rect,
};
pub struct Maybe<T> {
inner: T,
pad: Pad,
visible: bool,
}
impl<T> Maybe<T> {
pub fn new(pad: Pad, inner: T, visible: bool) -> Self {
Self {
inner,
visible,
pad,
}
}
pub fn visible(clear: Color, inner: T) -> Self {
Self::new(Pad::with_background(clear), inner, true)
}
pub fn hidden(clear: Color, inner: T) -> Self {
Self::new(Pad::with_background(clear), inner, false)
}
}
impl<T> Maybe<T>
where
T: Component,
{
pub fn show_if(&mut self, ctx: &mut EventCtx, show: bool) {
if self.visible != show {
self.visible = show;
// Invalidate the pad, so either we prepare a fresh canvas for the content, or
// paint over it.
self.pad.clear();
if show {
// Make sure the whole inner tree is painted.
self.inner.request_complete_repaint(ctx);
} else {
// Just make sure out `paint` method is called, to clear the pad.
ctx.request_paint();
}
}
}
pub fn show(&mut self, ctx: &mut EventCtx) {
self.show_if(ctx, true)
}
pub fn hide(&mut self, ctx: &mut EventCtx) {
self.show_if(ctx, false)
}
pub fn inner(&self) -> &T {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<T> Component for Maybe<T>
where
T: Component,
{
type Msg = T::Msg;
fn place(&mut self, bounds: Rect) -> Rect {
let area = self.inner.place(bounds);
self.pad.place(area);
area
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if self.visible {
self.inner.event(ctx, event)
} else {
None
}
}
fn paint(&mut self) {
self.pad.paint();
if self.visible {
self.inner.paint();
}
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
sink(self.pad.area);
self.inner.bounds(sink);
}
}
pub trait PaintOverlapping {
/// Return area that would be cleared during regular paint, along with
/// background color, or None if clearing isn't requested.
fn cleared_area(&self) -> Option<(Rect, Color)>;
/// Paint the component but do not clear background beforehand.
fn paint_overlapping(&mut self);
}
impl<T> PaintOverlapping for Maybe<T>
where
T: Component,
{
fn cleared_area(&self) -> Option<(Rect, Color)> {
self.pad.will_paint()
}
fn paint_overlapping(&mut self) {
self.pad.cancel_clear();
self.paint()
}
}
/// Paint multiple Maybe<T> components, correctly handling clearing of
/// background in the case the areas overlap, i.e. clear the combined area first
/// and then paint over it.
pub fn paint_overlapping(components: &mut [&mut dyn PaintOverlapping]) {
let mut area = Rect::zero();
let mut color = Color::rgb(0, 0, 0);
for component in components.iter() {
if let Some((clear_area, clear_color)) = component.cleared_area() {
area = area.union(clear_area);
color = clear_color;
}
}
if area != Rect::zero() {
display::rect_fill(area, color)
}
for component in components.iter_mut() {
component.paint_overlapping()
}
}