mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-22 04:22:07 +00:00
feat(core/rust): introduce trait PaginateFull throughout Delizia
PaginateFull uses Pager instead of reporting just the total number of pages. Delizia will rely on this trait; going forward, we'll want PaginateFull to replace Paginate, but this refactor would be too big if we also needed to include Caesar and Bolt in it
This commit is contained in:
parent
a02ba87a46
commit
68bda9cee8
@ -73,8 +73,8 @@ impl<T: crate::ui::flow::Swipable> crate::ui::flow::Swipable for SendButtonReque
|
||||
self.inner.get_swipe_config()
|
||||
}
|
||||
|
||||
fn get_internal_page_count(&self) -> usize {
|
||||
self.inner.get_internal_page_count()
|
||||
fn get_pager(&self) -> crate::ui::util::Pager {
|
||||
self.inner.get_pager()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,8 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::paginated::SinglePage;
|
||||
|
||||
pub struct CachedJpeg {
|
||||
area: Rect,
|
||||
image_size: Offset,
|
||||
@ -73,6 +75,8 @@ impl Component for CachedJpeg {
|
||||
}
|
||||
}
|
||||
|
||||
impl SinglePage for CachedJpeg {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for CachedJpeg {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -43,8 +43,8 @@ where
|
||||
fn get_swipe_config(&self) -> SwipeConfig {
|
||||
self.inner.get_swipe_config()
|
||||
}
|
||||
fn get_internal_page_count(&self) -> usize {
|
||||
self.inner.get_internal_page_count()
|
||||
fn get_pager(&self) -> crate::ui::util::Pager {
|
||||
self.inner.get_pager()
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ where
|
||||
fn get_swipe_config(&self) -> SwipeConfig {
|
||||
self.inner.get_swipe_config()
|
||||
}
|
||||
fn get_internal_page_count(&self) -> usize {
|
||||
self.inner.get_internal_page_count()
|
||||
fn get_pager(&self) -> crate::ui::util::Pager {
|
||||
self.inner.get_pager()
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ pub use map::{MsgMap, PageMap};
|
||||
pub use marquee::Marquee;
|
||||
pub use maybe::Maybe;
|
||||
pub use pad::Pad;
|
||||
pub use paginated::{PageMsg, Paginate};
|
||||
pub use paginated::{PageMsg, Paginate, PaginateFull};
|
||||
pub use placed::{FixedHeightBar, Floating, GridPlaced, Split};
|
||||
pub use qr_code::Qr;
|
||||
#[cfg(feature = "touch")]
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::ui::util::Pager;
|
||||
|
||||
/// Common message type for pagination components.
|
||||
#[cfg_attr(feature = "debug", derive(ufmt::derive::uDebug))]
|
||||
pub enum PageMsg<T> {
|
||||
@ -20,9 +22,43 @@ pub enum PageMsg<T> {
|
||||
SwipeRight,
|
||||
}
|
||||
|
||||
/// TRANSITIONAL paginate trait that only optionally returns the current page.
|
||||
/// Use PaginateFull for the new trait that returns a Pager.
|
||||
pub trait Paginate {
|
||||
/// How many pages of content are there in total?
|
||||
fn page_count(&self) -> usize;
|
||||
/// Navigate to the given page.
|
||||
fn change_page(&mut self, active_page: usize);
|
||||
}
|
||||
|
||||
/// Paginate trait allowing the user to see the internal pager state.
|
||||
pub trait PaginateFull {
|
||||
/// What is the internal pager state?
|
||||
fn pager(&self) -> Pager;
|
||||
/// Navigate to the given page.
|
||||
fn change_page(&mut self, active_page: u16);
|
||||
}
|
||||
|
||||
impl<T: PaginateFull> Paginate for T {
|
||||
fn change_page(&mut self, active_page: usize) {
|
||||
self.change_page(active_page as u16);
|
||||
}
|
||||
|
||||
fn page_count(&self) -> usize {
|
||||
self.pager().total() as usize
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SinglePage {}
|
||||
|
||||
impl<T: SinglePage> PaginateFull for T {
|
||||
fn pager(&self) -> Pager {
|
||||
Pager::single_page()
|
||||
}
|
||||
|
||||
fn change_page(&mut self, active_page: u16) {
|
||||
if active_page != 0 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ use crate::ui::{
|
||||
shape::Renderer,
|
||||
};
|
||||
|
||||
use super::paginated::SinglePage;
|
||||
|
||||
pub struct GridPlaced<T> {
|
||||
inner: T,
|
||||
grid: Grid,
|
||||
@ -272,6 +274,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> SinglePage for Split<T, U> {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, U> crate::trace::Trace for Split<T, U>
|
||||
where
|
||||
|
@ -12,6 +12,8 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::paginated::SinglePage;
|
||||
|
||||
const NVERSIONS: usize = 10; // range of versions (=capacities) that we support
|
||||
const THRESHOLDS_BINARY: [usize; NVERSIONS] = [14, 26, 42, 62, 84, 106, 122, 152, 180, 213];
|
||||
const THRESHOLDS_ALPHANUM: [usize; NVERSIONS] = [20, 38, 61, 90, 122, 154, 178, 221, 262, 311];
|
||||
@ -123,6 +125,8 @@ impl Component for Qr {
|
||||
}
|
||||
}
|
||||
|
||||
impl SinglePage for Qr {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for Qr {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
constant::screen,
|
||||
event::{SwipeEvent, TouchEvent},
|
||||
geometry::{Axis, Direction, Offset, Point},
|
||||
util::animation_disabled,
|
||||
util::{animation_disabled, Pager},
|
||||
},
|
||||
};
|
||||
|
||||
@ -106,23 +106,21 @@ impl SwipeConfig {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_pagination(mut self, current_page: u16, total_pages: u16) -> Self {
|
||||
let has_prev = current_page > 0;
|
||||
let has_next = current_page < total_pages.saturating_sub(1);
|
||||
pub fn with_pager(mut self, pager: Pager) -> Self {
|
||||
match self.page_axis {
|
||||
Some(Axis::Horizontal) => {
|
||||
if has_prev {
|
||||
if pager.has_prev() {
|
||||
self.right = Some(SwipeSettings::default());
|
||||
}
|
||||
if has_next {
|
||||
if pager.has_next() {
|
||||
self.left = Some(SwipeSettings::default());
|
||||
}
|
||||
}
|
||||
Some(Axis::Vertical) => {
|
||||
if has_prev {
|
||||
if pager.has_prev() {
|
||||
self.down = Some(SwipeSettings::default());
|
||||
}
|
||||
if has_next {
|
||||
if pager.has_next() {
|
||||
self.up = Some(SwipeSettings::default());
|
||||
}
|
||||
}
|
||||
@ -131,15 +129,13 @@ impl SwipeConfig {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn paging_event(&self, dir: Direction, current_page: u16, total_pages: u16) -> u16 {
|
||||
let prev_page = current_page.saturating_sub(1);
|
||||
let next_page = (current_page + 1).min(total_pages.saturating_sub(1));
|
||||
pub fn paging_event(&self, dir: Direction, pager: Pager) -> u16 {
|
||||
match (self.page_axis, dir) {
|
||||
(Some(Axis::Horizontal), Direction::Right) => prev_page,
|
||||
(Some(Axis::Horizontal), Direction::Left) => next_page,
|
||||
(Some(Axis::Vertical), Direction::Down) => prev_page,
|
||||
(Some(Axis::Vertical), Direction::Up) => next_page,
|
||||
_ => current_page,
|
||||
(Some(Axis::Horizontal), Direction::Right) => pager.prev(),
|
||||
(Some(Axis::Horizontal), Direction::Left) => pager.next(),
|
||||
(Some(Axis::Vertical), Direction::Down) => pager.prev(),
|
||||
(Some(Axis::Vertical), Direction::Up) => pager.next(),
|
||||
_ => pager.current(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never, Paginate},
|
||||
component::{Component, Event, EventCtx, Never, PaginateFull},
|
||||
geometry::{Alignment, Offset, Rect},
|
||||
shape::Renderer,
|
||||
util::Pager,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -15,6 +16,7 @@ pub struct FormattedText {
|
||||
vertical: Alignment,
|
||||
char_offset: usize,
|
||||
y_offset: i16,
|
||||
pager: Pager,
|
||||
}
|
||||
|
||||
impl FormattedText {
|
||||
@ -24,6 +26,7 @@ impl FormattedText {
|
||||
vertical: Alignment::Start,
|
||||
char_offset: 0,
|
||||
y_offset: 0,
|
||||
pager: Pager::single_page(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,18 +35,9 @@ impl FormattedText {
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn layout_content(&self, sink: &mut dyn LayoutSink) -> LayoutFit {
|
||||
self.layout_content_with_offset(sink, self.char_offset, self.y_offset)
|
||||
}
|
||||
|
||||
fn layout_content_with_offset(
|
||||
&self,
|
||||
sink: &mut dyn LayoutSink,
|
||||
char_offset: usize,
|
||||
y_offset: i16,
|
||||
) -> LayoutFit {
|
||||
fn layout_content(&self, sink: &mut dyn LayoutSink) -> LayoutFit {
|
||||
self.op_layout
|
||||
.layout_ops(char_offset, Offset::y(y_offset), sink)
|
||||
.layout_ops(self.char_offset, Offset::y(self.y_offset), sink)
|
||||
}
|
||||
|
||||
fn align_vertically(&mut self, content_height: i16) {
|
||||
@ -58,19 +52,79 @@ impl FormattedText {
|
||||
Alignment::End => bounds_height - content_height,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
pub(crate) fn trace_lines_as_list(&self, l: &mut dyn crate::trace::ListTracer) -> LayoutFit {
|
||||
use crate::ui::component::text::layout::trace::TraceSink;
|
||||
let result = self.layout_content(&mut TraceSink(l));
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
// Pagination
|
||||
impl Paginate for FormattedText {
|
||||
fn page_count(&self) -> usize {
|
||||
impl PaginateFull for FormattedText {
|
||||
fn pager(&self) -> Pager {
|
||||
self.pager
|
||||
}
|
||||
|
||||
fn change_page(&mut self, to_page: u16) {
|
||||
let to_page = to_page.min(self.pager.total() - 1);
|
||||
|
||||
// reset current position if needed and calculate how many pages forward we need
|
||||
// to go
|
||||
self.y_offset = 0;
|
||||
let mut pages_remaining = if to_page < self.pager.current() {
|
||||
self.char_offset = 0;
|
||||
to_page
|
||||
} else {
|
||||
to_page - self.pager.current()
|
||||
};
|
||||
|
||||
// move forward until we arrive at the wanted page
|
||||
let mut fit = self.layout_content(&mut TextNoOp);
|
||||
while pages_remaining > 0 {
|
||||
match fit {
|
||||
LayoutFit::Fitting { .. } => {
|
||||
break;
|
||||
}
|
||||
LayoutFit::OutOfBounds {
|
||||
processed_chars, ..
|
||||
} => {
|
||||
pages_remaining -= 1;
|
||||
self.char_offset += processed_chars;
|
||||
fit = self.layout_content(&mut TextNoOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Setting appropriate self.y_offset
|
||||
self.align_vertically(fit.height());
|
||||
// Update the pager
|
||||
self.pager.set_current(to_page);
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for FormattedText {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
if self.op_layout.layout.bounds == bounds {
|
||||
// Skip placement logic (and resetting pager) if the bounds haven't changed.
|
||||
return bounds;
|
||||
}
|
||||
|
||||
self.op_layout.place(bounds);
|
||||
|
||||
// reset current position
|
||||
self.char_offset = 0;
|
||||
self.y_offset = 0;
|
||||
|
||||
let mut page_count = 1; // There's always at least one page.
|
||||
let mut first_fit = None;
|
||||
|
||||
let mut char_offset = 0;
|
||||
|
||||
// Looping through the content and counting pages
|
||||
// until we finally fit.
|
||||
// Looping through the content and counting pages until we finally fit.
|
||||
loop {
|
||||
let fit = self.layout_content_with_offset(&mut TextNoOp, char_offset, 0);
|
||||
let fit = self.layout_content(&mut TextNoOp);
|
||||
first_fit.get_or_insert(fit);
|
||||
match fit {
|
||||
LayoutFit::Fitting { .. } => {
|
||||
break;
|
||||
@ -79,50 +133,18 @@ impl Paginate for FormattedText {
|
||||
processed_chars, ..
|
||||
} => {
|
||||
page_count += 1;
|
||||
char_offset += processed_chars;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
page_count
|
||||
}
|
||||
|
||||
fn change_page(&mut self, to_page: usize) {
|
||||
let mut active_page = 0;
|
||||
|
||||
// Make sure we're starting from the beginning.
|
||||
self.char_offset = 0;
|
||||
self.y_offset = 0;
|
||||
|
||||
// Looping through the content until we arrive at
|
||||
// the wanted page.
|
||||
let mut fit = self.layout_content(&mut TextNoOp);
|
||||
while active_page < to_page {
|
||||
match fit {
|
||||
LayoutFit::Fitting { .. } => {
|
||||
break;
|
||||
}
|
||||
LayoutFit::OutOfBounds {
|
||||
processed_chars, ..
|
||||
} => {
|
||||
active_page += 1;
|
||||
self.char_offset += processed_chars;
|
||||
fit = self.layout_content(&mut TextNoOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Setting appropriate self.y_offset
|
||||
self.align_vertically(fit.height());
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for FormattedText {
|
||||
type Msg = Never;
|
||||
// reset position to start
|
||||
self.char_offset = 0;
|
||||
// align vertically and set pager
|
||||
let first_fit = unwrap!(first_fit);
|
||||
self.align_vertically(first_fit.height());
|
||||
self.pager = Pager::new(page_count);
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.op_layout.place(bounds);
|
||||
let height = self.layout_content(&mut TextNoOp).height();
|
||||
self.align_vertically(height);
|
||||
bounds
|
||||
}
|
||||
|
||||
@ -140,12 +162,11 @@ impl Component for FormattedText {
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for FormattedText {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
use crate::ui::component::text::layout::trace::TraceSink;
|
||||
use core::cell::Cell;
|
||||
let fit: Cell<Option<LayoutFit>> = Cell::new(None);
|
||||
t.component("FormattedText");
|
||||
t.in_list("text", &|l| {
|
||||
let result = self.layout_content(&mut TraceSink(l));
|
||||
let result = self.trace_lines_as_list(l);
|
||||
fit.set(Some(result));
|
||||
});
|
||||
t.bool("fits", matches!(fit.get(), Some(LayoutFit::Fitting { .. })));
|
||||
|
@ -3,10 +3,11 @@ use heapless::Vec;
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Component, Event, EventCtx, Never, Paginate},
|
||||
component::{paginated::SinglePage, Component, Event, EventCtx, Never, PaginateFull},
|
||||
display::{font::Font, toif::Icon, Color},
|
||||
geometry::{Alignment, Dimensions, Insets, LinearPlacement, Offset, Point, Rect},
|
||||
shape::{self, Renderer},
|
||||
util::Pager,
|
||||
},
|
||||
};
|
||||
|
||||
@ -51,6 +52,7 @@ pub struct Paragraphs<T> {
|
||||
offset: PageOffset,
|
||||
visible: Vec<TextLayoutProxy, MAX_LINES>,
|
||||
source: T,
|
||||
pager: Pager,
|
||||
}
|
||||
|
||||
impl<'a, T> Paragraphs<T>
|
||||
@ -66,6 +68,7 @@ where
|
||||
offset: PageOffset::default(),
|
||||
visible: Vec::new(),
|
||||
source,
|
||||
pager: Pager::single_page(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,12 +103,6 @@ where
|
||||
result.unwrap_or(self.area)
|
||||
}
|
||||
|
||||
pub fn current_page(&self) -> usize {
|
||||
self.break_pages()
|
||||
.position(|offset| offset == self.offset)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Update bounding boxes of paragraphs on the current page. First determine
|
||||
/// the number of visible paragraphs and their sizes. These are then
|
||||
/// arranged according to the layout.
|
||||
@ -127,28 +124,42 @@ where
|
||||
let full_height = area.height();
|
||||
|
||||
while offset.par < source.size() {
|
||||
let (next_offset, remaining_area, layout) = offset.advance(area, source, full_height);
|
||||
if let Some(layout) = layout {
|
||||
let advance = offset.advance(area, source, full_height);
|
||||
if let Some(layout) = advance.layout {
|
||||
unwrap!(visible.push(layout));
|
||||
}
|
||||
if let Some(remaining_area) = remaining_area {
|
||||
if let Some(remaining_area) = advance.remaining_area {
|
||||
#[cfg(feature = "ui_debug")]
|
||||
assert_eq!(next_offset.par, offset.par + 1);
|
||||
assert_eq!(advance.offset.par, offset.par + 1);
|
||||
area = remaining_area;
|
||||
offset = next_offset;
|
||||
offset = advance.offset;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn break_pages(&self) -> PageBreakIterator<T> {
|
||||
fn break_pages_from(&self, offset: Option<PageOffset>) -> PageBreakIterator<T> {
|
||||
PageBreakIterator {
|
||||
paragraphs: self,
|
||||
current: None,
|
||||
current: offset,
|
||||
}
|
||||
}
|
||||
|
||||
/// Break pages from the start of the document.
|
||||
///
|
||||
/// The first pagebreak is at the start of the first screen.
|
||||
fn break_pages_from_start(&self) -> PageBreakIterator<T> {
|
||||
self.break_pages_from(None)
|
||||
}
|
||||
|
||||
/// Break pages, continuing from the current page.
|
||||
///
|
||||
/// The first pagebreak is at the start of the next screen.
|
||||
fn break_pages_from_next(&self) -> PageBreakIterator<T> {
|
||||
self.break_pages_from(Some(self.offset))
|
||||
}
|
||||
|
||||
/// Iterate over visible layouts (bounding box, style) together
|
||||
/// with corresponding string content. Should not get monomorphized.
|
||||
fn foreach_visible<'b>(
|
||||
@ -184,7 +195,13 @@ where
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let recalc = self.area != bounds;
|
||||
self.area = bounds;
|
||||
if recalc {
|
||||
self.offset = PageOffset::default();
|
||||
let total_pages = self.break_pages_from_start().count().max(1);
|
||||
self.pager = Pager::new(total_pages as u16);
|
||||
}
|
||||
self.change_offset(self.offset);
|
||||
self.area
|
||||
}
|
||||
@ -205,22 +222,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Paginate for Paragraphs<T>
|
||||
impl<'a, T> PaginateFull for Paragraphs<T>
|
||||
where
|
||||
T: ParagraphSource<'a>,
|
||||
{
|
||||
fn page_count(&self) -> usize {
|
||||
// There's always at least one page.
|
||||
self.break_pages().count().max(1)
|
||||
fn pager(&self) -> Pager {
|
||||
self.pager
|
||||
}
|
||||
|
||||
fn change_page(&mut self, to_page: usize) {
|
||||
if let Some(offset) = self.break_pages().nth(to_page) {
|
||||
self.change_offset(offset)
|
||||
fn change_page(&mut self, to_page: u16) {
|
||||
use core::cmp::Ordering;
|
||||
|
||||
let offset = match to_page.cmp(&self.pager.current()) {
|
||||
Ordering::Equal => return,
|
||||
Ordering::Greater => self
|
||||
.break_pages_from_next()
|
||||
.nth((to_page - self.pager.current() - 1) as usize),
|
||||
Ordering::Less => self.break_pages_from_start().nth(to_page as usize),
|
||||
};
|
||||
if let Some(offset) = offset {
|
||||
self.change_offset(offset);
|
||||
self.pager.set_current(to_page);
|
||||
} else {
|
||||
// Should not happen, set index to first paragraph and render empty page.
|
||||
self.offset = PageOffset::default();
|
||||
self.visible.clear()
|
||||
self.visible.clear();
|
||||
self.pager.set_current(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -363,7 +390,7 @@ impl Dimensions for TextLayoutProxy {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct PageOffset {
|
||||
/// Index of paragraph.
|
||||
par: usize,
|
||||
@ -372,6 +399,13 @@ struct PageOffset {
|
||||
chr: usize,
|
||||
}
|
||||
|
||||
/// Helper struct for `PageOffset::advance`
|
||||
struct PageOffsetAdvance {
|
||||
offset: PageOffset,
|
||||
remaining_area: Option<Rect>,
|
||||
layout: Option<TextLayoutProxy>,
|
||||
}
|
||||
|
||||
impl PageOffset {
|
||||
/// Given a `PageOffset` and a `Rect` area, returns:
|
||||
///
|
||||
@ -388,29 +422,34 @@ impl PageOffset {
|
||||
area: Rect,
|
||||
source: &dyn ParagraphSource<'_>,
|
||||
full_height: i16,
|
||||
) -> (PageOffset, Option<Rect>, Option<TextLayoutProxy>) {
|
||||
) -> PageOffsetAdvance {
|
||||
let paragraph = source.at(self.par, self.chr);
|
||||
|
||||
// Skip empty paragraphs.
|
||||
if paragraph.content().is_empty() {
|
||||
self.par += 1;
|
||||
self.chr = 0;
|
||||
return (self, Some(area), None);
|
||||
return PageOffsetAdvance {
|
||||
offset: self,
|
||||
remaining_area: Some(area),
|
||||
layout: None,
|
||||
};
|
||||
}
|
||||
|
||||
// Handle the `no_break` flag used to keep key-value pair on the same page.
|
||||
if paragraph.no_break && self.chr == 0 {
|
||||
if let Some(next_paragraph) =
|
||||
(self.par + 1 < source.size()).then(|| source.at(self.par + 1, 0))
|
||||
// Handle the `no_break` flag used to keep key-value pair on the same page:
|
||||
// * no_break is set
|
||||
// * we're at the start of a paragraph ("key")
|
||||
// * there is a next paragraph ("value")
|
||||
// then check if the pair fits on the next page.
|
||||
if paragraph.no_break && self.chr == 0 && self.par + 1 < source.size() {
|
||||
let next_paragraph = source.at(self.par + 1, 0);
|
||||
if Self::should_place_pair_on_next_page(¶graph, &next_paragraph, area, full_height)
|
||||
{
|
||||
if Self::should_place_pair_on_next_page(
|
||||
¶graph,
|
||||
&next_paragraph,
|
||||
area,
|
||||
full_height,
|
||||
) {
|
||||
return (self, None, None);
|
||||
}
|
||||
return PageOffsetAdvance {
|
||||
offset: self,
|
||||
remaining_area: None,
|
||||
layout: None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,11 +480,11 @@ impl PageOffset {
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
self,
|
||||
Some(remaining_area).filter(|_| !page_full),
|
||||
Some(layout).filter(|_| fit.height() > 0),
|
||||
)
|
||||
PageOffsetAdvance {
|
||||
offset: self,
|
||||
remaining_area: (!page_full).then_some(remaining_area),
|
||||
layout: (fit.height() > 0).then_some(layout),
|
||||
}
|
||||
}
|
||||
|
||||
fn should_place_pair_on_next_page(
|
||||
@ -505,18 +544,17 @@ impl<'a, T: ParagraphSource<'a>> PageBreakIterator<'_, T> {
|
||||
let full_height = area.height();
|
||||
|
||||
while offset.par < paragraphs.size() {
|
||||
let (next_offset, remaining_area, _layout) =
|
||||
offset.advance(area, paragraphs, full_height);
|
||||
if next_offset.par >= paragraphs.size() {
|
||||
let advance = offset.advance(area, paragraphs, full_height);
|
||||
if advance.offset.par >= paragraphs.size() {
|
||||
// Last page.
|
||||
return None;
|
||||
} else if let Some(remaining_area) = remaining_area {
|
||||
} else if let Some(remaining_area) = advance.remaining_area {
|
||||
#[cfg(feature = "ui_debug")]
|
||||
assert_eq!(next_offset.par, offset.par + 1);
|
||||
assert_eq!(advance.offset.par, offset.par + 1);
|
||||
area = remaining_area;
|
||||
offset = next_offset;
|
||||
offset = advance.offset;
|
||||
} else {
|
||||
return Some(next_offset);
|
||||
return Some(advance.offset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -693,16 +731,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Paginate for Checklist<T>
|
||||
where
|
||||
T: ParagraphSource<'a>,
|
||||
{
|
||||
fn page_count(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn change_page(&mut self, _to_page: usize) {}
|
||||
}
|
||||
impl<T> SinglePage for Checklist<T> {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<'a, T: ParagraphSource<'a>> crate::trace::Trace for Checklist<T> {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::ui::{
|
||||
component::{base::AttachType, swipe_detect::SwipeConfig},
|
||||
geometry::Direction,
|
||||
util::Pager,
|
||||
};
|
||||
|
||||
pub use crate::ui::component::FlowMsg;
|
||||
@ -8,7 +9,7 @@ pub use crate::ui::component::FlowMsg;
|
||||
pub trait Swipable {
|
||||
fn get_swipe_config(&self) -> SwipeConfig;
|
||||
|
||||
fn get_internal_page_count(&self) -> usize;
|
||||
fn get_pager(&self) -> Pager;
|
||||
}
|
||||
|
||||
/// Composable event handler result.
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Paginate},
|
||||
component::{Component, Event, EventCtx, PaginateFull},
|
||||
event::SwipeEvent,
|
||||
geometry::{Axis, Direction, Rect},
|
||||
shape::Renderer,
|
||||
util::Pager,
|
||||
};
|
||||
|
||||
/// Allows any implementor of `Paginate` to be part of `Swipable` UI flow.
|
||||
@ -11,19 +12,17 @@ pub struct SwipePage<T> {
|
||||
inner: T,
|
||||
bounds: Rect,
|
||||
axis: Axis,
|
||||
pages: usize,
|
||||
current: usize,
|
||||
limit: Option<usize>,
|
||||
pager: Pager,
|
||||
limit: Option<u16>,
|
||||
}
|
||||
|
||||
impl<T: Component + Paginate> SwipePage<T> {
|
||||
impl<T: Component + PaginateFull> SwipePage<T> {
|
||||
pub fn vertical(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
bounds: Rect::zero(),
|
||||
axis: Axis::Vertical,
|
||||
pages: 1,
|
||||
current: 0,
|
||||
pager: Pager::single_page(),
|
||||
limit: None,
|
||||
}
|
||||
}
|
||||
@ -33,8 +32,7 @@ impl<T: Component + Paginate> SwipePage<T> {
|
||||
inner,
|
||||
bounds: Rect::zero(),
|
||||
axis: Axis::Horizontal,
|
||||
pages: 1,
|
||||
current: 0,
|
||||
pager: Pager::single_page(),
|
||||
limit: None,
|
||||
}
|
||||
}
|
||||
@ -43,55 +41,47 @@ impl<T: Component + Paginate> SwipePage<T> {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn with_limit(mut self, limit: Option<usize>) -> Self {
|
||||
pub fn with_limit(mut self, limit: Option<u16>) -> Self {
|
||||
self.limit = limit;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn page_count(&self) -> usize {
|
||||
self.pages
|
||||
}
|
||||
|
||||
pub fn current_page(&self) -> usize {
|
||||
self.current
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component + Paginate> Component for SwipePage<T> {
|
||||
impl<T: Component + PaginateFull> Component for SwipePage<T> {
|
||||
type Msg = T::Msg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.bounds = self.inner.place(bounds);
|
||||
self.pages = self.inner.page_count();
|
||||
self.pager = self.inner.pager();
|
||||
if let Some(limit) = self.limit {
|
||||
self.pages = self.pages.min(limit);
|
||||
self.pager = self.pager.with_limit(limit);
|
||||
}
|
||||
self.bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
ctx.set_page_count(self.pages);
|
||||
ctx.set_page_count(self.pager.total() as usize);
|
||||
|
||||
if let Event::Swipe(SwipeEvent::End(direction)) = event {
|
||||
match (self.axis, direction) {
|
||||
(Axis::Vertical, Direction::Up) => {
|
||||
self.current = (self.current + 1).min(self.pages - 1);
|
||||
self.inner.change_page(self.current);
|
||||
self.pager.goto_next();
|
||||
self.inner.change_page(self.pager.current());
|
||||
ctx.request_paint();
|
||||
}
|
||||
(Axis::Vertical, Direction::Down) => {
|
||||
self.current = self.current.saturating_sub(1);
|
||||
self.inner.change_page(self.current);
|
||||
self.pager.goto_prev();
|
||||
self.inner.change_page(self.pager.current());
|
||||
ctx.request_paint();
|
||||
}
|
||||
(Axis::Horizontal, Direction::Left) => {
|
||||
self.current = (self.current + 1).min(self.pages - 1);
|
||||
self.inner.change_page(self.current);
|
||||
self.pager.goto_next();
|
||||
self.inner.change_page(self.pager.current());
|
||||
ctx.request_paint();
|
||||
}
|
||||
(Axis::Horizontal, Direction::Right) => {
|
||||
self.current = self.current.saturating_sub(1);
|
||||
self.inner.change_page(self.current);
|
||||
self.pager.goto_prev();
|
||||
self.inner.change_page(self.pager.current());
|
||||
ctx.request_paint();
|
||||
}
|
||||
_ => {}
|
||||
@ -106,6 +96,17 @@ impl<T: Component + Paginate> Component for SwipePage<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PaginateFull> PaginateFull for SwipePage<T> {
|
||||
fn pager(&self) -> Pager {
|
||||
self.pager
|
||||
}
|
||||
|
||||
fn change_page(&mut self, to_page: u16) {
|
||||
self.pager.set_current(to_page);
|
||||
self.inner.change_page(self.pager.current());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for SwipePage<T>
|
||||
where
|
||||
|
@ -103,10 +103,6 @@ pub struct SwipeFlow {
|
||||
swipe: SwipeDetect,
|
||||
/// Swipe allowed
|
||||
allow_swipe: bool,
|
||||
/// Current page index
|
||||
internal_page_idx: u16,
|
||||
/// Internal pages count
|
||||
internal_pages: u16,
|
||||
/// If triggering swipe by event, make this decision instead of default
|
||||
/// after the swipe.
|
||||
pending_decision: Option<Decision>,
|
||||
@ -123,8 +119,6 @@ impl SwipeFlow {
|
||||
swipe: SwipeDetect::new(),
|
||||
store: Vec::new(),
|
||||
allow_swipe: true,
|
||||
internal_page_idx: 0,
|
||||
internal_pages: 1,
|
||||
pending_decision: None,
|
||||
lifecycle_state: LayoutState::Initial,
|
||||
returned_value: None,
|
||||
@ -154,19 +148,6 @@ impl SwipeFlow {
|
||||
&mut self.store[self.state.index()]
|
||||
}
|
||||
|
||||
fn update_page_count(&mut self, attach_type: AttachType) {
|
||||
// update page count
|
||||
self.internal_pages = self.current_page_mut().get_internal_page_count() as u16;
|
||||
// reset internal state:
|
||||
self.internal_page_idx = if let Swipe(Direction::Down) = attach_type {
|
||||
// if coming from below, set to the last page
|
||||
self.internal_pages.saturating_sub(1)
|
||||
} else {
|
||||
// else reset to the first page
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
/// Transition to a different state.
|
||||
///
|
||||
/// This is the only way to change the current flow state.
|
||||
@ -183,7 +164,6 @@ impl SwipeFlow {
|
||||
self.current_page_mut()
|
||||
.event(ctx, Event::Attach(attach_type));
|
||||
|
||||
self.update_page_count(attach_type);
|
||||
ctx.request_paint();
|
||||
}
|
||||
|
||||
@ -209,27 +189,20 @@ impl SwipeFlow {
|
||||
let mut decision = Decision::Nothing;
|
||||
let mut return_transition: AttachType = AttachType::Initial;
|
||||
|
||||
if let Event::Attach(attach_type) = event {
|
||||
self.update_page_count(attach_type);
|
||||
}
|
||||
|
||||
let mut attach = false;
|
||||
|
||||
let event = if self.allow_swipe {
|
||||
let page = self.current_page();
|
||||
let config = page
|
||||
.get_swipe_config()
|
||||
.with_pagination(self.internal_page_idx, self.internal_pages);
|
||||
let pager = page.get_pager();
|
||||
let config = page.get_swipe_config().with_pager(pager);
|
||||
|
||||
match self.swipe.event(ctx, event, config) {
|
||||
Some(SwipeEvent::End(dir)) => {
|
||||
return_transition = AttachType::Swipe(dir);
|
||||
|
||||
let new_internal_page_idx =
|
||||
config.paging_event(dir, self.internal_page_idx, self.internal_pages);
|
||||
if new_internal_page_idx != self.internal_page_idx {
|
||||
let new_internal_page_idx = config.paging_event(dir, pager);
|
||||
if new_internal_page_idx != pager.current() {
|
||||
// internal paging event
|
||||
self.internal_page_idx = new_internal_page_idx;
|
||||
decision = Decision::Nothing;
|
||||
attach = true;
|
||||
} else if let Some(override_decision) = self.pending_decision.take() {
|
||||
|
@ -121,8 +121,8 @@ where
|
||||
fn update(&mut self, ctx: &mut EventCtx, get_new_page: bool) {
|
||||
if get_new_page {
|
||||
self.change_current_page(ctx);
|
||||
self.current_page.place(self.content_area);
|
||||
}
|
||||
self.current_page.place(self.content_area);
|
||||
self.set_buttons(ctx);
|
||||
self.scrollbar.request_complete_repaint(ctx);
|
||||
self.clear_and_repaint(ctx);
|
||||
|
@ -209,14 +209,10 @@ impl Paginate for Page {
|
||||
}
|
||||
|
||||
// DEBUG-ONLY SECTION BELOW
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use crate::ui::component::text::layout::LayoutFit;
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for Page {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
use crate::ui::component::text::layout::trace::TraceSink;
|
||||
use crate::ui::component::text::layout::LayoutFit;
|
||||
use core::cell::Cell;
|
||||
let fit: Cell<Option<LayoutFit>> = Cell::new(None);
|
||||
t.component("Page");
|
||||
@ -227,7 +223,7 @@ impl crate::trace::Trace for Page {
|
||||
t.int("active_page", self.current_page as i64);
|
||||
t.int("page_count", self.page_count as i64);
|
||||
t.in_list("text", &|l| {
|
||||
let result = self.formatted.layout_content(&mut TraceSink(l));
|
||||
let result = self.formatted.trace_lines_as_list(l);
|
||||
fit.set(Some(result));
|
||||
});
|
||||
}
|
||||
|
@ -8,12 +8,13 @@ use crate::{
|
||||
component::{
|
||||
swipe_detect::{SwipeConfig, SwipeSettings},
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt},
|
||||
Component, Event, EventCtx, Paginate,
|
||||
Component, Event, EventCtx, PaginateFull,
|
||||
},
|
||||
event::SwipeEvent,
|
||||
flow::Swipable,
|
||||
geometry::{Direction, Rect},
|
||||
shape::Renderer,
|
||||
util::Pager,
|
||||
},
|
||||
};
|
||||
|
||||
@ -26,7 +27,7 @@ pub struct AddressDetails {
|
||||
xpub_view: Frame<Paragraphs<Paragraph<'static>>>,
|
||||
xpubs: Vec<(TString<'static>, TString<'static>), MAX_XPUBS>,
|
||||
xpub_page_count: Vec<u8, MAX_XPUBS>,
|
||||
current_page: usize,
|
||||
current_page: u16,
|
||||
}
|
||||
|
||||
impl AddressDetails {
|
||||
@ -84,7 +85,7 @@ impl AddressDetails {
|
||||
.map_err(|_| Error::OutOfRange)
|
||||
}
|
||||
|
||||
fn switch_xpub(&mut self, i: usize, page: usize) -> usize {
|
||||
fn switch_xpub(&mut self, i: usize, page: u16) -> usize {
|
||||
// Context is needed for updating child so that it can request repaint. In this
|
||||
// case the parent component that handles paging always requests complete
|
||||
// repaint after page change so we can use a dummy context here.
|
||||
@ -92,19 +93,17 @@ impl AddressDetails {
|
||||
self.xpub_view.update_title(&mut dummy_ctx, self.xpubs[i].0);
|
||||
self.xpub_view.update_content(&mut dummy_ctx, |_ctx, p| {
|
||||
p.inner_mut().update(self.xpubs[i].1);
|
||||
let npages = p.page_count();
|
||||
let npages = p.pager().total() as usize;
|
||||
p.change_page(page);
|
||||
npages
|
||||
})
|
||||
}
|
||||
|
||||
fn lookup(&self, scrollbar_page: usize) -> (usize, usize) {
|
||||
fn lookup(&self, scrollbar_page: u16) -> (usize, u16) {
|
||||
let mut xpub_index = 0;
|
||||
let mut xpub_page = scrollbar_page;
|
||||
for page_count in self.xpub_page_count.iter().map(|pc| {
|
||||
let upc: usize = (*pc).into();
|
||||
upc
|
||||
}) {
|
||||
for page_count in self.xpub_page_count.iter() {
|
||||
let page_count = *page_count as u16;
|
||||
if page_count <= xpub_page {
|
||||
xpub_page -= page_count;
|
||||
xpub_index += 1;
|
||||
@ -116,12 +115,13 @@ impl AddressDetails {
|
||||
}
|
||||
}
|
||||
|
||||
impl Paginate for AddressDetails {
|
||||
fn page_count(&self) -> usize {
|
||||
self.get_internal_page_count()
|
||||
impl PaginateFull for AddressDetails {
|
||||
fn pager(&self) -> Pager {
|
||||
let total_xpub_pages: u8 = self.xpub_page_count.iter().copied().sum();
|
||||
Pager::new(total_xpub_pages as u16 + 1).with_current(self.current_page)
|
||||
}
|
||||
|
||||
fn change_page(&mut self, to_page: usize) {
|
||||
fn change_page(&mut self, to_page: u16) {
|
||||
self.current_page = to_page;
|
||||
if to_page > 0 {
|
||||
let i = to_page - 1;
|
||||
@ -148,17 +148,14 @@ impl Component for AddressDetails {
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
ctx.set_page_count(self.page_count());
|
||||
ctx.set_page_count(self.pager().total() as usize);
|
||||
match event {
|
||||
Event::Swipe(SwipeEvent::End(Direction::Right)) => {
|
||||
let to_page = self.current_page.saturating_sub(1);
|
||||
let to_page = self.pager().prev();
|
||||
self.change_page(to_page);
|
||||
}
|
||||
Event::Swipe(SwipeEvent::End(Direction::Left)) => {
|
||||
let to_page = self
|
||||
.current_page
|
||||
.saturating_add(1)
|
||||
.min(self.page_count() - 1);
|
||||
let to_page = self.pager().next();
|
||||
self.change_page(to_page);
|
||||
}
|
||||
_ => {}
|
||||
@ -190,9 +187,8 @@ impl Swipable for AddressDetails {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_internal_page_count(&self) -> usize {
|
||||
let total_xpub_pages: u8 = self.xpub_page_count.iter().copied().sum();
|
||||
1usize.saturating_add(total_xpub_pages.into())
|
||||
fn get_pager(&self) -> Pager {
|
||||
self.pager()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ use crate::{
|
||||
ui::{
|
||||
component::{
|
||||
image::Image,
|
||||
paginated::SinglePage,
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs},
|
||||
Component, Event, EventCtx,
|
||||
},
|
||||
@ -77,6 +78,8 @@ impl<F: Fn() -> TString<'static>> Component for FidoCredential<F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn() -> TString<'static>> SinglePage for FidoCredential<F> {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<F: Fn() -> TString<'static>> crate::trace::Trace for FidoCredential<F> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -8,9 +8,8 @@ use crate::{
|
||||
},
|
||||
display,
|
||||
geometry::{Alignment, Direction, Grid, Insets, Offset, Rect},
|
||||
shape,
|
||||
shape::Renderer,
|
||||
util::long_line_content_with_ellipsis,
|
||||
shape::{self, Renderer},
|
||||
util::{long_line_content_with_ellipsis, Pager},
|
||||
},
|
||||
};
|
||||
|
||||
@ -88,7 +87,6 @@ pub struct PassphraseKeyboard {
|
||||
active_layout: KeyboardLayout,
|
||||
fade: Cell<bool>,
|
||||
swipe_config: SwipeConfig, // FIXME: how about page_swipe
|
||||
internal_page_cnt: usize,
|
||||
}
|
||||
|
||||
const PAGE_COUNT: usize = 4;
|
||||
@ -159,7 +157,6 @@ impl PassphraseKeyboard {
|
||||
active_layout,
|
||||
fade: Cell::new(false),
|
||||
swipe_config: SwipeConfig::new(),
|
||||
internal_page_cnt: 1,
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,8 +478,8 @@ impl crate::ui::flow::Swipable for PassphraseKeyboard {
|
||||
self.swipe_config
|
||||
}
|
||||
|
||||
fn get_internal_page_count(&self) -> usize {
|
||||
self.internal_page_cnt
|
||||
fn get_pager(&self) -> Pager {
|
||||
Pager::single_page()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx},
|
||||
component::{paginated::SinglePage, Component, Event, EventCtx},
|
||||
geometry::{Alignment, Grid, GridCellSpan, Rect},
|
||||
shape::Renderer,
|
||||
};
|
||||
@ -72,6 +72,8 @@ impl Component for SelectWordCount {
|
||||
}
|
||||
}
|
||||
|
||||
impl SinglePage for SelectWordCount {}
|
||||
|
||||
pub struct ValueKeypad {
|
||||
button: [Button; Self::NUMBERS.len()],
|
||||
keypad_area: Rect,
|
||||
|
@ -79,7 +79,7 @@ pub use scroll::ScrollBar;
|
||||
#[cfg(feature = "translations")]
|
||||
pub use share_words::ShareWords;
|
||||
pub use status_screen::StatusScreen;
|
||||
pub use swipe_content::{InternallySwipable, InternallySwipableContent, SwipeContent};
|
||||
pub use swipe_content::{InternallySwipableContent, SwipeContent};
|
||||
#[cfg(feature = "translations")]
|
||||
pub use swipe_up_screen::{SwipeUpScreen, SwipeUpScreenMsg};
|
||||
#[cfg(feature = "translations")]
|
||||
|
@ -3,6 +3,7 @@ use crate::{
|
||||
strutil::{self, TString},
|
||||
ui::{
|
||||
component::{
|
||||
paginated::SinglePage,
|
||||
text::paragraphs::{Paragraph, Paragraphs},
|
||||
Component, Event, EventCtx, Pad,
|
||||
},
|
||||
@ -79,6 +80,8 @@ impl Component for NumberInputDialog {
|
||||
}
|
||||
}
|
||||
|
||||
impl SinglePage for NumberInputDialog {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for NumberInputDialog {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
strutil::{ShortString, TString},
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{Component, Event, EventCtx},
|
||||
component::{paginated::SinglePage, Component, Event, EventCtx},
|
||||
constant::screen,
|
||||
event::TouchEvent,
|
||||
geometry::{Alignment, Alignment2D, Insets, Offset, Point, Rect},
|
||||
@ -101,6 +101,8 @@ impl Component for NumberInputSliderDialog {
|
||||
}
|
||||
}
|
||||
|
||||
impl SinglePage for NumberInputSliderDialog {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for NumberInputSliderDialog {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx},
|
||||
component::{paginated::SinglePage, Component, Event, EventCtx},
|
||||
geometry::Rect,
|
||||
shape::Renderer,
|
||||
};
|
||||
@ -115,6 +115,8 @@ impl Component for PromptScreen {
|
||||
}
|
||||
}
|
||||
|
||||
impl SinglePage for PromptScreen {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for PromptScreen {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -2,19 +2,20 @@ use crate::{
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{base::AttachType, text::TextStyle, Component, Event, EventCtx, Never},
|
||||
component::{
|
||||
base::AttachType, paginated::PaginateFull, text::TextStyle, Component, Event, EventCtx,
|
||||
Never,
|
||||
},
|
||||
event::SwipeEvent,
|
||||
geometry::{Alignment, Alignment2D, Direction, Insets, Offset, Rect},
|
||||
shape::{self, Renderer},
|
||||
util::Pager,
|
||||
},
|
||||
};
|
||||
|
||||
use heapless::Vec;
|
||||
|
||||
use super::{
|
||||
super::component::{swipe_content::SwipeAttachAnimation, InternallySwipable},
|
||||
theme,
|
||||
};
|
||||
use super::{super::component::swipe_content::SwipeAttachAnimation, theme};
|
||||
|
||||
const MAX_WORDS: usize = 33; // super-shamir has 33 words, all other have less
|
||||
|
||||
@ -26,8 +27,8 @@ type IndexVec = Vec<u8, MAX_WORDS>;
|
||||
pub struct ShareWords<'a> {
|
||||
share_words: Vec<TString<'a>, MAX_WORDS>,
|
||||
subtitle: TString<'static>,
|
||||
page_index: i16,
|
||||
next_index: i16,
|
||||
page_index: u16,
|
||||
next_index: u16,
|
||||
/// Area reserved for a shown word from mnemonic/share
|
||||
area_word: Rect,
|
||||
progress: i16,
|
||||
@ -55,11 +56,11 @@ impl<'a> ShareWords<'a> {
|
||||
}
|
||||
|
||||
fn is_first_page(&self) -> bool {
|
||||
self.page_index == 0
|
||||
self.pager().is_first()
|
||||
}
|
||||
|
||||
fn is_final_page(&self) -> bool {
|
||||
self.page_index == self.share_words.len() as i16 - 1
|
||||
self.pager().is_last()
|
||||
}
|
||||
|
||||
fn find_repeated(share_words: &[TString]) -> IndexVec {
|
||||
@ -85,9 +86,9 @@ impl<'a> ShareWords<'a> {
|
||||
(self.subtitle, &theme::TEXT_SUB_GREY)
|
||||
}
|
||||
|
||||
fn render_word<'s>(&self, word_index: i16, target: &mut impl Renderer<'s>, area: Rect) {
|
||||
fn render_word<'s>(&self, word_index: u16, target: &mut impl Renderer<'s>, area: Rect) {
|
||||
// the share word
|
||||
if word_index >= self.share_words.len() as _ || word_index < 0 {
|
||||
if word_index >= self.share_words.len() as _ {
|
||||
return;
|
||||
}
|
||||
let word = self.share_words[word_index as usize];
|
||||
@ -157,13 +158,13 @@ impl<'a> Component for ShareWords<'a> {
|
||||
Event::Swipe(SwipeEvent::End(dir)) => match dir {
|
||||
Direction::Up if !self.is_final_page() => {
|
||||
self.progress = 0;
|
||||
self.page_index = (self.page_index + 1).min(self.share_words.len() as i16 - 1);
|
||||
self.page_index = self.pager().next();
|
||||
self.wait_for_attach = true;
|
||||
ctx.request_paint();
|
||||
}
|
||||
Direction::Down if !self.is_first_page() => {
|
||||
self.progress = 0;
|
||||
self.page_index = self.page_index.saturating_sub(1);
|
||||
self.page_index = self.pager().prev();
|
||||
self.wait_for_attach = true;
|
||||
ctx.request_paint();
|
||||
}
|
||||
@ -172,11 +173,11 @@ impl<'a> Component for ShareWords<'a> {
|
||||
Event::Swipe(SwipeEvent::Move(dir, progress)) => {
|
||||
match dir {
|
||||
Direction::Up => {
|
||||
self.next_index = self.page_index + 1;
|
||||
self.next_index = self.pager().next();
|
||||
self.progress = progress;
|
||||
}
|
||||
Direction::Down => {
|
||||
self.next_index = self.page_index - 1;
|
||||
self.next_index = self.pager().prev();
|
||||
self.progress = progress;
|
||||
}
|
||||
_ => {}
|
||||
@ -241,13 +242,13 @@ impl<'a> Component for ShareWords<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl InternallySwipable for ShareWords<'_> {
|
||||
fn current_page(&self) -> usize {
|
||||
self.page_index as usize
|
||||
impl PaginateFull for ShareWords<'_> {
|
||||
fn pager(&self) -> Pager {
|
||||
Pager::new(self.share_words.len() as u16).with_current(self.page_index)
|
||||
}
|
||||
|
||||
fn num_pages(&self) -> usize {
|
||||
self.share_words.len()
|
||||
fn change_page(&mut self, active_page: u16) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,12 @@ use crate::{
|
||||
strutil::TString,
|
||||
time::{Duration, Stopwatch},
|
||||
ui::{
|
||||
component::{Component, Event, EventCtx, Label, Timeout},
|
||||
component::{paginated::SinglePage, Component, Event, EventCtx, Label, Timeout},
|
||||
constant::screen,
|
||||
display::{Color, Icon},
|
||||
geometry::{Alignment, Alignment2D, Insets, Point, Rect},
|
||||
lerp::Lerp,
|
||||
shape,
|
||||
shape::Renderer,
|
||||
shape::{self, Renderer},
|
||||
util::animation_disabled,
|
||||
},
|
||||
};
|
||||
@ -276,6 +275,8 @@ impl Component for StatusScreen {
|
||||
}
|
||||
}
|
||||
|
||||
impl SinglePage for StatusScreen {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for StatusScreen {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
ui::{
|
||||
component::{
|
||||
base::{AttachType, EventPropagation},
|
||||
Component, Event, EventCtx,
|
||||
Component, Event, EventCtx, PaginateFull,
|
||||
},
|
||||
constant::screen,
|
||||
display::Color,
|
||||
@ -11,7 +11,7 @@ use crate::{
|
||||
geometry::{Direction, Offset, Rect},
|
||||
lerp::Lerp,
|
||||
shape::{self, Renderer},
|
||||
util::animation_disabled,
|
||||
util::{animation_disabled, Pager},
|
||||
},
|
||||
};
|
||||
|
||||
@ -284,6 +284,16 @@ impl<T: Component> Component for SwipeContent<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PaginateFull> PaginateFull for SwipeContent<T> {
|
||||
fn pager(&self) -> Pager {
|
||||
self.inner.pager()
|
||||
}
|
||||
|
||||
fn change_page(&mut self, to_page: u16) {
|
||||
self.inner.change_page(to_page);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for SwipeContent<T>
|
||||
where
|
||||
@ -295,15 +305,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InternallySwipable {
|
||||
fn current_page(&self) -> usize;
|
||||
|
||||
fn num_pages(&self) -> usize;
|
||||
}
|
||||
|
||||
pub struct InternallySwipableContent<T>
|
||||
where
|
||||
T: Component + InternallySwipable,
|
||||
T: Component + PaginateFull,
|
||||
{
|
||||
content: SwipeContent<T>,
|
||||
animate: bool,
|
||||
@ -311,7 +315,7 @@ where
|
||||
|
||||
impl<T> InternallySwipableContent<T>
|
||||
where
|
||||
T: Component + InternallySwipable,
|
||||
T: Component + PaginateFull,
|
||||
{
|
||||
pub fn new(content: T) -> Self {
|
||||
Self {
|
||||
@ -325,10 +329,7 @@ where
|
||||
}
|
||||
|
||||
fn should_animate_attach(&self, attach_type: AttachType) -> bool {
|
||||
let is_first_page = self.content.inner.current_page() == 0;
|
||||
let is_last_page =
|
||||
self.content.inner.current_page() == (self.content.inner.num_pages() - 1);
|
||||
|
||||
let pager = self.content.inner.pager();
|
||||
let is_swipe_up = matches!(attach_type, AttachType::Swipe(Direction::Up));
|
||||
let is_swipe_down = matches!(attach_type, AttachType::Swipe(Direction::Down));
|
||||
|
||||
@ -336,11 +337,11 @@ where
|
||||
return false;
|
||||
}
|
||||
|
||||
if is_first_page && is_swipe_up {
|
||||
if pager.is_first() && is_swipe_up {
|
||||
return true;
|
||||
}
|
||||
|
||||
if is_last_page && is_swipe_down {
|
||||
if pager.is_last() && is_swipe_down {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -348,18 +349,15 @@ where
|
||||
}
|
||||
|
||||
fn should_animate_swipe(&self, swipe_direction: Direction) -> bool {
|
||||
let is_first_page = self.content.inner.current_page() == 0;
|
||||
let is_last_page =
|
||||
self.content.inner.current_page() == (self.content.inner.num_pages() - 1);
|
||||
|
||||
let pager = self.content.inner.pager();
|
||||
let is_swipe_up = matches!(swipe_direction, Direction::Up);
|
||||
let is_swipe_down = matches!(swipe_direction, Direction::Down);
|
||||
|
||||
if is_last_page && is_swipe_up {
|
||||
if pager.is_last() && is_swipe_up {
|
||||
return true;
|
||||
}
|
||||
|
||||
if is_first_page && is_swipe_down {
|
||||
if pager.is_first() && is_swipe_down {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -369,7 +367,7 @@ where
|
||||
|
||||
impl<T> Component for InternallySwipableContent<T>
|
||||
where
|
||||
T: Component + InternallySwipable,
|
||||
T: Component + PaginateFull,
|
||||
{
|
||||
type Msg = T::Msg;
|
||||
|
||||
@ -395,10 +393,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PaginateFull for InternallySwipableContent<T>
|
||||
where
|
||||
T: Component + PaginateFull,
|
||||
{
|
||||
fn pager(&self) -> Pager {
|
||||
self.content.pager()
|
||||
}
|
||||
|
||||
fn change_page(&mut self, to_page: u16) {
|
||||
self.content.change_page(to_page);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for InternallySwipableContent<T>
|
||||
where
|
||||
T: crate::trace::Trace + Component + InternallySwipable,
|
||||
T: crate::trace::Trace + Component + PaginateFull,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("InternallySwipableContent");
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{
|
||||
paginated::Paginate,
|
||||
paginated::SinglePage,
|
||||
text::paragraphs::{Paragraph, Paragraphs},
|
||||
Component, Event, EventCtx, Never,
|
||||
},
|
||||
@ -66,6 +66,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn() -> TString<'static>> SinglePage for UpdatableMoreInfo<F> {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<F> crate::trace::Trace for UpdatableMoreInfo<F>
|
||||
where
|
||||
|
@ -6,20 +6,21 @@ use crate::{
|
||||
ui::{
|
||||
component::{
|
||||
base::{AttachType, Component},
|
||||
Event, EventCtx, Paginate,
|
||||
paginated::SinglePage,
|
||||
Event, EventCtx, PaginateFull,
|
||||
},
|
||||
constant::screen,
|
||||
display::{Color, Icon},
|
||||
geometry::{Direction, Offset, Rect},
|
||||
lerp::Lerp,
|
||||
shape::{Bar, Renderer},
|
||||
util::animation_disabled,
|
||||
util::{animation_disabled, Pager},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
super::component::button::{Button, ButtonContent, ButtonMsg, IconText},
|
||||
theme, InternallySwipable,
|
||||
theme,
|
||||
};
|
||||
|
||||
pub enum VerticalMenuChoiceMsg {
|
||||
@ -303,6 +304,8 @@ impl Component for VerticalMenu {
|
||||
}
|
||||
}
|
||||
|
||||
impl SinglePage for VerticalMenu {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for VerticalMenu {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
@ -317,14 +320,14 @@ impl crate::trace::Trace for VerticalMenu {
|
||||
|
||||
// Polymorphic struct, avoid adding code as it gets duplicated, prefer
|
||||
// extending VerticalMenu instead.
|
||||
pub struct PagedVerticalMenu<F: Fn(usize) -> TString<'static>> {
|
||||
pub struct PagedVerticalMenu<F: Fn(u16) -> TString<'static>> {
|
||||
inner: VerticalMenu,
|
||||
page: usize,
|
||||
page: u16,
|
||||
item_count: usize,
|
||||
label_fn: F,
|
||||
}
|
||||
|
||||
impl<F: Fn(usize) -> TString<'static>> PagedVerticalMenu<F> {
|
||||
impl<F: Fn(u16) -> TString<'static>> PagedVerticalMenu<F> {
|
||||
pub fn new(item_count: usize, label_fn: F) -> Self {
|
||||
let mut result = Self {
|
||||
inner: VerticalMenu::select_word(["".into(), "".into(), "".into()]),
|
||||
@ -337,20 +340,24 @@ impl<F: Fn(usize) -> TString<'static>> PagedVerticalMenu<F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn(usize) -> TString<'static>> Paginate for PagedVerticalMenu<F> {
|
||||
fn page_count(&self) -> usize {
|
||||
self.num_pages()
|
||||
impl<F: Fn(u16) -> TString<'static>> PaginateFull for PagedVerticalMenu<F> {
|
||||
fn pager(&self) -> Pager {
|
||||
let num_pages =
|
||||
(self.item_count / self.inner.n_items) + (self.item_count % self.inner.n_items).min(1);
|
||||
Pager::new(num_pages as u16).with_current(self.page)
|
||||
}
|
||||
|
||||
fn change_page(&mut self, active_page: usize) {
|
||||
for b in 0..self.inner.n_items {
|
||||
let i = active_page * self.inner.n_items + b;
|
||||
let text = if i < self.item_count {
|
||||
fn change_page(&mut self, active_page: u16) {
|
||||
let n_items = self.inner.n_items as u16;
|
||||
for b in 0..n_items {
|
||||
let i = active_page * n_items + b;
|
||||
let text = if i < self.item_count as u16 {
|
||||
(self.label_fn)(i)
|
||||
} else {
|
||||
"".into()
|
||||
};
|
||||
let mut dummy_ctx = EventCtx::new();
|
||||
let b = b as usize;
|
||||
self.inner.buttons[b].enable_if(&mut dummy_ctx, !text.is_empty());
|
||||
self.inner.buttons[b].set_content(ButtonContent::Text(text));
|
||||
}
|
||||
@ -359,7 +366,7 @@ impl<F: Fn(usize) -> TString<'static>> Paginate for PagedVerticalMenu<F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn(usize) -> TString<'static>> Component for PagedVerticalMenu<F> {
|
||||
impl<F: Fn(u16) -> TString<'static>> Component for PagedVerticalMenu<F> {
|
||||
type Msg = VerticalMenuChoiceMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -370,7 +377,7 @@ impl<F: Fn(usize) -> TString<'static>> Component for PagedVerticalMenu<F> {
|
||||
let msg = self.inner.event(ctx, event);
|
||||
if let Some(VerticalMenuChoiceMsg::Selected(i)) = msg {
|
||||
return Some(VerticalMenuChoiceMsg::Selected(
|
||||
self.inner.n_items * self.page + i,
|
||||
self.inner.n_items * self.page as usize + i,
|
||||
));
|
||||
}
|
||||
msg
|
||||
@ -381,18 +388,8 @@ impl<F: Fn(usize) -> TString<'static>> Component for PagedVerticalMenu<F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn(usize) -> TString<'static>> InternallySwipable for PagedVerticalMenu<F> {
|
||||
fn current_page(&self) -> usize {
|
||||
self.page
|
||||
}
|
||||
|
||||
fn num_pages(&self) -> usize {
|
||||
(self.item_count / self.inner.n_items) + (self.item_count % self.inner.n_items).min(1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<F: Fn(usize) -> TString<'static>> crate::trace::Trace for PagedVerticalMenu<F> {
|
||||
impl<F: Fn(u16) -> TString<'static>> crate::trace::Trace for PagedVerticalMenu<F> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
self.inner.trace(t)
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ use crate::{
|
||||
micropython::obj::Obj,
|
||||
ui::{
|
||||
component::{
|
||||
paginated::PaginateFull,
|
||||
text::paragraphs::{ParagraphSource, Paragraphs},
|
||||
Component, Never, Timeout,
|
||||
},
|
||||
@ -71,7 +72,7 @@ where
|
||||
|
||||
impl<T> ComponentMsgObj for Frame<T>
|
||||
where
|
||||
T: ComponentMsgObj,
|
||||
T: ComponentMsgObj + PaginateFull,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
component::{
|
||||
swipe_detect::SwipeSettings,
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, VecExt},
|
||||
Component, ComponentExt, EventCtx, Paginate,
|
||||
Component, ComponentExt, EventCtx, PaginateFull,
|
||||
},
|
||||
flow::{
|
||||
base::{Decision, DecisionBuilder as _},
|
||||
@ -234,7 +234,7 @@ pub fn new_confirm_action(
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn new_confirm_action_uni<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
fn new_confirm_action_uni<T: Component + PaginateFull + MaybeTrace + 'static>(
|
||||
content: SwipeContent<SwipePage<T>>,
|
||||
extra: ConfirmActionExtra,
|
||||
strings: ConfirmActionStrings,
|
||||
@ -266,14 +266,12 @@ fn new_confirm_action_uni<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
}
|
||||
|
||||
if page_counter {
|
||||
fn footer_update_fn<T: Component + Paginate>(
|
||||
fn footer_update_fn<T: Component + PaginateFull>(
|
||||
content: &SwipeContent<SwipePage<T>>,
|
||||
ctx: &mut EventCtx,
|
||||
footer: &mut Footer,
|
||||
) {
|
||||
let current_page = content.inner().current_page();
|
||||
let page_count = content.inner().page_count();
|
||||
footer.update_page_counter(ctx, current_page, page_count);
|
||||
footer.update_pager(ctx, content.inner().pager());
|
||||
}
|
||||
|
||||
content = content
|
||||
@ -412,12 +410,12 @@ fn create_confirm(
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn new_confirm_action_simple<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
pub fn new_confirm_action_simple<T: Component + PaginateFull + MaybeTrace + 'static>(
|
||||
content: T,
|
||||
extra: ConfirmActionExtra,
|
||||
strings: ConfirmActionStrings,
|
||||
hold: bool,
|
||||
page_limit: Option<usize>,
|
||||
page_limit: Option<u16>,
|
||||
frame_margin: usize,
|
||||
page_counter: bool,
|
||||
) -> Result<SwipeFlow, error::Error> {
|
||||
|
@ -5,6 +5,7 @@ use crate::{
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{
|
||||
paginated::PaginateFull as _,
|
||||
swipe_detect::SwipeSettings,
|
||||
text::paragraphs::{Paragraph, Paragraphs},
|
||||
ComponentExt, EventCtx,
|
||||
@ -19,8 +20,8 @@ use crate::{
|
||||
|
||||
use super::super::{
|
||||
component::{
|
||||
FidoCredential, Footer, Frame, FrameMsg, InternallySwipable, PagedVerticalMenu, PromptMsg,
|
||||
PromptScreen, SwipeContent, VerticalMenu, VerticalMenuChoiceMsg,
|
||||
FidoCredential, Footer, Frame, FrameMsg, PagedVerticalMenu, PromptMsg, PromptScreen,
|
||||
SwipeContent, VerticalMenu, VerticalMenuChoiceMsg,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
@ -89,13 +90,11 @@ impl FlowController for ConfirmFido {
|
||||
}
|
||||
|
||||
fn footer_update_fn(
|
||||
content: &SwipeContent<SwipePage<PagedVerticalMenu<impl Fn(usize) -> TString<'static>>>>,
|
||||
content: &SwipeContent<SwipePage<PagedVerticalMenu<impl Fn(u16) -> TString<'static>>>>,
|
||||
ctx: &mut EventCtx,
|
||||
footer: &mut Footer,
|
||||
) {
|
||||
let current_page = content.inner().inner().current_page();
|
||||
let total_pages = content.inner().inner().num_pages();
|
||||
footer.update_page_counter(ctx, current_page, total_pages);
|
||||
footer.update_pager(ctx, content.inner().inner().pager());
|
||||
}
|
||||
|
||||
fn single_cred() -> bool {
|
||||
@ -128,8 +127,8 @@ pub fn new_confirm_fido(
|
||||
// Closure to lazy-load the information on given page index.
|
||||
// Done like this to allow arbitrarily many pages without
|
||||
// the need of any allocation here in Rust.
|
||||
let label_fn = move |page_index| {
|
||||
let account = unwrap!(accounts.get(page_index));
|
||||
let label_fn = move |page_index: u16| {
|
||||
let account = unwrap!(accounts.get(page_index as usize));
|
||||
account
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| TString::from_str("-"))
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
text::paragraphs::{
|
||||
Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, Paragraphs, VecExt,
|
||||
},
|
||||
ComponentExt, EventCtx,
|
||||
ComponentExt, EventCtx, PaginateFull as _,
|
||||
},
|
||||
flow::{
|
||||
base::{Decision, DecisionBuilder as _},
|
||||
@ -147,11 +147,7 @@ fn footer_update_fn(
|
||||
ctx: &mut EventCtx,
|
||||
footer: &mut Footer,
|
||||
) {
|
||||
// FIXME: current_page is implemented for Paragraphs and we have to use Vec::len
|
||||
// to get total pages instead of using Paginate because it borrows mutably
|
||||
let current_page = content.inner().inner().current_page();
|
||||
let total_pages = content.inner().inner().inner().len() / 2; // 2 paragraphs per page
|
||||
footer.update_page_counter(ctx, current_page, total_pages);
|
||||
footer.update_pager(ctx, content.inner().inner().pager());
|
||||
}
|
||||
|
||||
pub fn new_continue_recovery_homepage(
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
component::{
|
||||
swipe_detect::SwipeSettings,
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs},
|
||||
ButtonRequestExt, ComponentExt, EventCtx,
|
||||
ButtonRequestExt, ComponentExt, EventCtx, PaginateFull as _,
|
||||
},
|
||||
flow::{
|
||||
base::{Decision, DecisionBuilder as _},
|
||||
@ -20,8 +20,8 @@ use heapless::Vec;
|
||||
|
||||
use super::super::{
|
||||
component::{
|
||||
Footer, Frame, FrameMsg, Header, InternallySwipable, InternallySwipableContent,
|
||||
PromptScreen, ShareWords, SwipeContent,
|
||||
Footer, Frame, FrameMsg, Header, InternallySwipableContent, PromptScreen, ShareWords,
|
||||
SwipeContent,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
@ -74,9 +74,7 @@ fn footer_updating_func(
|
||||
ctx: &mut EventCtx,
|
||||
footer: &mut Footer,
|
||||
) {
|
||||
let current_page = content.inner().current_page();
|
||||
let total_pages = content.inner().num_pages();
|
||||
footer.update_page_counter(ctx, current_page, total_pages);
|
||||
footer.update_pager(ctx, content.inner().pager());
|
||||
}
|
||||
|
||||
pub fn new_show_share_words(
|
||||
|
@ -48,7 +48,7 @@ pub struct ConfirmValue {
|
||||
chunkify: bool,
|
||||
text_mono: bool,
|
||||
page_counter: bool,
|
||||
page_limit: Option<usize>,
|
||||
page_limit: Option<u16>,
|
||||
swipe_up: bool,
|
||||
swipe_down: bool,
|
||||
swipe_right: bool,
|
||||
@ -189,7 +189,7 @@ impl ConfirmValue {
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_page_limit(mut self, page_limit: Option<usize>) -> Self {
|
||||
pub const fn with_page_limit(mut self, page_limit: Option<u16>) -> Self {
|
||||
self.page_limit = page_limit;
|
||||
self
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user