1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-19 12:58:13 +00:00

refactor(core/mercury): remove unnecessary Child

[no changelog]
This commit is contained in:
Martin Milata 2024-07-18 17:19:22 +02:00
parent 1212a7319a
commit 888e384f79
10 changed files with 140 additions and 198 deletions

View File

@ -6,9 +6,7 @@ use crate::{
strutil::TString,
translations::TR,
ui::{
component::{
base::Never, Bar, Child, Component, ComponentExt, Empty, Event, EventCtx, Label, Split,
},
component::{base::Never, Bar, Component, Empty, Event, EventCtx, Label, Split},
display::loader::{loader_circular_uncompress, LoaderDimensions},
geometry::{Insets, Offset, Rect},
model_mercury::constant,
@ -30,7 +28,7 @@ const LOADER_SPEED: u16 = 5;
pub struct CoinJoinProgress<U> {
value: u16,
indeterminate: bool,
content: Child<Frame<Split<Empty, U>>>,
content: Frame<Split<Empty, U>>,
// 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>,
@ -65,8 +63,7 @@ where
content: Frame::centered(
TR::coinjoin__title_progress.into(),
Split::bottom(RECTANGLE_HEIGHT, 0, Empty, inner),
)
.into_child(),
),
label: Label::centered(text, theme::TEXT_NORMAL),
})
}

View File

