refactor(core/rust): pass around &str instead of [u8] in most places

pull/2220/head
matejcik 2 years ago committed by matejcik
parent 785dc7f4d7
commit 32c3320f07

@ -67,7 +67,7 @@ pub fn backlight(val: i32) -> i32 {
unsafe { display_backlight(val) }
}
pub fn text(baseline_x: i32, baseline_y: i32, text: &[u8], font: i32, fgcolor: u16, bgcolor: u16) {
pub fn text(baseline_x: i32, baseline_y: i32, text: &str, font: i32, fgcolor: u16, bgcolor: u16) {
unsafe {
display_text(
baseline_x,
@ -81,10 +81,16 @@ pub fn text(baseline_x: i32, baseline_y: i32, text: &[u8], font: i32, fgcolor: u
}
}
pub fn text_width(text: &[u8], font: i32) -> i32 {
pub fn text_width(text: &str, font: i32) -> i32 {
unsafe { display_text_width(text.as_ptr() as _, text.len() as _, font) }
}
pub fn char_width(ch: char, font: i32) -> i32 {
let mut buf = [0u8; 4];
let encoding = ch.encode_utf8(&mut buf);
text_width(encoding, font)
}
pub fn text_height(font: i32) -> i32 {
unsafe { display_text_height(font) }
}

@ -21,7 +21,7 @@ pub struct Label<T> {
impl<T> Label<T>
where
T: Deref<Target = [u8]>,
T: Deref<Target = str>,
{
pub fn new(text: T, align: Alignment, style: LabelStyle) -> Self {
Self {
@ -58,7 +58,7 @@ where
impl<T> Component for Label<T>
where
T: Deref<Target = [u8]>,
T: Deref<Target = str>,
{
type Msg = Never;

@ -20,8 +20,8 @@ pub trait Paginate {
impl<F, T> Paginate for FormattedText<F, T>
where
F: AsRef<[u8]>,
T: AsRef<[u8]>,
F: AsRef<str>,
T: AsRef<str>,
{
fn page_count(&mut self) -> usize {
let mut page_count = 1; // There's always at least one page.

@ -21,7 +21,7 @@ pub const MAX_ARGUMENTS: usize = 6;
pub struct FormattedText<F, T> {
layout: TextLayout,
format: F,
args: LinearMap<&'static [u8], T, MAX_ARGUMENTS>,
args: LinearMap<&'static str, T, MAX_ARGUMENTS>,
char_offset: usize,
}
@ -35,7 +35,7 @@ impl<F, T> FormattedText<F, T> {
}
}
pub fn with(mut self, key: &'static [u8], value: T) -> Self {
pub fn with(mut self, key: &'static str, value: T) -> Self {
if self.args.insert(key, value).is_err() {
#[cfg(feature = "ui_debug")]
panic!("text args map is full");
@ -83,18 +83,18 @@ impl<F, T> FormattedText<F, T> {
impl<F, T> FormattedText<F, T>
where
F: AsRef<[u8]>,
T: AsRef<[u8]>,
F: AsRef<str>,
T: AsRef<str>,
{
pub fn layout_content(&self, sink: &mut dyn LayoutSink) -> LayoutFit {
let mut cursor = self.layout.initial_cursor();
let mut ops = Op::skip_n_text_bytes(
Tokenizer::new(self.format.as_ref()).flat_map(|arg| match arg {
Token::Literal(literal) => Some(Op::Text(literal)),
Token::Argument(b"mono") => Some(Op::Font(self.layout.mono_font)),
Token::Argument(b"bold") => Some(Op::Font(self.layout.bold_font)),
Token::Argument(b"normal") => Some(Op::Font(self.layout.normal_font)),
Token::Argument(b"medium") => Some(Op::Font(self.layout.medium_font)),
Token::Argument("mono") => Some(Op::Font(self.layout.mono_font)),
Token::Argument("bold") => Some(Op::Font(self.layout.bold_font)),
Token::Argument("normal") => Some(Op::Font(self.layout.normal_font)),
Token::Argument("medium") => Some(Op::Font(self.layout.medium_font)),
Token::Argument(argument) => self
.args
.get(argument)
@ -108,8 +108,8 @@ where
impl<F, T> Component for FormattedText<F, T>
where
F: AsRef<[u8]>,
T: AsRef<[u8]>,
F: AsRef<str>,
T: AsRef<str>,
{
type Msg = Never;
@ -141,8 +141,8 @@ pub mod trace {
impl<'a, F, T> crate::trace::Trace for TraceText<'a, F, T>
where
F: AsRef<[u8]>,
T: AsRef<[u8]>,
F: AsRef<str>,
T: AsRef<str>,
{
fn trace(&self, d: &mut dyn crate::trace::Tracer) {
self.0.layout_content(&mut TraceSink(d));
@ -153,8 +153,8 @@ pub mod trace {
#[cfg(feature = "ui_debug")]
impl<F, T> crate::trace::Trace for FormattedText<F, T>
where
F: AsRef<[u8]>,
T: AsRef<[u8]>,
F: AsRef<str>,
T: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Text");
@ -166,9 +166,9 @@ where
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Token<'a> {
/// Process literal text content.
Literal(&'a [u8]),
Literal(&'a str),
/// Process argument with specified descriptor.
Argument(&'a [u8]),
Argument(&'a str),
}
/// Processes a format string into an iterator of `Token`s.
@ -182,17 +182,18 @@ pub enum Token<'a> {
/// assert!(matches!(parser.next(), Some(Token::Literal(", where you been?"))));
/// ```
pub struct Tokenizer<'a> {
input: &'a [u8],
input: &'a str,
inner: Peekable<Enumerate<slice::Iter<'a, u8>>>,
}
impl<'a> Tokenizer<'a> {
/// Create a new tokenizer for bytes of a formatting string `input`,
/// returning an iterator.
pub fn new(input: &'a [u8]) -> Self {
pub fn new(input: &'a str) -> Self {
assert!(input.is_ascii());
Self {
input,
inner: input.iter().enumerate().peekable(),
inner: input.as_bytes().iter().enumerate().peekable(),
}
}
}
@ -246,26 +247,26 @@ mod tests {
#[test]
fn tokenizer_yields_expected_tokens() {
assert!(Tokenizer::new(b"").eq([]));
assert!(Tokenizer::new(b"x").eq([Token::Literal(b"x")]));
assert!(Tokenizer::new(b"x\0y").eq([Token::Literal("x\0y".as_bytes())]));
assert!(Tokenizer::new(b"{").eq([]));
assert!(Tokenizer::new(b"x{").eq([Token::Literal(b"x")]));
assert!(Tokenizer::new(b"x{y").eq([Token::Literal(b"x")]));
assert!(Tokenizer::new(b"{}").eq([Token::Argument(b"")]));
assert!(Tokenizer::new(b"x{}y{").eq([
Token::Literal(b"x"),
Token::Argument(b""),
Token::Literal(b"y"),
assert!(Tokenizer::new("").eq([]));
assert!(Tokenizer::new("x").eq([Token::Literal("x")]));
assert!(Tokenizer::new("x\0y").eq([Token::Literal("x\0y")]));
assert!(Tokenizer::new("{").eq([]));
assert!(Tokenizer::new("x{").eq([Token::Literal("x")]));
assert!(Tokenizer::new("x{y").eq([Token::Literal("x")]));
assert!(Tokenizer::new("{}").eq([Token::Argument("")]));
assert!(Tokenizer::new("x{}y{").eq([
Token::Literal("x"),
Token::Argument(""),
Token::Literal("y"),
]));
assert!(Tokenizer::new(b"{\0}").eq([Token::Argument("\0".as_bytes()),]));
assert!(Tokenizer::new(b"{{y}").eq([Token::Argument(b"{y"),]));
assert!(Tokenizer::new(b"{{{{xyz").eq([]));
assert!(Tokenizer::new(b"x{}{{}}}}").eq([
Token::Literal(b"x"),
Token::Argument(b""),
Token::Argument(b"{"),
Token::Literal(b"}}}"),
assert!(Tokenizer::new("{\0}").eq([Token::Argument("\0"),]));
assert!(Tokenizer::new("{{y}").eq([Token::Argument("{y"),]));
assert!(Tokenizer::new("{{{{xyz").eq([]));
assert!(Tokenizer::new("x{}{{}}}}").eq([
Token::Literal("x"),
Token::Argument(""),
Token::Argument("{"),
Token::Literal("}}}"),
]));
}
}

@ -169,7 +169,7 @@ trait GlyphMetrics {
impl GlyphMetrics for Font {
fn char_width(&self, ch: char) -> i32 {
self.text_width(&[ch as u8])
Font::char_width(*self, ch)
}
fn line_height(&self) -> i32 {

@ -114,11 +114,11 @@ impl TextLayout {
self.bounds.top_left() + Offset::y(self.text_font.text_height() + self.padding_top)
}
pub fn fit_text(&self, text: &[u8]) -> LayoutFit {
pub fn fit_text(&self, text: &str) -> LayoutFit {
self.layout_text(text, &mut self.initial_cursor(), &mut TextNoOp)
}
pub fn render_text(&self, text: &[u8]) {
pub fn render_text(&self, text: &str) {
self.layout_text(text, &mut self.initial_cursor(), &mut TextRenderer);
}
@ -167,7 +167,7 @@ impl TextLayout {
pub fn layout_text(
&self,
text: &[u8],
text: &str,
cursor: &mut Point,
sink: &mut dyn LayoutSink,
) -> LayoutFit {
@ -276,7 +276,7 @@ impl LayoutFit {
/// Visitor for text segment operations.
pub trait LayoutSink {
fn text(&mut self, _cursor: Point, _layout: &TextLayout, _text: &[u8]) {}
fn text(&mut self, _cursor: Point, _layout: &TextLayout, _text: &str) {}
fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {}
fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {}
fn line_break(&mut self, _cursor: Point) {}
@ -290,7 +290,7 @@ impl LayoutSink for TextNoOp {}
pub struct TextRenderer;
impl LayoutSink for TextRenderer {
fn text(&mut self, cursor: Point, layout: &TextLayout, text: &[u8]) {
fn text(&mut self, cursor: Point, layout: &TextLayout, text: &str) {
display::text(
cursor,
text,
@ -303,7 +303,7 @@ impl LayoutSink for TextRenderer {
fn hyphen(&mut self, cursor: Point, layout: &TextLayout) {
display::text(
cursor,
b"-",
"-",
layout.hyphen_font,
layout.hyphen_color,
layout.background_color,
@ -313,7 +313,7 @@ impl LayoutSink for TextRenderer {
fn ellipsis(&mut self, cursor: Point, layout: &TextLayout) {
display::text(
cursor,
b"...",
"...",
layout.ellipsis_font,
layout.ellipsis_color,
layout.background_color,
@ -330,8 +330,8 @@ pub mod trace {
pub struct TraceSink<'a>(pub &'a mut dyn crate::trace::Tracer);
impl<'a> LayoutSink for TraceSink<'a> {
fn text(&mut self, _cursor: Point, _layout: &TextLayout, text: &[u8]) {
self.0.bytes(text);
fn text(&mut self, _cursor: Point, _layout: &TextLayout, text: &str) {
self.0.string(text);
}
fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {
@ -351,7 +351,7 @@ pub mod trace {
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Op<'a> {
/// Render text with current color and font.
Text(&'a [u8]),
Text(&'a str),
/// Set current text color.
Color(Color),
/// Set currently used font.
@ -396,22 +396,22 @@ struct Span {
impl Span {
fn fit_horizontally(
text: &[u8],
text: &str,
max_width: i32,
text_font: Font,
hyphen_font: Font,
breaking: LineBreaking,
) -> Self {
const ASCII_LF: u8 = b'\n';
const ASCII_CR: u8 = b'\r';
const ASCII_SPACE: u8 = b' ';
const ASCII_HYPHEN: u8 = b'-';
const ASCII_LF: char = '\n';
const ASCII_CR: char = '\r';
const ASCII_SPACE: char = ' ';
const ASCII_HYPHEN: char = '-';
fn is_whitespace(ch: u8) -> bool {
fn is_whitespace(ch: char) -> bool {
ch == ASCII_SPACE || ch == ASCII_LF || ch == ASCII_CR
}
let hyphen_width = hyphen_font.text_width(&[ASCII_HYPHEN]);
let hyphen_width = hyphen_font.char_width(ASCII_HYPHEN);
// The span we return in case the line has to break. We mutate it in the
// possible break points, and its initial value is returned in case no text
@ -427,8 +427,8 @@ impl Span {
let mut span_width = 0;
let mut found_any_whitespace = false;
for (i, &ch) in text.iter().enumerate() {
let char_width = text_font.text_width(&[ch]);
for (i, ch) in text.char_indices() {
let char_width = text_font.char_width(ch);
// Consider if we could be breaking the line at this position.
if is_whitespace(ch) {

@ -29,7 +29,7 @@ pub struct Paragraphs<T> {
impl<T> Paragraphs<T>
where
T: AsRef<[u8]>,
T: AsRef<str>,
{
pub fn new() -> Self {
Self {
@ -114,7 +114,7 @@ where
impl<T> Component for Paragraphs<T>
where
T: AsRef<[u8]>,
T: AsRef<str>,
{
type Msg = Never;
@ -146,7 +146,7 @@ where
impl<T> Paginate for Paragraphs<T>
where
T: AsRef<[u8]>,
T: AsRef<str>,
{
fn page_count(&mut self) -> usize {
// There's always at least one page.
@ -175,7 +175,7 @@ pub mod trace {
impl<T> crate::trace::Trace for Paragraphs<T>
where
T: AsRef<[u8]>,
T: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Paragraphs");
@ -201,20 +201,20 @@ pub struct Paragraph<T> {
impl<T> Paragraph<T>
where
T: AsRef<[u8]>,
T: AsRef<str>,
{
pub fn new(content: T, layout: TextLayout) -> Self {
Self { content, layout }
}
pub fn content(&self, char_offset: usize) -> &[u8] {
pub fn content(&self, char_offset: usize) -> &str {
&self.content.as_ref()[char_offset..]
}
}
impl<T> Dimensions for Paragraph<T>
where
T: AsRef<[u8]>,
T: AsRef<str>,
{
fn fit(&mut self, area: Rect) {
self.layout.bounds = area;
@ -246,7 +246,7 @@ struct PageBreakIterator<'a, T> {
/// `PageOffset { 0, 0 }` even if the paragraph vector is empty.
impl<'a, T> Iterator for PageBreakIterator<'a, T>
where
T: AsRef<[u8]>,
T: AsRef<str>,
{
/// `PageOffset` denotes the first paragraph that is rendered and a
/// character offset in that paragraph.

@ -152,7 +152,7 @@ pub fn loader_indeterminate(
);
}
pub fn text(baseline: Point, text: &[u8], font: Font, fg_color: Color, bg_color: Color) {
pub fn text(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) {
display::text(
baseline.x,
baseline.y,
@ -163,7 +163,7 @@ pub fn text(baseline: Point, text: &[u8], font: Font, fg_color: Color, bg_color:
);
}
pub fn text_center(baseline: Point, text: &[u8], font: Font, fg_color: Color, bg_color: Color) {
pub fn text_center(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) {
let w = font.text_width(text);
display::text(
baseline.x - w / 2,
@ -183,10 +183,14 @@ impl Font {
Self(id)
}
pub fn text_width(self, text: &[u8]) -> i32 {
pub fn text_width(self, text: &str) -> i32 {
display::text_width(text, self.0)
}
pub fn char_width(self, ch: char) -> i32 {
display::char_width(ch, self.0)
}
pub fn text_height(self) -> i32 {
display::text_height(self.0)
}

@ -37,7 +37,7 @@ pub struct Button<T> {
state: State,
}
impl<T: AsRef<[u8]>> Button<T> {
impl<T: AsRef<str>> Button<T> {
pub fn new(pos: ButtonPos, content: ButtonContent<T>, styles: ButtonStyleSheet) -> Self {
Self {
pos,
@ -100,7 +100,7 @@ impl<T: AsRef<[u8]>> Button<T> {
impl<T> Component for Button<T>
where
T: AsRef<[u8]>,
T: AsRef<str>,
{
type Msg = ButtonMsg;
@ -157,7 +157,7 @@ where
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Button<T>
where
T: AsRef<[u8]> + crate::trace::Trace,
T: AsRef<str> + crate::trace::Trace,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Button");

@ -22,7 +22,7 @@ pub struct Dialog<T, U> {
impl<T, U> Dialog<T, U>
where
T: Component,
U: AsRef<[u8]>,
U: AsRef<str>,
{
pub fn new(content: T, left: Option<Button<U>>, right: Option<Button<U>>) -> Self {
Self {
@ -40,7 +40,7 @@ where
impl<T, U> Component for Dialog<T, U>
where
T: Component,
U: AsRef<[u8]>,
U: AsRef<str>,
{
type Msg = DialogMsg<T::Msg>;
@ -80,7 +80,7 @@ where
impl<T, U> crate::trace::Trace for Dialog<T, U>
where
T: crate::trace::Trace,
U: crate::trace::Trace + AsRef<[u8]>,
U: crate::trace::Trace + AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Dialog");

@ -14,7 +14,7 @@ pub struct Frame<T, U> {
impl<T, U> Frame<T, U>
where
T: Component,
U: AsRef<[u8]>,
U: AsRef<str>,
{
pub fn new(title: U, content: T) -> Self {
Self {
@ -32,7 +32,7 @@ where
impl<T, U> Component for Frame<T, U>
where
T: Component,
U: AsRef<[u8]>,
U: AsRef<str>,
{
type Msg = T::Msg;
@ -68,7 +68,7 @@ where
impl<T, U> crate::trace::Trace for Frame<T, U>
where
T: crate::trace::Trace,
U: crate::trace::Trace + AsRef<[u8]>,
U: crate::trace::Trace + AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Frame");

@ -2,7 +2,7 @@ use core::convert::TryInto;
use crate::{
error::Error,
micropython::{buffer::Buffer, map::Map, module::Module, obj::Obj, qstr::Qstr},
micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr},
ui::{
component::{
base::Component,
@ -39,7 +39,7 @@ where
impl<T, U> ComponentMsgObj for Frame<T, U>
where
T: ComponentMsgObj,
U: AsRef<[u8]>,
U: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
self.inner().msg_try_into_obj(msg)
@ -48,12 +48,12 @@ where
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], kwargs: &Map| {
let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let action: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
let description: Option<Buffer> =
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let action: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
let description: Option<StrBuffer> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let verb: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_verb)?.try_into_option()?;
let verb_cancel: Option<Buffer> =
let verb: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_verb)?.try_into_option()?;
let verb_cancel: Option<StrBuffer> =
kwargs.get(Qstr::MP_QSTR_verb_cancel)?.try_into_option()?;
let reverse: bool = kwargs.get(Qstr::MP_QSTR_reverse)?.try_into()?;
@ -74,8 +74,8 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
title,
ButtonPage::new(
FormattedText::new::<theme::T1DefaultText>(format)
.with(b"action", action.unwrap_or_default())
.with(b"description", description.unwrap_or_default()),
.with("action", action.unwrap_or_default())
.with("description", description.unwrap_or_default()),
theme::BG,
),
))?;
@ -86,9 +86,9 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
extern "C" fn new_confirm_text(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], kwargs: &Map| {
let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let data: Buffer = kwargs.get(Qstr::MP_QSTR_data)?.try_into()?;
let description: Option<Buffer> =
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let data: StrBuffer = kwargs.get(Qstr::MP_QSTR_data)?.try_into()?;
let description: Option<StrBuffer> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let obj = LayoutObj::new(Frame::new(
@ -165,7 +165,7 @@ mod tests {
impl<T, U> ComponentMsgObj for Dialog<T, U>
where
T: ComponentMsgObj,
U: AsRef<[u8]>,
U: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
@ -182,7 +182,7 @@ mod tests {
FormattedText::new::<theme::T1DefaultText>(
"Testing text layout, with some text, and some more text. And {param}",
)
.with(b"param", b"parameters!"),
.with("param", "parameters!"),
Some(Button::with_text(
ButtonPos::Left,
"Left",
@ -212,7 +212,7 @@ arameters! > left:<Button text:Left > right:<Button text:Right > >"#
FormattedText::new::<theme::T1DefaultText>(
"Testing text layout, with some text, and some more text. And {param}",
)
.with(b"param", b"parameters!"),
.with("param", "parameters!"),
Some(Button::with_text(
ButtonPos::Left,
"Left",

@ -156,7 +156,7 @@ impl<T> Button<T> {
pub fn paint_content(&self, style: &ButtonStyle)
where
T: AsRef<[u8]>,
T: AsRef<str>,
{
match &self.content {
ButtonContent::Empty => {}
@ -189,7 +189,7 @@ impl<T> Button<T> {
impl<T> Component for Button<T>
where
T: AsRef<[u8]>,
T: AsRef<str>,
{
type Msg = ButtonMsg;
@ -266,7 +266,7 @@ where
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Button<T>
where
T: AsRef<[u8]> + crate::trace::Trace,
T: AsRef<str> + crate::trace::Trace,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Button");
@ -322,7 +322,7 @@ impl<T> Button<T> {
where
F0: Fn(ButtonMsg) -> Option<R>,
F1: Fn(ButtonMsg) -> Option<R>,
T: AsRef<[u8]>,
T: AsRef<str>,
{
const BUTTON_SPACING: i32 = 6;
(

@ -14,7 +14,7 @@ pub struct Frame<T, U> {
impl<T, U> Frame<T, U>
where
T: Component,
U: AsRef<[u8]>,
U: AsRef<str>,
{
pub fn new(title: U, content: T) -> Self {
Self {
@ -32,7 +32,7 @@ where
impl<T, U> Component for Frame<T, U>
where
T: Component,
U: AsRef<[u8]>,
U: AsRef<str>,
{
type Msg = T::Msg;
@ -76,7 +76,7 @@ where
impl<T, U> crate::trace::Trace for Frame<T, U>
where
T: crate::trace::Trace,
U: crate::trace::Trace + AsRef<[u8]>,
U: crate::trace::Trace + AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Frame");

@ -20,7 +20,7 @@ use crate::{
const MAX_LENGTH: usize = 8;
pub struct Bip39Input {
button: Button<&'static [u8]>,
button: Button<&'static str>,
textbox: TextBox<MAX_LENGTH>,
multi_tap: MultiTapKeyboard,
suggested_word: Option<&'static str>,
@ -95,7 +95,7 @@ impl Component for Bip39Input {
self.button.paint_background(&style);
// Paint the entered content (the prefix of the suggested word).
let text = self.textbox.content().as_bytes();
let text = self.textbox.content();
let width = style.font.text_width(text);
// Content starts in the left-center point, offset by 16px to the right and 8px
// to the bottom.
@ -113,7 +113,7 @@ impl Component for Bip39Input {
let word_baseline = text_baseline + Offset::new(width, 0);
display::text(
word_baseline,
word.as_bytes(),
word,
style.font,
theme::GREY_LIGHT,
style.button_color,
@ -211,7 +211,7 @@ impl Bip39Input {
// Disabled button.
self.button.disable(ctx);
self.button.set_stylesheet(ctx, theme::button_default());
self.button.set_content(ctx, ButtonContent::Text(b""));
self.button.set_content(ctx, ButtonContent::Text(""));
}
}
}

@ -79,9 +79,6 @@ impl MultiTapKeyboard {
/// the pending state. Caller is required to handle the timer event and
/// call `Self::clear_pending_state` when the timer hits.
pub fn click_key(&mut self, ctx: &mut EventCtx, key: usize, key_text: &str) -> TextEdit {
// To simplify things, we assume the key text is ASCII-only.
let ascii_text = key_text.as_bytes();
let (is_pending, press) = match &self.pending {
Some(pending) if pending.key == key => {
// This key is pending. Cycle the last inserted character through the
@ -102,7 +99,7 @@ impl MultiTapKeyboard {
// progress into a pending state (to display the pending marker), but such
// transition only happens as a result of an append op, so the painting should
// be requested by handling the `TextEdit`.
self.pending = if ascii_text.len() > 1 {
self.pending = if key_text.len() > 1 {
Some(Pending {
key,
press,
@ -112,7 +109,9 @@ impl MultiTapKeyboard {
None
};
let ch = ascii_text[press % ascii_text.len()] as char;
assert!(!key_text.is_empty());
// Now we can be sure that a looped iterator will return a value
let ch = key_text.chars().cycle().nth(press).unwrap();
if is_pending {
TextEdit::ReplaceLast(ch)
} else {
@ -208,11 +207,11 @@ impl<const L: usize> TextBox<L> {
}
}
pub fn paint_pending_marker(text_baseline: Point, text: &[u8], font: Font, color: Color) {
pub fn paint_pending_marker(text_baseline: Point, text: &str, font: Font, color: Color) {
// Measure the width of the last character of input.
if let Some(last) = text.last().copied() {
if let Some(last) = text.chars().last() {
let width = font.text_width(text);
let last_width = font.text_width(&[last]);
let last_width = font.char_width(last);
// Draw the marker 2px under the start of the baseline of the last character.
let marker_origin = text_baseline + Offset::new(width - last_width, 2);
// Draw the marker 1px longer than the last character, and 3px thick.

@ -19,17 +19,17 @@ pub struct MnemonicKeyboard<T, U> {
/// Initial prompt, displayed on empty input.
prompt: Child<Maybe<Label<U>>>,
/// Backspace button.
back: Child<Maybe<Button<&'static [u8]>>>,
back: Child<Maybe<Button<&'static str>>>,
/// Input area, acting as the auto-complete and confirm button.
input: Child<Maybe<T>>,
/// Key buttons.
keys: [Child<Button<&'static [u8]>>; MNEMONIC_KEY_COUNT],
keys: [Child<Button<&'static str>>; MNEMONIC_KEY_COUNT],
}
impl<T, U> MnemonicKeyboard<T, U>
where
T: MnemonicInput,
U: Deref<Target = [u8]>,
U: Deref<Target = str>,
{
pub fn new(input: T, prompt: U) -> Self {
Self {
@ -42,10 +42,7 @@ where
Button::with_icon(theme::ICON_BACK).styled(theme::button_clear()),
)),
input: Child::new(Maybe::hidden(theme::BG, input)),
keys: T::keys()
.map(str::as_bytes)
.map(Button::with_text)
.map(Child::new),
keys: T::keys().map(Button::with_text).map(Child::new),
}
}
@ -87,7 +84,7 @@ where
impl<T, U> Component for MnemonicKeyboard<T, U>
where
T: MnemonicInput,
U: Deref<Target = [u8]>,
U: Deref<Target = str>,
{
type Msg = MnemonicKeyboardMsg;

@ -250,7 +250,7 @@ impl Component for Input {
let style = theme::label_default();
let text_baseline = self.area.bottom_left() - TEXT_OFFSET;
let text = self.textbox.content().as_bytes();
let text = self.textbox.content();
// Possible optimization is to redraw the background only when pending character
// is replaced, or only draw rectangle over the pending character and

@ -1,5 +1,5 @@
use core::ops::Deref;
use heapless::Vec;
use heapless::String;
use crate::{
trezorhal::random,
@ -26,7 +26,7 @@ const MAX_LENGTH: usize = 9;
const DIGIT_COUNT: usize = 10; // 0..10
pub struct PinKeyboard<T> {
digits: Vec<u8, MAX_LENGTH>,
digits: String<MAX_LENGTH>,
allow_cancel: bool,
major_prompt: Label<T>,
minor_prompt: Label<T>,
@ -40,7 +40,7 @@ pub struct PinKeyboard<T> {
impl<T> PinKeyboard<T>
where
T: Deref<Target = [u8]>,
T: Deref<Target = str>,
{
const HEADER_HEIGHT: i32 = 25;
const HEADER_PADDING_SIDE: i32 = 5;
@ -56,7 +56,7 @@ where
major_warning: Option<T>,
allow_cancel: bool,
) -> Self {
let digits = Vec::new();
let digits = String::new();
// Control buttons.
let reset_btn = Button::with_icon(theme::ICON_BACK)
@ -97,7 +97,7 @@ where
}
fn pin_modified(&mut self, ctx: &mut EventCtx) {
let is_full = self.digits.is_full();
let is_full = self.digits.len() == self.digits.capacity();
for btn in &mut self.digit_btns {
btn.mutate(ctx, |ctx, btn| btn.enable_if(ctx, !is_full));
}
@ -118,14 +118,14 @@ where
.mutate(ctx, |ctx, dots| dots.update(ctx, digit_count));
}
pub fn pin(&self) -> &[u8] {
pub fn pin(&self) -> &str {
&self.digits
}
}
impl<T> Component for PinKeyboard<T>
where
T: Deref<Target = [u8]>,
T: Deref<Target = str>,
{
type Msg = PinKeyboardMsg;
@ -188,7 +188,7 @@ where
for btn in &mut self.digit_btns {
if let Some(Clicked) = btn.event(ctx, event) {
if let ButtonContent::Text(text) = btn.inner().content() {
if self.digits.extend_from_slice(text.as_ref()).is_err() {
if self.digits.push_str(text).is_err() {
// `self.pin` is full and wasn't able to accept all of
// `text`. Should not happen.
}
@ -309,7 +309,7 @@ impl Component for PinDots {
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for PinKeyboard<T>
where
T: Deref<Target = [u8]>,
T: Deref<Target = str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("PinKeyboard");

@ -25,7 +25,7 @@ use crate::{
const MAX_LENGTH: usize = 8;
pub struct Slip39Input {
button: Button<&'static [u8]>,
button: Button<&'static str>,
textbox: TextBox<MAX_LENGTH>,
multi_tap: MultiTapKeyboard,
final_word: Option<&'static str>,
@ -139,8 +139,9 @@ impl Component for Slip39Input {
if let (Some(key), Some(press)) =
(self.multi_tap.pending_key(), self.multi_tap.pending_press())
{
let ascii_text = Self::keys()[key].as_bytes();
let ch = ascii_text[press % ascii_text.len()] as char;
assert!(!Self::keys()[key].is_empty());
// Now we can be sure that the looped iterator will return a value.
let ch = Self::keys()[key].chars().cycle().nth(press).unwrap();
text.pop();
text.push(ch)
.assert_if_debugging_ui("Text buffer is too small");
@ -148,7 +149,7 @@ impl Component for Slip39Input {
}
display::text(
text_baseline,
text.as_bytes(),
text.as_str(),
style.font,
style.text_color,
style.button_color,
@ -156,7 +157,7 @@ impl Component for Slip39Input {
// Paint the pending marker.
if self.multi_tap.pending_key().is_some() && self.final_word.is_none() {
paint_pending_marker(text_baseline, text.as_bytes(), style.font, style.text_color);
paint_pending_marker(text_baseline, text.as_str(), style.font, style.text_color);
}
// Paint the icon.
@ -221,7 +222,7 @@ impl Slip39Input {
// Disabled button.
self.button.disable(ctx);
self.button.set_stylesheet(ctx, theme::button_default());
self.button.set_content(ctx, ButtonContent::Text(b""));
self.button.set_content(ctx, ButtonContent::Text(""));
}
}

@ -57,7 +57,7 @@ where
fn paint_hint(&mut self) {
display::text_center(
self.pad.area.bottom_center() - Offset::y(3),
b"SWIPE TO CONTINUE",
"SWIPE TO CONTINUE",
theme::FONT_BOLD, // FIXME: Figma has this as 14px but bold is 16px
theme::GREY_LIGHT,
theme::BG,

@ -2,7 +2,7 @@ use core::{convert::TryInto, ops::Deref};
use crate::{
error::Error,
micropython::{buffer::Buffer, map::Map, module::Module, obj::Obj, qstr::Qstr},
micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr},
ui::{
component::{
base::ComponentExt,
@ -30,7 +30,7 @@ use super::{
impl<T, U> ComponentMsgObj for Dialog<T, Button<U>, Button<U>>
where
T: ComponentMsgObj,
U: AsRef<[u8]>,
U: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
@ -57,7 +57,7 @@ where
impl<T> ComponentMsgObj for PinKeyboard<T>
where
T: Deref<Target = [u8]>,
T: Deref<Target = str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
@ -79,7 +79,7 @@ impl ComponentMsgObj for PassphraseKeyboard {
impl<T, U> ComponentMsgObj for MnemonicKeyboard<T, U>
where
T: MnemonicInput,
U: Deref<Target = [u8]>,
U: Deref<Target = str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
@ -97,7 +97,7 @@ where
impl<T, U> ComponentMsgObj for Frame<T, U>
where
T: ComponentMsgObj,
U: AsRef<[u8]>,
U: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
self.inner().msg_try_into_obj(msg)
@ -120,11 +120,11 @@ where
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let action: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
let description: Option<Buffer> =
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let action: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
let description: Option<StrBuffer> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let verb: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_verb)?.try_into_option()?;
let verb: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_verb)?.try_into_option()?;
let reverse: bool = kwargs.get(Qstr::MP_QSTR_reverse)?.try_into()?;
let paragraphs = {
@ -161,11 +161,11 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let subprompt: Buffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?;
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let subprompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?;
let allow_cancel: Option<bool> =
kwargs.get(Qstr::MP_QSTR_allow_cancel)?.try_into_option()?;
let warning: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_warning)?.try_into_option()?;
let warning: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_warning)?.try_into_option()?;
let obj = LayoutObj::new(
PinKeyboard::new(prompt, subprompt, warning, allow_cancel.unwrap_or(true)).into_child(),
)?;
@ -176,7 +176,7 @@ extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map)
extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let _prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let _prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let _max_len: u32 = kwargs.get(Qstr::MP_QSTR_max_len)?.try_into()?;
let obj = LayoutObj::new(PassphraseKeyboard::new().into_child())?;
Ok(obj.into())
@ -186,7 +186,7 @@ extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *m
extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let obj = LayoutObj::new(MnemonicKeyboard::new(Bip39Input::new(), prompt).into_child())?;
Ok(obj.into())
};
@ -195,7 +195,7 @@ extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Ma
extern "C" fn new_request_slip39(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let obj = LayoutObj::new(MnemonicKeyboard::new(Slip39Input::new(), prompt).into_child())?;
Ok(obj.into())
};
@ -288,9 +288,9 @@ mod tests {
FormattedText::new::<theme::TTDefaultText>(
"Testing text layout, with some text, and some more text. And {param}",
)
.with(b"param", b"parameters!"),
Button::with_text(b"Left"),
Button::with_text(b"Right"),
.with("param", "parameters!"),
Button::with_text("Left"),
Button::with_text("Right"),
);
layout.place(SCREEN);
assert_eq!(

Loading…
Cancel
Save