@ -1,7 +1,7 @@
use crate::{
strutil::TString,
ui::{
component::{Child, Component, Event, EventCtx, Label, Never, Pad},
component::{Component, Event, EventCtx, Label, Never, Pad},
constant::screen,
geometry::{Alignment2D, Point, Rect},
shape,
@ -26,9 +26,9 @@ const STYLE: &ResultStyle = &super::theme::RESULT_ERROR;
pub struct ErrorScreen<'a> {
bg: Pad,
title: Child<Label<'a>>,
message: Child<Label<'a>>,
footer: Child<ResultFooter<'a>>,
title: Label<'a>,
message: Label<'a>,
footer: ResultFooter<'a>,
}
impl<'a> ErrorScreen<'a> {
@ -42,9 +42,9 @@ impl<'a> ErrorScreen<'a> {
Self {
bg: Pad::with_background(FATAL_ERROR_COLOR).with_clear(),
title: Child::new(title),
message: Child::new(message),
footer: Child::new(footer),
title,
message,
footer,
}
}
}

View File

@ -1,9 +1,7 @@
use crate::{
strutil::TString,
ui::{
component::{
image::Image, Child, Component, Event, EventCtx, Label, Swipe, SwipeDirection,
},
component::{image::Image, Component, Event, EventCtx, Label, Swipe, SwipeDirection},
display,
geometry::{Insets, Rect},
model_mercury::component::{fido_icons::get_fido_icon_data, theme, ScrollBar},
@ -30,7 +28,7 @@ pub struct FidoConfirm<F: Fn(usize) -> TString<'static>, U> {
page_swipe: Swipe,
app_name: Label<'static>,
account_name: Label<'static>,
icon: Child<Image>,
icon: Image,
/// Function/closure that will return appropriate page on demand.
get_account: F,
scrollbar: ScrollBar,
@ -79,7 +77,7 @@ where
app_name: Label::centered(app_name, theme::TEXT_DEMIBOLD),
account_name: Label::centered(current_account, theme::TEXT_DEMIBOLD),
page_swipe,
icon: Child::new(Image::new(icon_data)),
icon: Image::new(icon_data),
get_account,
scrollbar,
fade: Cell::new(false),

View File

@ -2,7 +2,7 @@ use crate::{
strutil::TString,
ui::{
component::{
maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe, Swipe,
maybe::paint_overlapping, Component, Event, EventCtx, Label, Maybe, Swipe,
SwipeDirection,
},
geometry::{Alignment, Grid, Insets, Rect},
@ -24,17 +24,17 @@ pub enum MnemonicKeyboardMsg {
pub struct MnemonicKeyboard<T> {
/// Initial prompt, displayed on empty input.
prompt: Child<Maybe<Label<'static>>>,
prompt: Maybe<Label<'static>>,
/// Delete a char button.
erase: Child<Maybe<Button>>,
erase: Maybe<Button>,
/// Go to previous word button
back: Child<Maybe<Button>>,
back: Maybe<Button>,
/// Input area, acting as the auto-complete and confirm button.
input: Child<Maybe<T>>,
input: Maybe<T>,
/// Area with keypads - used for rounded overlay
keypad_area: Rect,
/// Key buttons.
keys: [Child<Button>; MNEMONIC_KEY_COUNT],
keys: [Button; MNEMONIC_KEY_COUNT],
/// Swipe controller - allowing for going to the previous word.
swipe: Swipe,
/// Whether going back is allowed (is not on the very first word).
@ -56,26 +56,20 @@ where
.styled(theme::button_default())
.with_expanded_touch_area(Insets::right(BACK_BUTTON_RIGHT_EXPAND));
Self {
prompt: Child::new(Maybe::new(
prompt: Maybe::new(
theme::BG,
Label::centered(prompt, theme::TEXT_MAIN_GREY_LIGHT).vertically_centered(),
prompt_visible,
)),
erase: Child::new(Maybe::new(theme::BG, erase_btn, !prompt_visible)),
back: Child::new(Maybe::new(
theme::BG,
back_btn,
prompt_visible && can_go_back,
)),
input: Child::new(Maybe::new(theme::BG, input, !prompt_visible)),
),
erase: Maybe::new(theme::BG, erase_btn, !prompt_visible),
back: Maybe::new(theme::BG, back_btn, prompt_visible && can_go_back),
input: Maybe::new(theme::BG, input, !prompt_visible),
keypad_area: Rect::zero(),
keys: T::keys()
.map(|t| {
Button::with_text(t.into())
.styled(theme::button_keyboard())
.with_text_align(Alignment::Center)
})
.map(Child::new),
keys: T::keys().map(|t| {
Button::with_text(t.into())
.styled(theme::button_keyboard())
.with_text_align(Alignment::Center)
}),
swipe: Swipe::new().right(),
can_go_back,
}
@ -90,32 +84,23 @@ where
/// completion mask and the pending key.
fn toggle_key_buttons(&mut self, ctx: &mut EventCtx) {
for (key, btn) in self.keys.iter_mut().enumerate() {
let enabled = self
.input
.inner()
.inner()
.can_key_press_lead_to_a_valid_word(key);
btn.mutate(ctx, |ctx, b| b.enable_if(ctx, enabled));
let enabled = self.input.inner().can_key_press_lead_to_a_valid_word(key);
btn.enable_if(ctx, enabled);
}
}
/// After edit operations, we need to either show or hide the prompt, the
/// input, the erase button and the back button.
fn toggle_prompt_or_input(&mut self, ctx: &mut EventCtx) {
let prompt_visible = self.input.inner().inner().is_empty();
self.prompt
.mutate(ctx, |ctx, p| p.show_if(ctx, prompt_visible));
self.input
.mutate(ctx, |ctx, i| i.show_if(ctx, !prompt_visible));
self.erase
.mutate(ctx, |ctx, b| b.show_if(ctx, !prompt_visible));
self.back.mutate(ctx, |ctx, b| {
b.show_if(ctx, prompt_visible && self.can_go_back)
});
let prompt_visible = self.input.inner().is_empty();
self.prompt.show_if(ctx, prompt_visible);
self.input.show_if(ctx, !prompt_visible);
self.erase.show_if(ctx, !prompt_visible);
self.back.show_if(ctx, prompt_visible && self.can_go_back);
}
pub fn mnemonic(&self) -> Option<&'static str> {
self.input.inner().inner().mnemonic()
self.input.inner().mnemonic()
}
}
@ -181,14 +166,12 @@ where
match self.erase.event(ctx, event) {
Some(ButtonMsg::Clicked) => {
self.input
.mutate(ctx, |ctx, i| i.inner_mut().on_backspace_click(ctx));
self.input.inner_mut().on_backspace_click(ctx);
self.on_input_change(ctx);
return None;
}
Some(ButtonMsg::LongPressed) => {
self.input
.mutate(ctx, |ctx, i| i.inner_mut().on_backspace_long_press(ctx));
self.input.inner_mut().on_backspace_long_press(ctx);
self.on_input_change(ctx);
return None;
}
@ -196,8 +179,7 @@ where
}
for (key, btn) in self.keys.iter_mut().enumerate() {
if let Some(ButtonMsg::Clicked) = btn.event(ctx, event) {
self.input
.mutate(ctx, |ctx, i| i.inner_mut().on_key_click(ctx, key));
self.input.inner_mut().on_key_click(ctx, key);
self.on_input_change(ctx);
return None;
}
@ -213,7 +195,7 @@ where
}
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
if self.input.inner().inner().is_empty() {
if self.input.inner().is_empty() {
self.prompt.render(target);
if self.can_go_back {
self.back.render(target);

View File

@ -3,8 +3,8 @@ use crate::{
translations::TR,
ui::{
component::{
base::ComponentExt, text::common::TextBox, Child, Component, Event, EventCtx, Label,
Maybe, Never, Swipe, SwipeDirection,
base::ComponentExt, text::common::TextBox, Component, Event, EventCtx, Label, Maybe,
Never, Swipe, SwipeDirection,
},
display,
geometry::{Alignment, Grid, Insets, Offset, Rect},
@ -75,14 +75,14 @@ impl From<KeyboardLayout> for ButtonContent {
pub struct PassphraseKeyboard {
page_swipe: Swipe,
input: Child<Input>,
input_prompt: Child<Label<'static>>,
erase_btn: Child<Maybe<Button>>,
cancel_btn: Child<Maybe<Button>>,
confirm_btn: Child<Button>,
next_btn: Child<Button>,
input: Input,
input_prompt: Label<'static>,
erase_btn: Maybe<Button>,
cancel_btn: Maybe<Button>,
confirm_btn: Button,
next_btn: Button,
keypad_area: Rect,
keys: [Child<Button>; KEY_COUNT],
keys: [Button; KEY_COUNT],
active_layout: KeyboardLayout,
fade: Cell<bool>,
}
@ -105,43 +105,38 @@ impl PassphraseKeyboard {
let confirm_btn = Button::with_icon(theme::ICON_SIMPLE_CHECKMARK24)
.styled(theme::button_passphrase_confirm())
.with_radius(15)
.into_child();
.with_radius(15);
let next_btn = Button::new(active_layout.next().into())
.styled(theme::button_passphrase_next())
.with_text_align(Alignment::Center)
.into_child();
.with_text_align(Alignment::Center);
let erase_btn = Button::with_icon(theme::ICON_DELETE)
.styled(theme::button_keyboard_erase())
.with_long_press(theme::ERASE_HOLD_DURATION)
.initially_enabled(false);
let erase_btn = Maybe::hidden(theme::BG, erase_btn).into_child();
let erase_btn = Maybe::hidden(theme::BG, erase_btn);
let cancel_btn =
Button::with_icon(theme::ICON_CLOSE).styled(theme::button_keyboard_cancel());
let cancel_btn = Maybe::visible(theme::BG, cancel_btn).into_child();
let cancel_btn = Maybe::visible(theme::BG, cancel_btn);
Self {
page_swipe: Swipe::horizontal(),
input: Input::new().into_child(),
input: Input::new(),
input_prompt: Label::left_aligned(
TString::from_translation(TR::passphrase__title_enter),
theme::label_keyboard(),
)
.into_child(),
),
erase_btn,
cancel_btn,
confirm_btn,
next_btn,
keypad_area: Rect::zero(),
keys: KEYBOARD[active_layout.to_usize().unwrap()].map(|text| {
Child::new(
Button::new(Self::key_content(text))
.styled(theme::button_keyboard())
.with_text_align(Alignment::Center),
)
Button::new(Self::key_content(text))
.styled(theme::button_keyboard())
.with_text_align(Alignment::Center)
}),
active_layout,
fade: Cell::new(false),
@ -174,8 +169,7 @@ impl PassphraseKeyboard {
_ => self.active_layout,
};
// Clear the pending state.
self.input
.mutate(ctx, |ctx, i| i.multi_tap.clear_pending_state(ctx));
self.input.multi_tap.clear_pending_state(ctx);
// Update keys.
self.replace_keys_contents(ctx);
// Reset backlight to normal level on next paint.
@ -186,13 +180,12 @@ impl PassphraseKeyboard {
}
fn replace_keys_contents(&mut self, ctx: &mut EventCtx) {
self.next_btn.mutate(ctx, |ctx, b| {
b.set_content(ctx, self.active_layout.next().into());
});
self.next_btn
.set_content(ctx, self.active_layout.next().into());
for (i, btn) in self.keys.iter_mut().enumerate() {
let text = KEYBOARD[self.active_layout.to_usize().unwrap()][i];
let content = Self::key_content(text);
btn.mutate(ctx, |ctx, b| b.set_content(ctx, content));
btn.set_content(ctx, content);
btn.request_complete_repaint(ctx);
}
}
@ -201,15 +194,11 @@ impl PassphraseKeyboard {
fn after_edit(&mut self, ctx: &mut EventCtx) {
// When the input is empty, enable cancel button. Otherwise show erase and
// confirm button.
let is_empty = self.input.inner().textbox.is_empty();
self.erase_btn.mutate(ctx, |ctx, btn| {
btn.show_if(ctx, !is_empty);
btn.inner_mut().enable_if(ctx, !is_empty);
});
self.cancel_btn.mutate(ctx, |ctx, btn| {
btn.show_if(ctx, is_empty);
btn.inner_mut().enable_if(ctx, is_empty);
});
let is_empty = self.input.textbox.is_empty();
self.erase_btn.show_if(ctx, !is_empty);
self.erase_btn.inner_mut().enable_if(ctx, !is_empty);
self.cancel_btn.show_if(ctx, is_empty);
self.cancel_btn.inner_mut().enable_if(ctx, is_empty);
self.update_input_btns_state(ctx);
}
@ -218,13 +207,11 @@ impl PassphraseKeyboard {
fn update_input_btns_state(&mut self, ctx: &mut EventCtx) {
let active_states = self.get_buttons_active_states();
for (key, btn) in self.keys.iter_mut().enumerate() {
btn.mutate(ctx, |ctx, b| {
if active_states[key] {
b.enable(ctx);
} else {
b.disable(ctx);
}
});
if active_states[key] {
btn.enable(ctx);
} else {
btn.disable(ctx);
}
}
}
@ -241,9 +228,9 @@ impl PassphraseKeyboard {
/// We should disable the input when the passphrase has reached maximum
/// length and we are not cycling through the characters.
fn is_button_active(&self, key: usize) -> bool {
let textbox_not_full = self.input.inner().textbox.len() < MAX_LENGTH;
let textbox_not_full = self.input.textbox.len() < MAX_LENGTH;
let key_is_pending = {
if let Some(pending) = self.input.inner().multi_tap.pending_key() {
if let Some(pending) = self.input.multi_tap.pending_key() {
pending == key
} else {
false
@ -253,7 +240,7 @@ impl PassphraseKeyboard {
}
pub fn passphrase(&self) -> &str {
self.input.inner().textbox.content()
self.input.textbox.content()
}
}
@ -307,9 +294,8 @@ impl Component for PassphraseKeyboard {
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if self.input.inner().multi_tap.is_timeout_event(event) {
self.input
.mutate(ctx, |ctx, i| i.multi_tap.clear_pending_state(ctx));
if self.input.multi_tap.is_timeout_event(event) {
self.input.multi_tap.clear_pending_state(ctx);
return None;
}
if let Some(swipe) = self.page_swipe.event(ctx, event) {
@ -331,18 +317,14 @@ impl Component for PassphraseKeyboard {
match self.erase_btn.event(ctx, event) {
Some(ButtonMsg::Clicked) => {
self.input.mutate(ctx, |ctx, i| {
i.multi_tap.clear_pending_state(ctx);
i.textbox.delete_last(ctx);
});
self.input.multi_tap.clear_pending_state(ctx);
self.input.textbox.delete_last(ctx);
self.after_edit(ctx);
return None;
}
Some(ButtonMsg::LongPressed) => {
self.input.mutate(ctx, |ctx, i| {
i.multi_tap.clear_pending_state(ctx);
i.textbox.clear(ctx);
});
self.input.multi_tap.clear_pending_state(ctx);
self.input.textbox.clear(ctx);
self.after_edit(ctx);
return None;
}
@ -362,11 +344,9 @@ impl Component for PassphraseKeyboard {
if let Some(ButtonMsg::Clicked) = btn.event(ctx, event) {
// Key button was clicked. If this button is pending, let's cycle the pending
// character in textbox. If not, let's just append the first character.
let text = Self::key_text(btn.inner().content());
self.input.mutate(ctx, |ctx, i| {
let edit = text.map(|c| i.multi_tap.click_key(ctx, key, c));
i.textbox.apply(ctx, edit);
});
let text = Self::key_text(btn.content());
let edit = text.map(|c| self.input.multi_tap.click_key(ctx, key, c));
self.input.textbox.apply(ctx, edit);
self.after_edit(ctx);
return None;
}
@ -383,7 +363,7 @@ impl Component for PassphraseKeyboard {
self.next_btn.render(target);
self.erase_btn.render(target);
self.confirm_btn.render(target);
if self.input.inner().textbox.is_empty() {
if self.input.textbox.is_empty() {
self.cancel_btn.render(target);
// FIXME: when prompt fixed in Figma
// self.input_prompt.render(target);

View File

@ -8,7 +8,7 @@ use crate::{
component::{
base::{AttachType, ComponentExt},
text::TextStyle,
Child, Component, Event, EventCtx, Label, Never, Pad, SwipeDirection, TimerToken,
Component, Event, EventCtx, Label, Never, Pad, SwipeDirection, TimerToken,
},
display::Font,
event::TouchEvent,
@ -246,12 +246,12 @@ pub struct PinKeyboard<'a> {
allow_cancel: bool,
show_erase: bool,
show_cancel: bool,
major_prompt: Child<Label<'a>>,
minor_prompt: Child<Label<'a>>,
major_warning: Option<Child<Label<'a>>>,
major_prompt: Label<'a>,
minor_prompt: Label<'a>,
major_warning: Option<Label<'a>>,
keypad_area: Rect,
textbox_area: Rect,
textbox: Child<PinDots>,
textbox: PinDots,
erase_btn: Button,
cancel_btn: Button,
confirm_btn: Button,
@ -282,15 +282,13 @@ impl<'a> PinKeyboard<'a> {
allow_cancel,
show_erase: false,
show_cancel: allow_cancel,
major_prompt: Label::left_aligned(major_prompt, theme::label_keyboard()).into_child(),
minor_prompt: Label::right_aligned(minor_prompt, theme::label_keyboard_minor())
.into_child(),
major_warning: major_warning.map(|text| {
Label::left_aligned(text, theme::label_keyboard_warning()).into_child()
}),
major_prompt: Label::left_aligned(major_prompt, theme::label_keyboard()),
minor_prompt: Label::right_aligned(minor_prompt, theme::label_keyboard_minor()),
major_warning: major_warning
.map(|text| Label::left_aligned(text, theme::label_keyboard_warning())),
keypad_area: Rect::zero(),
textbox_area: Rect::zero(),
textbox: PinDots::new(theme::label_default()).into_child(),
textbox: PinDots::new(theme::label_default()),
erase_btn,
cancel_btn,
confirm_btn: Button::with_icon(theme::ICON_SIMPLE_CHECKMARK24)
@ -318,8 +316,8 @@ impl<'a> PinKeyboard<'a> {
}
fn pin_modified(&mut self, ctx: &mut EventCtx) {
let is_full = self.textbox.inner().is_full();
let is_empty = self.textbox.inner().is_empty();
let is_full = self.textbox.is_full();
let is_empty = self.textbox.is_empty();
self.textbox.request_complete_repaint(ctx);
@ -343,7 +341,7 @@ impl<'a> PinKeyboard<'a> {
}
pub fn pin(&self) -> &str {
self.textbox.inner().pin()
self.textbox.pin()
}
fn get_button_alpha(&self, x: usize, y: usize, attach_time: f32, close_time: f32) -> u8 {
@ -454,12 +452,12 @@ impl Component for PinKeyboard<'_> {
}
match self.erase_btn.event(ctx, event) {
Some(ButtonMsg::Clicked) => {
self.textbox.mutate(ctx, |ctx, t| t.pop(ctx));
self.textbox.pop(ctx);
self.pin_modified(ctx);
return None;
}
Some(ButtonMsg::LongPressed) => {
self.textbox.mutate(ctx, |ctx, t| t.clear(ctx));
self.textbox.clear(ctx);
self.pin_modified(ctx);
return None;
}
@ -469,7 +467,7 @@ impl Component for PinKeyboard<'_> {
if let Some(Clicked) = btn.0.event(ctx, event) {
if let ButtonContent::Text(text) = btn.0.content() {
text.map(|text| {
self.textbox.mutate(ctx, |ctx, t| t.push(ctx, text));
self.textbox.push(ctx, text);
});
self.pin_modified(ctx);
return None;
@ -493,7 +491,7 @@ impl Component for PinKeyboard<'_> {
self.erase_btn.render_with_alpha(target, erase_alpha);
}
if self.textbox.inner().is_empty() {
if self.textbox.is_empty() {
if let Some(ref w) = self.major_warning {
w.render(target);
} else {
@ -710,7 +708,7 @@ impl crate::trace::Trace for PinKeyboard<'_> {
}
}
t.string("digits_order", digits_order.as_str().into());
t.string("pin", self.textbox.inner().pin().into());
t.bool("display_digits", self.textbox.inner().display_digits);
t.string("pin", self.textbox.pin().into());
t.bool("display_digits", self.textbox.display_digits);
}
}

View File

@ -3,9 +3,8 @@ use crate::{
strutil::{self, TString},
ui::{
component::{
base::ComponentExt,
text::paragraphs::{Paragraph, Paragraphs},
Child, Component, Event, EventCtx, Pad, SwipeDirection,
Component, Event, EventCtx, Pad, SwipeDirection,
},
display::Font,
event::SwipeEvent,
@ -23,8 +22,8 @@ pub enum NumberInputDialogMsg {
pub struct NumberInputDialog {
area: Rect,
input: Child<NumberInput>,
paragraphs: Child<Paragraphs<Paragraph<'static>>>,
input: NumberInput,
paragraphs: Paragraphs<Paragraph<'static>>,
paragraphs_pad: Pad,
}
@ -32,15 +31,14 @@ impl NumberInputDialog {
pub fn new(min: u32, max: u32, init_value: u32, text: TString<'static>) -> Result<Self, Error> {
Ok(Self {
area: Rect::zero(),
input: NumberInput::new(min, max, init_value).into_child(),
paragraphs: Paragraphs::new(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, text))
.into_child(),
input: NumberInput::new(min, max, init_value),
paragraphs: Paragraphs::new(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, text)),
paragraphs_pad: Pad::with_background(theme::BG),
})
}
pub fn value(&self) -> u32 {
self.input.inner().value
self.input.value
}
}
@ -69,7 +67,7 @@ impl Component for NumberInputDialog {
}
if let Event::Swipe(SwipeEvent::End(SwipeDirection::Up)) = event {
return Some(NumberInputDialogMsg::Confirmed(self.input.inner().value));
return Some(NumberInputDialogMsg::Confirmed(self.input.value));
}
self.paragraphs.event(ctx, event);
None
@ -101,8 +99,8 @@ pub enum NumberInputMsg {
pub struct NumberInput {
area: Rect,
dec: Child<Button>,
inc: Child<Button>,
dec: Button,
inc: Button,
min: u32,
max: u32,
value: u32,
@ -110,12 +108,8 @@ pub struct NumberInput {
impl NumberInput {
pub fn new(min: u32, max: u32, value: u32) -> Self {
let dec = Button::with_icon(theme::ICON_MINUS)
.styled(theme::button_counter())
.into_child();
let inc = Button::with_icon(theme::ICON_PLUS)
.styled(theme::button_counter())
.into_child();
let dec = Button::with_icon(theme::ICON_MINUS).styled(theme::button_counter());
let inc = Button::with_icon(theme::ICON_PLUS).styled(theme::button_counter());
let value = value.clamp(min, max);
Self {
area: Rect::zero(),
@ -150,10 +144,8 @@ impl Component for NumberInput {
changed = true;
};
if changed {
self.dec
.mutate(ctx, |ctx, btn| btn.enable_if(ctx, self.value > self.min));
self.inc
.mutate(ctx, |ctx, btn| btn.enable_if(ctx, self.value < self.max));
self.dec.enable_if(ctx, self.value > self.min);
self.inc.enable_if(ctx, self.value < self.max);
ctx.request_paint();
return Some(NumberInputMsg::Changed(self.value));
}

View File

@ -3,7 +3,7 @@ use crate::{
strutil::{ShortString, TString},
translations::TR,
ui::{
component::{base::ComponentExt, Child, Component, Event, EventCtx},
component::{Component, Event, EventCtx},
constant::screen,
display,
event::TouchEvent,
@ -18,7 +18,7 @@ pub enum NumberInputSliderDialogMsg {
pub struct NumberInputSliderDialog {
area: Rect,
input: Child<NumberInputSlider>,
input: NumberInputSlider,
footer: Footer<'static>,
min: u16,
max: u16,
@ -30,7 +30,7 @@ impl NumberInputSliderDialog {
pub fn new(min: u16, max: u16, init_value: u16) -> Self {
Self {
area: Rect::zero(),
input: NumberInputSlider::new(min, max, init_value).into_child(),
input: NumberInputSlider::new(min, max, init_value),
footer: Footer::new::<TString<'static>>(
TR::instructions__swipe_horizontally.into(),
Some(TR::setting__adjust.into()),
@ -43,7 +43,7 @@ impl NumberInputSliderDialog {
}
pub fn value(&self) -> u16 {
self.input.inner().value
self.input.value
}
}
@ -79,7 +79,7 @@ impl Component for NumberInputSliderDialog {
if let Some(value) = self.input.event(ctx, event) {
self.val = value;
if self.val == self.init_val || self.input.inner().touching {
if self.val == self.init_val || self.input.touching {
self.footer
.update_instruction(ctx, TR::instructions__swipe_horizontally);
self.footer.update_description(ctx, TR::setting__adjust);

View File

@ -4,10 +4,9 @@ use crate::{
strutil::TString,
ui::{
component::{
base::ComponentExt,
paginated::Paginate,
text::paragraphs::{Paragraph, Paragraphs},
Child, Component, Event, EventCtx, Label, Never, Pad,
Component, Event, EventCtx, Label, Never, Pad,
},
display::{self, Font, LOADER_MAX},
geometry::{Insets, Offset, Rect},
@ -23,11 +22,11 @@ use crate::{
use super::theme;
pub struct Progress {
title: Child<Label<'static>>,
title: Label<'static>,
value: u16,
loader_y_offset: i16,
indeterminate: bool,
description: Child<Paragraphs<Paragraph<'static>>>,
description: Paragraphs<Paragraph<'static>>,
description_pad: Pad,
}
@ -40,14 +39,13 @@ impl Progress {
description: TString<'static>,
) -> Self {
Self {
title: Label::centered(title, theme::label_progress()).into_child(),
title: Label::centered(title, theme::label_progress()),
value: 0,
loader_y_offset: 0,
indeterminate,
description: Paragraphs::new(
Paragraph::new(&theme::TEXT_NORMAL, description).centered(),
)
.into_child(),
),
description_pad: Pad::with_background(theme::BG),
}
}
@ -60,10 +58,9 @@ impl Component for Progress {
let description_lines = 1 + self
.description
.inner()
.inner()
.content()
.map(|t| t.chars().filter(|c| *c == '\n').count() as i16);
let (title, rest) = Self::AREA.split_top(self.title.inner().max_size().y);
let (title, rest) = Self::AREA.split_top(self.title.max_size().y);
let (loader, description) =
rest.split_bottom(Font::NORMAL.line_height() * description_lines);
let loader = loader.inset(Insets::top(theme::CONTENT_BORDER));
@ -80,14 +77,12 @@ impl Component for Progress {
if !animation_disabled() {
ctx.request_paint();
}
self.description.mutate(ctx, |ctx, para| {
if para.inner_mut().content() != &new_description {
para.inner_mut().update(new_description);
para.change_page(0); // Recompute bounding box.
ctx.request_paint();
self.description_pad.clear();
}
});
if self.description.inner_mut().content() != &new_description {
self.description.inner_mut().update(new_description);
self.description.change_page(0); // Recompute bounding box.
ctx.request_paint();
self.description_pad.clear();
}
}
}
None

View File

@ -1,7 +1,7 @@
use crate::{
strutil::TString,
ui::{
component::{text::TextStyle, Child, Component, Event, EventCtx, Label, Never, Pad},
component::{text::TextStyle, Component, Event, EventCtx, Label, Never, Pad},
constant::screen,
display::{self, Color, Font, Icon},
geometry::{Alignment2D, Insets, Offset, Point, Rect},
@ -117,8 +117,8 @@ pub struct ResultScreen<'a> {
footer_pad: Pad,
style: &'a ResultStyle,
icon: Icon,
message: Child<Label<'a>>,
footer: Child<ResultFooter<'a>>,
message: Label<'a>,
footer: ResultFooter<'a>,
}
impl<'a> ResultScreen<'a> {
@ -134,8 +134,8 @@ impl<'a> ResultScreen<'a> {
footer_pad: Pad::with_background(style.bg_color),
style,
icon,
message: Child::new(Label::centered(message, style.message_style())),
footer: Child::new(ResultFooter::new(footer, style)),
message: Label::centered(message, style.message_style()),
footer: ResultFooter::new(footer, style),
};
if complete_draw {