WIP - apply TString / allocable translations

WIP - move translations to core top-level
matejcik 4 months ago committed by grdddj
parent 00213621ec
commit 447dbb357e

@ -5,6 +5,7 @@
#pragma GCC diagnostic ignored "-Wunused-function"
static void _librust_qstrs(void) {
MP_QSTR_;
MP_QSTR_CANCELLED;
MP_QSTR_CONFIRMED;
MP_QSTR_INFO;
@ -14,6 +15,7 @@ static void _librust_qstrs(void) {
MP_QSTR_Msg;
MP_QSTR_MsgDef;
MP_QSTR_TR;
MP_QSTR_TranslationsHeader;
MP_QSTR___dict__;
MP_QSTR___name__;
MP_QSTR_account;
@ -21,7 +23,24 @@ static void _librust_qstrs(void) {
MP_QSTR_accounts;
MP_QSTR_action;
MP_QSTR_active;
MP_QSTR_addr_mismatch__contact_support_at;
MP_QSTR_addr_mismatch__key_mismatch;
MP_QSTR_addr_mismatch__mismatch;
MP_QSTR_addr_mismatch__support_url;
MP_QSTR_addr_mismatch__title;
MP_QSTR_addr_mismatch__title_key_mismatch;
MP_QSTR_addr_mismatch__wrong_derication_path;
MP_QSTR_addr_mismatch__xpub_mismatch;
MP_QSTR_address;
MP_QSTR_address__address;
MP_QSTR_address__public_key;
MP_QSTR_address__title_cosigner;
MP_QSTR_address__title_receive_address;
MP_QSTR_address__title_yours;
MP_QSTR_address_details__account;
MP_QSTR_address_details__derivation_path;
MP_QSTR_address_details__title_receive_address;
MP_QSTR_address_details__title_receiving_to;
MP_QSTR_address_label;
MP_QSTR_address_title;
MP_QSTR_allow_cancel;
@ -33,15 +52,232 @@ static void _librust_qstrs(void) {
MP_QSTR_amount_value;
MP_QSTR_app_name;
MP_QSTR_attach_timer_fn;
MP_QSTR_authenticate__confirm_template;
MP_QSTR_authenticate__header;
MP_QSTR_auto_lock__change_template;
MP_QSTR_auto_lock__title;
MP_QSTR_backup__can_back_up_anytime;
MP_QSTR_backup__it_should_be_backed_up;
MP_QSTR_backup__it_should_be_backed_up_now;
MP_QSTR_backup__new_wallet_created;
MP_QSTR_backup__new_wallet_successfully_created;
MP_QSTR_backup__recover_anytime;
MP_QSTR_backup__title_backup_wallet;
MP_QSTR_backup__title_skip;
MP_QSTR_backup__want_to_skip;
MP_QSTR_binance__buy;
MP_QSTR_binance__confirm_cancel;
MP_QSTR_binance__confirm_input;
MP_QSTR_binance__confirm_order;
MP_QSTR_binance__confirm_output;
MP_QSTR_binance__order_id;
MP_QSTR_binance__pair;
MP_QSTR_binance__price;
MP_QSTR_binance__quantity;
MP_QSTR_binance__sell;
MP_QSTR_binance__sender_address;
MP_QSTR_binance__side;
MP_QSTR_binance__unknown;
MP_QSTR_bitcoin__commitment_data;
MP_QSTR_bitcoin__confirm_locktime;
MP_QSTR_bitcoin__create_proof_of_ownership;
MP_QSTR_bitcoin__high_mining_fee_template;
MP_QSTR_bitcoin__locktime_no_effect;
MP_QSTR_bitcoin__locktime_set_to;
MP_QSTR_bitcoin__locktime_set_to_blockheight;
MP_QSTR_bitcoin__lot_of_change_outputs;
MP_QSTR_bitcoin__multiple_accounts;
MP_QSTR_bitcoin__new_fee_rate;
MP_QSTR_bitcoin__simple_send_of;
MP_QSTR_bitcoin__ticket_amount;
MP_QSTR_bitcoin__title_confirm_details;
MP_QSTR_bitcoin__title_finalize_transaction;
MP_QSTR_bitcoin__title_high_mining_fee;
MP_QSTR_bitcoin__title_meld_transaction;
MP_QSTR_bitcoin__title_modify_amount;
MP_QSTR_bitcoin__title_payjoin;
MP_QSTR_bitcoin__title_proof_of_ownership;
MP_QSTR_bitcoin__title_purchase_ticket;
MP_QSTR_bitcoin__title_update_transaction;
MP_QSTR_bitcoin__unknown_path;
MP_QSTR_bitcoin__unknown_transaction;
MP_QSTR_bitcoin__unusually_high_fee;
MP_QSTR_bitcoin__unverified_external_inputs;
MP_QSTR_bitcoin__valid_signature;
MP_QSTR_bitcoin__voting_rights;
MP_QSTR_bootscreen;
MP_QSTR_bounds;
MP_QSTR_button;
MP_QSTR_button_event;
MP_QSTR_buttons__abort;
MP_QSTR_buttons__access;
MP_QSTR_buttons__again;
MP_QSTR_buttons__allow;
MP_QSTR_buttons__back;
MP_QSTR_buttons__back_up;
MP_QSTR_buttons__cancel;
MP_QSTR_buttons__change;
MP_QSTR_buttons__check;
MP_QSTR_buttons__check_again;
MP_QSTR_buttons__close;
MP_QSTR_buttons__confirm;
MP_QSTR_buttons__continue;
MP_QSTR_buttons__details;
MP_QSTR_buttons__enable;
MP_QSTR_buttons__enter;
MP_QSTR_buttons__enter_share;
MP_QSTR_buttons__export;
MP_QSTR_buttons__format;
MP_QSTR_buttons__go_back;
MP_QSTR_buttons__hold_to_confirm;
MP_QSTR_buttons__info;
MP_QSTR_buttons__install;
MP_QSTR_buttons__more_info;
MP_QSTR_buttons__ok_i_understand;
MP_QSTR_buttons__purchase;
MP_QSTR_buttons__quit;
MP_QSTR_buttons__restart;
MP_QSTR_buttons__retry;
MP_QSTR_buttons__select;
MP_QSTR_buttons__set;
MP_QSTR_buttons__show_all;
MP_QSTR_buttons__show_details;
MP_QSTR_buttons__show_words;
MP_QSTR_buttons__skip;
MP_QSTR_buttons__try_again;
MP_QSTR_buttons__turn_off;
MP_QSTR_buttons__turn_on;
MP_QSTR_cancel_arrow;
MP_QSTR_cancel_cross;
MP_QSTR_cardano__addr_base;
MP_QSTR_cardano__addr_enterprise;
MP_QSTR_cardano__addr_legacy;
MP_QSTR_cardano__addr_pointer;
MP_QSTR_cardano__addr_reward;
MP_QSTR_cardano__address_no_staking;
MP_QSTR_cardano__amount;
MP_QSTR_cardano__amount_burned_decimals_unknown;
MP_QSTR_cardano__amount_minted_decimals_unknown;
MP_QSTR_cardano__amount_sent_decimals_unknown;
MP_QSTR_cardano__anonymous_pool;
MP_QSTR_cardano__asset_fingerprint;
MP_QSTR_cardano__auxiliary_data_hash;
MP_QSTR_cardano__block;
MP_QSTR_cardano__catalyst;
MP_QSTR_cardano__certificate;
MP_QSTR_cardano__certificate_path;
MP_QSTR_cardano__change_output;
MP_QSTR_cardano__change_output_path;
MP_QSTR_cardano__change_output_staking_path;
MP_QSTR_cardano__check_all_items;
MP_QSTR_cardano__choose_level_of_details;
MP_QSTR_cardano__collateral_input_id;
MP_QSTR_cardano__collateral_input_index;
MP_QSTR_cardano__collateral_output_contains_tokens;
MP_QSTR_cardano__collateral_return;
MP_QSTR_cardano__confirm;
MP_QSTR_cardano__confirm_signing_stake_pool;
MP_QSTR_cardano__confirm_transaction;
MP_QSTR_cardano__confirming_a_multisig_transaction;
MP_QSTR_cardano__confirming_pool_registration;
MP_QSTR_cardano__confirming_transction;
MP_QSTR_cardano__cost;
MP_QSTR_cardano__credential_mismatch;
MP_QSTR_cardano__datum_hash;
MP_QSTR_cardano__delegating_to;
MP_QSTR_cardano__for_account_and_index_template;
MP_QSTR_cardano__for_account_template;
MP_QSTR_cardano__for_key_hash;
MP_QSTR_cardano__for_script;
MP_QSTR_cardano__inline_datum;
MP_QSTR_cardano__input_id;
MP_QSTR_cardano__input_index;
MP_QSTR_cardano__intro_text_address;
MP_QSTR_cardano__intro_text_change;
MP_QSTR_cardano__intro_text_owned_by_device;
MP_QSTR_cardano__intro_text_registration_payment;
MP_QSTR_cardano__key_hash;
MP_QSTR_cardano__margin;
MP_QSTR_cardano__multisig_path;
MP_QSTR_cardano__nested_scripts_template;
MP_QSTR_cardano__network;
MP_QSTR_cardano__no_output_tx;
MP_QSTR_cardano__nonce;
MP_QSTR_cardano__other;
MP_QSTR_cardano__path;
MP_QSTR_cardano__pledge;
MP_QSTR_cardano__pointer;
MP_QSTR_cardano__policy_id;
MP_QSTR_cardano__pool_metadata_hash;
MP_QSTR_cardano__pool_metadata_url;
MP_QSTR_cardano__pool_owner;
MP_QSTR_cardano__pool_owner_path;
MP_QSTR_cardano__pool_reward_account;
MP_QSTR_cardano__reference_input_id;
MP_QSTR_cardano__reference_input_index;
MP_QSTR_cardano__reference_script;
MP_QSTR_cardano__required_signer;
MP_QSTR_cardano__reward;
MP_QSTR_cardano__reward_address;
MP_QSTR_cardano__reward_eligibility_warning;
MP_QSTR_cardano__rewards_go_to;
MP_QSTR_cardano__script;
MP_QSTR_cardano__script_all;
MP_QSTR_cardano__script_any;
MP_QSTR_cardano__script_data_hash;
MP_QSTR_cardano__script_hash;
MP_QSTR_cardano__script_invalid_before;
MP_QSTR_cardano__script_invalid_hereafter;
MP_QSTR_cardano__script_key;
MP_QSTR_cardano__script_n_of_k;
MP_QSTR_cardano__script_reward;
MP_QSTR_cardano__sending;
MP_QSTR_cardano__show_simple;
MP_QSTR_cardano__sign_tx_path_template;
MP_QSTR_cardano__stake_delegation;
MP_QSTR_cardano__stake_deregistration;
MP_QSTR_cardano__stake_pool_registration;
MP_QSTR_cardano__stake_pool_registration_pool_id;
MP_QSTR_cardano__stake_registration;
MP_QSTR_cardano__staking_key_for_account;
MP_QSTR_cardano__to_pool;
MP_QSTR_cardano__token_minting_path;
MP_QSTR_cardano__total_collateral;
MP_QSTR_cardano__transaction;
MP_QSTR_cardano__transaction_contains_minting_or_burning;
MP_QSTR_cardano__transaction_contains_script_address_no_datum;
MP_QSTR_cardano__transaction_fee;
MP_QSTR_cardano__transaction_id;
MP_QSTR_cardano__transaction_no_collateral_input;
MP_QSTR_cardano__transaction_no_script_data_hash;
MP_QSTR_cardano__transaction_output_contains_tokens;
MP_QSTR_cardano__ttl;
MP_QSTR_cardano__unknown;
MP_QSTR_cardano__unknown_collateral_amount;
MP_QSTR_cardano__unusual_path;
MP_QSTR_cardano__valid_since;
MP_QSTR_cardano__verify_script;
MP_QSTR_cardano__vote_key_registration;
MP_QSTR_cardano__vote_public_key;
MP_QSTR_cardano__voting_purpose;
MP_QSTR_cardano__warning;
MP_QSTR_cardano__weight;
MP_QSTR_cardano__withdrawal_for_address_template;
MP_QSTR_cardano__witness_path;
MP_QSTR_cardano__x_of_y_signatures_template;
MP_QSTR_case_sensitive;
MP_QSTR_change_language_prompt;
MP_QSTR_change_language_title;
MP_QSTR_check_homescreen_format;
MP_QSTR_chunkify;
MP_QSTR_coinjoin__access_account;
MP_QSTR_coinjoin__do_not_disconnect;
MP_QSTR_coinjoin__max_mining_fee;
MP_QSTR_coinjoin__max_rounds;
MP_QSTR_coinjoin__title;
MP_QSTR_coinjoin__title_do_not_disconnect;
MP_QSTR_coinjoin__title_progress;
MP_QSTR_coinjoin__waiting_for_others;
MP_QSTR_coinjoin_authorized;
MP_QSTR_confirm_action;
MP_QSTR_confirm_address;
@ -62,61 +298,515 @@ static void _librust_qstrs(void) {
MP_QSTR_confirm_recovery;
MP_QSTR_confirm_reset_device;
MP_QSTR_confirm_total;
MP_QSTR_confirm_total__account;
MP_QSTR_confirm_total__fee_rate;
MP_QSTR_confirm_total__sending_from_account;
MP_QSTR_confirm_total__title_fee;
MP_QSTR_confirm_total__title_sending_from;
MP_QSTR_confirm_value;
MP_QSTR_confirm_with_info;
MP_QSTR_count;
MP_QSTR_data;
MP_QSTR_data_length;
MP_QSTR_debug__loading_seed;
MP_QSTR_debug__loading_seed_not_recommended;
MP_QSTR_decode;
MP_QSTR_description;
MP_QSTR_details_title;
MP_QSTR_device_name__change_template;
MP_QSTR_device_name__title;
MP_QSTR_disable_animation;
MP_QSTR_dry_run;
MP_QSTR_encode;
MP_QSTR_encoded_length;
MP_QSTR_entropy__send;
MP_QSTR_entropy__title;
MP_QSTR_entropy__title_confirm;
MP_QSTR_eos__about_to_sign_template;
MP_QSTR_eos__account;
MP_QSTR_eos__action_name;
MP_QSTR_eos__amount;
MP_QSTR_eos__arbitrary_data;
MP_QSTR_eos__buy_ram;
MP_QSTR_eos__bytes;
MP_QSTR_eos__cancel_vote;
MP_QSTR_eos__checksum;
MP_QSTR_eos__code;
MP_QSTR_eos__contract;
MP_QSTR_eos__cpu;
MP_QSTR_eos__creator;
MP_QSTR_eos__delegate;
MP_QSTR_eos__delete_auth;
MP_QSTR_eos__from;
MP_QSTR_eos__link_auth;
MP_QSTR_eos__memo;
MP_QSTR_eos__name;
MP_QSTR_eos__net;
MP_QSTR_eos__new_account;
MP_QSTR_eos__no;
MP_QSTR_eos__owner;
MP_QSTR_eos__parent;
MP_QSTR_eos__payer;
MP_QSTR_eos__permission;
MP_QSTR_eos__proxy;
MP_QSTR_eos__receiver;
MP_QSTR_eos__refund;
MP_QSTR_eos__requirement;
MP_QSTR_eos__sell_ram;
MP_QSTR_eos__sender;
MP_QSTR_eos__sign_transaction;
MP_QSTR_eos__threshold;
MP_QSTR_eos__to;
MP_QSTR_eos__transfer;
MP_QSTR_eos__type;
MP_QSTR_eos__undelegate;
MP_QSTR_eos__unlink_auth;
MP_QSTR_eos__update_auth;
MP_QSTR_eos__vote_for_producers;
MP_QSTR_eos__vote_for_proxy;
MP_QSTR_eos__voter;
MP_QSTR_eos__yes;
MP_QSTR_ethereum__amount_sent;
MP_QSTR_ethereum__confirm_fee;
MP_QSTR_ethereum__contract;
MP_QSTR_ethereum__data_size_template;
MP_QSTR_ethereum__gas_limit;
MP_QSTR_ethereum__gas_price;
MP_QSTR_ethereum__max_gas_price;
MP_QSTR_ethereum__name_and_version;
MP_QSTR_ethereum__new_contract;
MP_QSTR_ethereum__no_message_field;
MP_QSTR_ethereum__priority_fee;
MP_QSTR_ethereum__show_full_array;
MP_QSTR_ethereum__show_full_domain;
MP_QSTR_ethereum__show_full_message;
MP_QSTR_ethereum__show_full_struct;
MP_QSTR_ethereum__sign_eip712;
MP_QSTR_ethereum__title_confirm_data;
MP_QSTR_ethereum__title_confirm_domain;
MP_QSTR_ethereum__title_confirm_message;
MP_QSTR_ethereum__title_confirm_struct;
MP_QSTR_ethereum__title_confirm_typed_data;
MP_QSTR_ethereum__title_signing_address;
MP_QSTR_ethereum__units_template;
MP_QSTR_ethereum__unknown_token;
MP_QSTR_ethereum__valid_signature;
MP_QSTR_experimental_mode__enable;
MP_QSTR_experimental_mode__only_for_dev;
MP_QSTR_experimental_mode__title;
MP_QSTR_extra;
MP_QSTR_fee_amount;
MP_QSTR_fee_label;
MP_QSTR_fee_rate_amount;
MP_QSTR_fee_title;
MP_QSTR_fee_value;
MP_QSTR_fido__already_registered;
MP_QSTR_fido__device_already_registered;
MP_QSTR_fido__device_already_registered_with_template;
MP_QSTR_fido__device_not_registered;
MP_QSTR_fido__does_not_belong;
MP_QSTR_fido__erase_credentials;
MP_QSTR_fido__export_credentials;
MP_QSTR_fido__not_registered;
MP_QSTR_fido__not_registered_with_template;
MP_QSTR_fido__please_enable_pin_protection;
MP_QSTR_fido__title_authenticate;
MP_QSTR_fido__title_import_credential;
MP_QSTR_fido__title_list_credentials;
MP_QSTR_fido__title_register;
MP_QSTR_fido__title_remove_credential;
MP_QSTR_fido__title_reset;
MP_QSTR_fido__title_u2f_auth;
MP_QSTR_fido__title_u2f_register;
MP_QSTR_fido__title_verify_user;
MP_QSTR_fido__unable_to_verify_user;
MP_QSTR_fido__wanna_erase_credentials;
MP_QSTR_fingerprint;
MP_QSTR_firmware_update__title;
MP_QSTR_firmware_update__title_fingerprint;
MP_QSTR_header_length;
MP_QSTR_hold;
MP_QSTR_hold_danger;
MP_QSTR_homescreen__click_to_connect;
MP_QSTR_homescreen__click_to_unlock;
MP_QSTR_homescreen__title_backup_failed;
MP_QSTR_homescreen__title_backup_needed;
MP_QSTR_homescreen__title_coinjoin_authorized;
MP_QSTR_homescreen__title_experimental_mode;
MP_QSTR_homescreen__title_hold_to_lock;
MP_QSTR_homescreen__title_no_usb_connection;
MP_QSTR_homescreen__title_pin_not_set;
MP_QSTR_homescreen__title_seedless;
MP_QSTR_homescreen__title_set;
MP_QSTR_horizontal;
MP_QSTR_icon_name;
MP_QSTR_image;
MP_QSTR_indeterminate;
MP_QSTR_info_button;
MP_QSTR_inputs__back;
MP_QSTR_inputs__cancel;
MP_QSTR_inputs__delete;
MP_QSTR_inputs__enter;
MP_QSTR_inputs__return;
MP_QSTR_inputs__show;
MP_QSTR_inputs__space;
MP_QSTR_is_type_of;
MP_QSTR_items;
MP_QSTR_joint__title;
MP_QSTR_joint__to_the_total_amount;
MP_QSTR_joint__you_are_contributing;
MP_QSTR_label;
MP_QSTR_language__change_template;
MP_QSTR_language__set_default;
MP_QSTR_language__title_change;
MP_QSTR_language_name;
MP_QSTR_lines;
MP_QSTR_load_from_flash;
MP_QSTR_lockscreen__tap_to_connect;
MP_QSTR_lockscreen__tap_to_unlock;
MP_QSTR_lockscreen__title_locked;
MP_QSTR_lockscreen__title_not_connected;
MP_QSTR_max_count;
MP_QSTR_max_feerate;
MP_QSTR_max_len;
MP_QSTR_max_rounds;
MP_QSTR_min_count;
MP_QSTR_misc__decrypt_value;
MP_QSTR_misc__encrypt_value;
MP_QSTR_misc__title_suite_labeling;
MP_QSTR_modify_amount__address;
MP_QSTR_modify_amount__decrease_amount;
MP_QSTR_modify_amount__increase_amount;
MP_QSTR_modify_amount__new_amount;
MP_QSTR_modify_amount__title;
MP_QSTR_modify_fee__decrease_fee;
MP_QSTR_modify_fee__fee_rate;
MP_QSTR_modify_fee__increase_fee;
MP_QSTR_modify_fee__new_transaction_fee;
MP_QSTR_modify_fee__no_change;
MP_QSTR_modify_fee__title;
MP_QSTR_modify_fee__transaction_fee;
MP_QSTR_monero__confirm_export;
MP_QSTR_monero__confirm_fee;
MP_QSTR_monero__confirm_ki_sync;
MP_QSTR_monero__confirm_refresh;
MP_QSTR_monero__confirm_unlock_time;
MP_QSTR_monero__hashing_inputs;
MP_QSTR_monero__payment_id;
MP_QSTR_monero__postprocessing;
MP_QSTR_monero__processing;
MP_QSTR_monero__processing_inputs;
MP_QSTR_monero__processing_outputs;
MP_QSTR_monero__signing;
MP_QSTR_monero__signing_inputs;
MP_QSTR_monero__unlock_time_set_template;
MP_QSTR_monero__wanna_export_tx_der;
MP_QSTR_monero__wanna_export_tx_key;
MP_QSTR_monero__wanna_export_watchkey;
MP_QSTR_monero__wanna_start_refresh;
MP_QSTR_monero__wanna_sync_key_images;
MP_QSTR_multiple_pages_texts;
MP_QSTR_nem__absolute;
MP_QSTR_nem__activate;
MP_QSTR_nem__add;
MP_QSTR_nem__confirm_action;
MP_QSTR_nem__confirm_address;
MP_QSTR_nem__confirm_creation_fee;
MP_QSTR_nem__confirm_fee;
MP_QSTR_nem__confirm_mosaic;
MP_QSTR_nem__confirm_multisig_fee;
MP_QSTR_nem__confirm_namespace;
MP_QSTR_nem__confirm_payload;
MP_QSTR_nem__confirm_properties;
MP_QSTR_nem__confirm_rental_fee;
MP_QSTR_nem__confirm_transfer_of;
MP_QSTR_nem__convert_account_to_multisig;
MP_QSTR_nem__cosign_transaction_for;
MP_QSTR_nem__cosignatory;
MP_QSTR_nem__create_mosaic;
MP_QSTR_nem__create_namespace;
MP_QSTR_nem__deactivate;
MP_QSTR_nem__decrease;
MP_QSTR_nem__description;
MP_QSTR_nem__divisibility_and_levy_cannot_be_shown;
MP_QSTR_nem__encrypted;
MP_QSTR_nem__final_confirm;
MP_QSTR_nem__immutable;
MP_QSTR_nem__increase;
MP_QSTR_nem__initial_supply;
MP_QSTR_nem__initiate_transaction_for;
MP_QSTR_nem__levy_divisibility;
MP_QSTR_nem__levy_fee;
MP_QSTR_nem__levy_fee_of;
MP_QSTR_nem__levy_mosaic;
MP_QSTR_nem__levy_namespace;
MP_QSTR_nem__levy_recipient;
MP_QSTR_nem__levy_type;
MP_QSTR_nem__modify_supply_for;
MP_QSTR_nem__modify_the_number_of_cosignatories_by;
MP_QSTR_nem__mutable;
MP_QSTR_nem__no;
MP_QSTR_nem__of;
MP_QSTR_nem__percentile;
MP_QSTR_nem__raw_units_template;
MP_QSTR_nem__remote_harvesting;
MP_QSTR_nem__remove;
MP_QSTR_nem__set_minimum_cosignatories_to;
MP_QSTR_nem__sign_tx_fee_template;
MP_QSTR_nem__supply_change;
MP_QSTR_nem__supply_units_template;
MP_QSTR_nem__transferable;
MP_QSTR_nem__under_namespace;
MP_QSTR_nem__unencrypted;
MP_QSTR_nem__unknown_mosaic;
MP_QSTR_nem__yes;
MP_QSTR_notification;
MP_QSTR_notification_level;
MP_QSTR_page_count;
MP_QSTR_pages;
MP_QSTR_paint;
MP_QSTR_passphrase__access_hidden_wallet;
MP_QSTR_passphrase__always_on_device;
MP_QSTR_passphrase__from_host_not_shown;
MP_QSTR_passphrase__hidden_wallet;
MP_QSTR_passphrase__hide;
MP_QSTR_passphrase__next_screen_will_show_passphrase;
MP_QSTR_passphrase__please_enter;
MP_QSTR_passphrase__revoke_on_device;
MP_QSTR_passphrase__title_confirm;
MP_QSTR_passphrase__title_enter;
MP_QSTR_passphrase__title_hide;
MP_QSTR_passphrase__title_settings;
MP_QSTR_passphrase__title_source;
MP_QSTR_passphrase__turn_off;
MP_QSTR_passphrase__turn_on;
MP_QSTR_path;
MP_QSTR_pin__change;
MP_QSTR_pin__changed;
MP_QSTR_pin__cursor_will_change;
MP_QSTR_pin__diff_from_wipe_code;
MP_QSTR_pin__disabled;
MP_QSTR_pin__enabled;
MP_QSTR_pin__enter;
MP_QSTR_pin__enter_new;
MP_QSTR_pin__entered_not_valid;
MP_QSTR_pin__info;
MP_QSTR_pin__invalid_pin;
MP_QSTR_pin__last_attempt;
MP_QSTR_pin__mismatch;
MP_QSTR_pin__pin_mismatch;
MP_QSTR_pin__please_check_again;
MP_QSTR_pin__reenter_new;
MP_QSTR_pin__reenter_to_confirm;
MP_QSTR_pin__should_be_long;
MP_QSTR_pin__title_check_pin;
MP_QSTR_pin__title_settings;
MP_QSTR_pin__title_wrong_pin;
MP_QSTR_pin__tries_left;
MP_QSTR_pin__turn_off;
MP_QSTR_pin__turn_on;
MP_QSTR_pin__wrong_pin;
MP_QSTR_plurals__contains_x_keys;
MP_QSTR_plurals__lock_after_x_hours;
MP_QSTR_plurals__lock_after_x_milliseconds;
MP_QSTR_plurals__lock_after_x_minutes;
MP_QSTR_plurals__lock_after_x_seconds;
MP_QSTR_plurals__sign_x_actions;
MP_QSTR_plurals__transaction_of_x_operations;
MP_QSTR_plurals__x_groups_needed;
MP_QSTR_plurals__x_shares_needed;
MP_QSTR_progress__authenticity_check;
MP_QSTR_progress__done;
MP_QSTR_progress__loading_transaction;
MP_QSTR_progress__one_second_left;
MP_QSTR_progress__please_wait;
MP_QSTR_progress__processing;
MP_QSTR_progress__refreshing;
MP_QSTR_progress__signing_transaction;
MP_QSTR_progress__syncing;
MP_QSTR_progress__x_seconds_left_template;
MP_QSTR_progress_event;
MP_QSTR_prompt;
MP_QSTR_qr_title;
MP_QSTR_reboot_to_bootloader__restart;
MP_QSTR_reboot_to_bootloader__title;
MP_QSTR_reboot_to_bootloader__version_by_template;
MP_QSTR_recovery__cancel_dry_run;
MP_QSTR_recovery__check_dry_run;
MP_QSTR_recovery__cursor_will_change;
MP_QSTR_recovery__dry_run_bip39_valid_match;
MP_QSTR_recovery__dry_run_bip39_valid_mismatch;
MP_QSTR_recovery__dry_run_slip39_valid_match;
MP_QSTR_recovery__dry_run_slip39_valid_mismatch;
MP_QSTR_recovery__enter_any_share;
MP_QSTR_recovery__enter_backup;
MP_QSTR_recovery__enter_different_share;
MP_QSTR_recovery__enter_share_from_diff_group;
MP_QSTR_recovery__group_num_template;
MP_QSTR_recovery__group_threshold_reached;
MP_QSTR_recovery__invalid_seed_entered;
MP_QSTR_recovery__invalid_share_entered;
MP_QSTR_recovery__more_shares_needed;
MP_QSTR_recovery__num_of_words;
MP_QSTR_recovery__only_first_n_letters;
MP_QSTR_recovery__progress_will_be_lost;
MP_QSTR_recovery__select_num_of_words;
MP_QSTR_recovery__share_already_entered;
MP_QSTR_recovery__share_from_another_shamir;
MP_QSTR_recovery__share_num_template;
MP_QSTR_recovery__title;
MP_QSTR_recovery__title_cancel_dry_run;
MP_QSTR_recovery__title_cancel_recovery;
MP_QSTR_recovery__title_dry_run;
MP_QSTR_recovery__title_recover;
MP_QSTR_recovery__title_remaining_shares;
MP_QSTR_recovery__type_word_x_of_y_template;
MP_QSTR_recovery__wallet_recovered;
MP_QSTR_recovery__wanna_cancel_dry_run;
MP_QSTR_recovery__wanna_cancel_recovery;
MP_QSTR_recovery__word_count_template;
MP_QSTR_recovery__word_x_of_y_template;
MP_QSTR_recovery__x_more_items_starting_template_plural;
MP_QSTR_recovery__x_more_shares_needed_template_plural;
MP_QSTR_recovery__x_of_y_entered_template;
MP_QSTR_recovery__you_have_entered;
MP_QSTR_request_bip39;
MP_QSTR_request_complete_repaint;
MP_QSTR_request_number;
MP_QSTR_request_passphrase;
MP_QSTR_request_pin;
MP_QSTR_request_slip39;
MP_QSTR_reset__advanced_group_threshold_info;
MP_QSTR_reset__all_x_of_y_template;
MP_QSTR_reset__any_x_of_y_template;
MP_QSTR_reset__button_create;
MP_QSTR_reset__button_recover;
MP_QSTR_reset__by_continuing;
MP_QSTR_reset__check_backup_title;
MP_QSTR_reset__check_group_share_title_template;
MP_QSTR_reset__check_seed_title;
MP_QSTR_reset__check_share_title_template;
MP_QSTR_reset__continue_with_next_share;
MP_QSTR_reset__continue_with_share_template;
MP_QSTR_reset__finished_verifying_group_template;
MP_QSTR_reset__finished_verifying_seed;
MP_QSTR_reset__finished_verifying_shares;
MP_QSTR_reset__group_description;
MP_QSTR_reset__group_info;
MP_QSTR_reset__group_share_checked_successfully_template;
MP_QSTR_reset__group_share_title_template;
MP_QSTR_reset__more_info_at;
MP_QSTR_reset__need_all_share_template;
MP_QSTR_reset__need_any_share_template;
MP_QSTR_reset__needed_to_form_a_group;
MP_QSTR_reset__needed_to_recover_your_wallet;
MP_QSTR_reset__never_make_digital_copy;
MP_QSTR_reset__num_of_share_holders_template;
MP_QSTR_reset__num_of_shares_advanced_info_template;
MP_QSTR_reset__num_of_shares_basic_info;
MP_QSTR_reset__num_shares_for_group_template;
MP_QSTR_reset__number_of_shares_info;
MP_QSTR_reset__one_share;
MP_QSTR_reset__only_one_share_will_be_created;
MP_QSTR_reset__recovery_seed_title;
MP_QSTR_reset__recovery_share_title_template;
MP_QSTR_reset__required_number_of_groups;
MP_QSTR_reset__select_correct_word;
MP_QSTR_reset__select_word_template;
MP_QSTR_reset__select_word_x_of_y_template;
MP_QSTR_reset__set_it_to_count_template;
MP_QSTR_reset__share_checked_successfully_template;
MP_QSTR_reset__share_words_title;
MP_QSTR_reset__slip39_checklist_num_groups;
MP_QSTR_reset__slip39_checklist_num_shares;
MP_QSTR_reset__slip39_checklist_set_num_groups;
MP_QSTR_reset__slip39_checklist_set_num_shares;
MP_QSTR_reset__slip39_checklist_set_sizes;
MP_QSTR_reset__slip39_checklist_set_sizes_longer;
MP_QSTR_reset__slip39_checklist_set_threshold;
MP_QSTR_reset__slip39_checklist_title;
MP_QSTR_reset__slip39_checklist_write_down;
MP_QSTR_reset__slip39_checklist_write_down_recovery;
MP_QSTR_reset__the_threshold_sets_the_number_of_shares;
MP_QSTR_reset__threshold_info;
MP_QSTR_reset__title_backup_is_done;
MP_QSTR_reset__title_create_wallet;
MP_QSTR_reset__title_create_wallet_shamir;
MP_QSTR_reset__title_group_threshold;
MP_QSTR_reset__title_number_of_groups;
MP_QSTR_reset__title_number_of_shares;
MP_QSTR_reset__title_set_group_threshold;
MP_QSTR_reset__title_set_number_of_groups;
MP_QSTR_reset__title_set_number_of_shares;
MP_QSTR_reset__title_set_threshold;
MP_QSTR_reset__to_form_group_template;
MP_QSTR_reset__tos_link;
MP_QSTR_reset__total_number_of_shares_in_group_template;
MP_QSTR_reset__use_your_backup;
MP_QSTR_reset__write_down_words_template;
MP_QSTR_reset__wrong_word_selected;
MP_QSTR_reset__you_need_one_share;
MP_QSTR_reset__your_backup_is_done;
MP_QSTR_reverse;
MP_QSTR_ripple__confirm_tag;
MP_QSTR_ripple__destination_tag_template;
MP_QSTR_rotation__change_template;
MP_QSTR_rotation__east;
MP_QSTR_rotation__north;
MP_QSTR_rotation__south;
MP_QSTR_rotation__title_change;
MP_QSTR_rotation__west;
MP_QSTR_safety_checks__approve_unsafe_always;
MP_QSTR_safety_checks__approve_unsafe_temporary;
MP_QSTR_safety_checks__enforce_strict;
MP_QSTR_safety_checks__title;
MP_QSTR_safety_checks__title_safety_override;
MP_QSTR_sd_card__all_data_will_be_lost;
MP_QSTR_sd_card__card_required;
MP_QSTR_sd_card__disable;
MP_QSTR_sd_card__disabled;
MP_QSTR_sd_card__enable;
MP_QSTR_sd_card__enabled;
MP_QSTR_sd_card__error;
MP_QSTR_sd_card__format_card;
MP_QSTR_sd_card__insert_correct_card;
MP_QSTR_sd_card__please_insert;
MP_QSTR_sd_card__please_unplug_and_insert;
MP_QSTR_sd_card__problem_accessing;
MP_QSTR_sd_card__refresh;
MP_QSTR_sd_card__refreshed;
MP_QSTR_sd_card__restart;
MP_QSTR_sd_card__title;
MP_QSTR_sd_card__title_problem;
MP_QSTR_sd_card__unknown_filesystem;
MP_QSTR_sd_card__unplug_and_insert_correct;
MP_QSTR_sd_card__use_different_card;
MP_QSTR_sd_card__wanna_format;
MP_QSTR_sd_card__wrong_sd_card;
MP_QSTR_select_word;
MP_QSTR_select_word_count;
MP_QSTR_send__address_path;
MP_QSTR_send__amount;
MP_QSTR_send__confirm_sending;
MP_QSTR_send__from_multiple_accounts;
MP_QSTR_send__including_fee;
MP_QSTR_send__maximum_fee;
MP_QSTR_send__receiving_to_multisig;
MP_QSTR_send__title_amount;
MP_QSTR_send__title_confirm_sending;
MP_QSTR_send__title_joint_transaction;
MP_QSTR_send__title_receiving_to;
MP_QSTR_send__title_recipient;
MP_QSTR_send__title_sending;
MP_QSTR_send__title_sending_amount;
MP_QSTR_send__title_sending_to;
MP_QSTR_send__to_the_total_amount;
MP_QSTR_send__total_amount;
MP_QSTR_send__transaction_id;
MP_QSTR_send__you_are_contributing;
MP_QSTR_share_words;
MP_QSTR_share_words__words_in_order;
MP_QSTR_share_words__wrote_down_all;
MP_QSTR_show_address_details;
MP_QSTR_show_checklist;
MP_QSTR_show_error;
@ -135,10 +825,112 @@ static void _librust_qstrs(void) {
MP_QSTR_show_success;
MP_QSTR_show_warning;
MP_QSTR_sign;
MP_QSTR_sign_message__bytes_template;
MP_QSTR_sign_message__confirm_address;
MP_QSTR_sign_message__confirm_message;
MP_QSTR_sign_message__message_size;
MP_QSTR_sign_message__verify_address;
MP_QSTR_skip_first_paint;
MP_QSTR_solana__account_index;
MP_QSTR_solana__associated_token_account;
MP_QSTR_solana__confirm_multisig;
MP_QSTR_solana__expected_fee;
MP_QSTR_solana__instruction_accounts_template;
MP_QSTR_solana__instruction_data;
MP_QSTR_solana__instruction_is_multisig;
MP_QSTR_solana__is_provided_via_lookup_table_template;
MP_QSTR_solana__lookup_table_address;
MP_QSTR_solana__multiple_signers;
MP_QSTR_solana__token_address;
MP_QSTR_solana__transaction_contains_unknown_instructions;
MP_QSTR_solana__transaction_requires_x_signers_template;
MP_QSTR_spending_amount;
MP_QSTR_stellar__account;
MP_QSTR_stellar__account_merge;
MP_QSTR_stellar__account_thresholds;
MP_QSTR_stellar__add_signer;
MP_QSTR_stellar__add_trust;
MP_QSTR_stellar__all_will_be_sent_to;
MP_QSTR_stellar__allow_trust;
MP_QSTR_stellar__asset;
MP_QSTR_stellar__balance_id;
MP_QSTR_stellar__bump_sequence;
MP_QSTR_stellar__buying;
MP_QSTR_stellar__claim_claimable_balance;
MP_QSTR_stellar__clear_data;
MP_QSTR_stellar__clear_flags;
MP_QSTR_stellar__confirm_issuer;
MP_QSTR_stellar__confirm_memo;
MP_QSTR_stellar__confirm_network;
MP_QSTR_stellar__confirm_operation;
MP_QSTR_stellar__confirm_stellar;
MP_QSTR_stellar__confirm_timebounds;
MP_QSTR_stellar__create_account;
MP_QSTR_stellar__debited_amount;
MP_QSTR_stellar__delete;
MP_QSTR_stellar__delete_passive_offer;
MP_QSTR_stellar__delete_trust;
MP_QSTR_stellar__destination;
MP_QSTR_stellar__exchanges_require_memo;
MP_QSTR_stellar__final_confirm;
MP_QSTR_stellar__hash;
MP_QSTR_stellar__high;
MP_QSTR_stellar__home_domain;
MP_QSTR_stellar__inflation;
MP_QSTR_stellar__initial_balance;
MP_QSTR_stellar__initialize_signing_with;
MP_QSTR_stellar__issuer_template;
MP_QSTR_stellar__key;
MP_QSTR_stellar__limit;
MP_QSTR_stellar__low;
MP_QSTR_stellar__master_weight;
MP_QSTR_stellar__medium;
MP_QSTR_stellar__new_offer;
MP_QSTR_stellar__new_passive_offer;
MP_QSTR_stellar__no_memo_set;
MP_QSTR_stellar__no_restriction;
MP_QSTR_stellar__on_network_template;
MP_QSTR_stellar__path_pay;
MP_QSTR_stellar__path_pay_at_least;
MP_QSTR_stellar__pay;
MP_QSTR_stellar__pay_at_most;
MP_QSTR_stellar__preauth_transaction;
MP_QSTR_stellar__price_per_template;
MP_QSTR_stellar__private_network;
MP_QSTR_stellar__remove_signer;
MP_QSTR_stellar__revoke_trust;
MP_QSTR_stellar__selling;
MP_QSTR_stellar__set_data;
MP_QSTR_stellar__set_flags;
MP_QSTR_stellar__set_sequence_to_template;
MP_QSTR_stellar__sign_tx_count_template;
MP_QSTR_stellar__sign_tx_fee_template;
MP_QSTR_stellar__source_account;
MP_QSTR_stellar__testnet_network;
MP_QSTR_stellar__trusted_account;
MP_QSTR_stellar__update;
MP_QSTR_stellar__valid_from;
MP_QSTR_stellar__valid_to;
MP_QSTR_stellar__value_sha256;
MP_QSTR_stellar__wanna_clean_value_key_template;
MP_QSTR_stellar__your_account;
MP_QSTR_subprompt;
MP_QSTR_subtitle;
MP_QSTR_tezos__address;
MP_QSTR_tezos__amount;
MP_QSTR_tezos__baker_address;
MP_QSTR_tezos__balance;
MP_QSTR_tezos__ballot;
MP_QSTR_tezos__confirm_delegation;
MP_QSTR_tezos__confirm_origination;
MP_QSTR_tezos__delegator;
MP_QSTR_tezos__fee;
MP_QSTR_tezos__proposal;
MP_QSTR_tezos__register_delegate;
MP_QSTR_tezos__remove_delegation;
MP_QSTR_tezos__submit_ballot;
MP_QSTR_tezos__submit_proposal;
MP_QSTR_tezos__submit_proposals;
MP_QSTR_time_ms;
MP_QSTR_timer;
MP_QSTR_title;
@ -150,15 +942,91 @@ static void _librust_qstrs(void) {
MP_QSTR_trezorproto;
MP_QSTR_trezorui2;
MP_QSTR_tutorial;
MP_QSTR_tutorial__middle_click;
MP_QSTR_tutorial__press_and_hold;
MP_QSTR_tutorial__ready_to_use;
MP_QSTR_tutorial__scroll_down;
MP_QSTR_tutorial__sure_you_want_skip;
MP_QSTR_tutorial__title_hello;
MP_QSTR_tutorial__title_screen_scroll;
MP_QSTR_tutorial__title_skip;
MP_QSTR_tutorial__title_tutorial_complete;
MP_QSTR_tutorial__use_trezor;
MP_QSTR_tutorial__welcome_press_right;
MP_QSTR_type_for_name;
MP_QSTR_type_for_wire;
MP_QSTR_u2f__get;
MP_QSTR_u2f__set_template;
MP_QSTR_u2f__title_get;
MP_QSTR_u2f__title_set;
MP_QSTR_usb_event;
MP_QSTR_user_fee_change;
MP_QSTR_value;
MP_QSTR_verb;
MP_QSTR_verb_cancel;
MP_QSTR_version;
MP_QSTR_warning;
MP_QSTR_wipe__info;
MP_QSTR_wipe__title;
MP_QSTR_wipe__want_to_wipe;
MP_QSTR_wipe_code__change;
MP_QSTR_wipe_code__changed;
MP_QSTR_wipe_code__diff_from_pin;
MP_QSTR_wipe_code__disabled;
MP_QSTR_wipe_code__enabled;
MP_QSTR_wipe_code__enter_new;
MP_QSTR_wipe_code__info;
MP_QSTR_wipe_code__invalid;
MP_QSTR_wipe_code__mismatch;
MP_QSTR_wipe_code__reenter;
MP_QSTR_wipe_code__reenter_to_confirm;
MP_QSTR_wipe_code__title_check;
MP_QSTR_wipe_code__title_invalid;
MP_QSTR_wipe_code__title_settings;
MP_QSTR_wipe_code__turn_off;
MP_QSTR_wipe_code__turn_on;
MP_QSTR_wipe_code__wipe_code_mismatch;
MP_QSTR_word_count__title;
MP_QSTR_words;
MP_QSTR_words__account;
MP_QSTR_words__address;
MP_QSTR_words__amount;
MP_QSTR_words__are_you_sure;
MP_QSTR_words__array_of;
MP_QSTR_words__blockhash;
MP_QSTR_words__buying;
MP_QSTR_words__confirm;
MP_QSTR_words__confirm_fee;
MP_QSTR_words__contains;
MP_QSTR_words__continue_anyway;
MP_QSTR_words__continue_with;
MP_QSTR_words__error;
MP_QSTR_words__fee;
MP_QSTR_words__from;
MP_QSTR_words__keep_it_safe;
MP_QSTR_words__know_what_your_doing;
MP_QSTR_words__my_trezor;
MP_QSTR_words__no;
MP_QSTR_words__outputs;
MP_QSTR_words__please_check_again;
MP_QSTR_words__please_try_again;
MP_QSTR_words__really_wanna;
MP_QSTR_words__recipient;
MP_QSTR_words__sign;
MP_QSTR_words__signer;
MP_QSTR_words__title_check;
MP_QSTR_words__title_group;
MP_QSTR_words__title_information;
MP_QSTR_words__title_remember;
MP_QSTR_words__title_share;
MP_QSTR_words__title_shares;
MP_QSTR_words__title_success;
MP_QSTR_words__title_summary;
MP_QSTR_words__title_threshold;
MP_QSTR_words__unknown;
MP_QSTR_words__warning;
MP_QSTR_words__writable;
MP_QSTR_words__yes;
MP_QSTR_wrong_pin;
MP_QSTR_xpubs;
}

@ -1,9 +1,9 @@
use crate::strutil::format_i64;
use crate::strutil::{format_i64, TString};
pub trait Tracer {
fn child(&mut self, key: &str, value: &dyn Trace);
fn int(&mut self, key: &str, i: i64);
fn string(&mut self, key: &str, s: &str);
fn string(&mut self, key: &str, s: TString<'_>);
fn bool(&mut self, key: &str, b: bool);
fn null(&mut self, key: &str);
@ -11,14 +11,14 @@ pub trait Tracer {
fn in_list(&mut self, key: &str, block: &dyn Fn(&mut dyn ListTracer));
fn component(&mut self, name: &str) {
self.string("component", name);
self.string("component", name.into());
}
}
pub trait ListTracer {
fn child(&mut self, value: &dyn Trace);
fn int(&mut self, i: i64);
fn string(&mut self, s: &str);
fn string(&mut self, s: &TString<'_>);
fn bool(&mut self, b: bool);
fn in_child(&mut self, block: &dyn Fn(&mut dyn Tracer));
@ -121,9 +121,9 @@ impl<F: FnMut(&str)> ListTracer for JsonTracer<F> {
self.write_int(i);
}
fn string(&mut self, s: &str) {
fn string(&mut self, s: &TString<'_>) {
self.maybe_comma();
self.write_str_quoted(s);
s.map(|s| self.write_str_quoted(s));
}
fn bool(&mut self, b: bool) {
@ -160,9 +160,9 @@ impl<F: FnMut(&str)> Tracer for JsonTracer<F> {
self.write_int(i);
}
fn string(&mut self, key: &str, s: &str) {
fn string(&mut self, key: &str, s: TString<'_>) {
self.key(key);
self.write_str_quoted(s);
s.map(|s| self.write_str_quoted(s));
}
fn bool(&mut self, key: &str, b: bool) {

@ -1,7 +1,6 @@
//! generated from enum.out.mako
//! generated from translated_string.rs.mako
//! (by running `make templates` in `core`)
//! do not edit manually!
#[cfg(feature = "micropython")]
use crate::micropython::qstr::Qstr;

@ -1,20 +1,17 @@
//! generated from enum.out.mako
//! generated from ${THIS_FILE.name}
//! (by running `make templates` in `core`)
//! do not edit manually!
<%
import json
from pathlib import Path
THIS = Path(local.filename).resolve()
SRCDIR = THIS.parent.parent
TR_DIR = ROOT / "core" / "translations"
order_file = SRCDIR / "order.json"
order_file = TR_DIR / "order.json"
order_index_name = json.loads(order_file.read_text())
order = {int(k): v for k, v in order_index_name.items()}
en_file = SRCDIR / "en.json"
en_file = TR_DIR / "en.json"
en_data = json.loads(en_file.read_text())["translations"]
def get_en_strings(data: dict) -> dict[str, str]:

@ -15,11 +15,17 @@ use crate::{
use super::translated_string::TranslatedString;
pub fn tr(item: TranslatedString) -> StrBuffer {
// SAFETY: The translated string is copied into a new memory. Reference to flash
// data is discarded at the end of this function.
let translated = item.translate(unsafe { super::flash::get() });
StrBuffer::alloc(translated).unwrap_or_else(|_| item.untranslated().into())
impl TryFrom<TranslatedString> for StrBuffer {
type Error = Error;
fn try_from(value: TranslatedString) -> Result<Self, Self::Error> {
// SAFETY: The translated string is copied into a new memory. Reference to flash
// data is discarded at the end of this function.
let translated = value.translate(unsafe { super::flash::get() });
StrBuffer::alloc(translated)
// TODO fall back to English (which is static and can be converted infallibly)
// if the allocation fails?
}
}
#[repr(C)]

@ -4,8 +4,6 @@ mod generated;
mod micropython;
mod translated_string;
#[cfg(feature = "micropython")]
pub use micropython::tr;
pub use translated_string::TranslatedString as TR;
use crate::{error::Error, io::InputStream};
@ -159,7 +157,7 @@ struct TranslationsHeader<'a> {
}
impl<'a> TranslationsHeader<'a> {
const MAGIC: [u8; 4] = [84, 82, 84, 82]; // b"TRTR"
const MAGIC: &'static [u8] = b"TRTR00";
const VERSION_LEN: usize = 16;
const LANG_LEN: usize = 32;
const DATA_HASH_LEN: usize = 32;
@ -182,7 +180,7 @@ impl<'a> TranslationsHeader<'a> {
let mut reader = crate::io::InputStream::new(data);
let magic = reader.read(4)?;
let magic = reader.read(Self::MAGIC.len())?;
if magic != Self::MAGIC {
return Err(value_error!("Invalid header magic"));
}

@ -133,6 +133,6 @@ where
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Label");
t.string("text", self.text.as_ref());
t.string("text", self.text.as_ref().into());
}
}

@ -234,6 +234,6 @@ where
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Marquee");
t.string("text", self.text.as_ref());
t.string("text", self.text.as_ref().into());
}
}

@ -151,6 +151,6 @@ impl Component for Qr {
impl crate::trace::Trace for Qr {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Qr");
t.string("text", self.text.as_ref());
t.string("text", self.text.as_str().into());
}
}

@ -110,6 +110,6 @@ impl<const L: usize> TextBox<L> {
impl<const L: usize> crate::trace::Trace for TextBox<L> {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("TextBox");
t.string("text", &self.text);
t.string("text", self.text.as_str().into());
}
}

@ -541,23 +541,23 @@ pub mod trace {
impl LayoutSink for TraceSink<'_> {
fn text(&mut self, _cursor: Point, _layout: &TextLayout, text: &str) {
self.0.string(text);
self.0.string(&text.into());
}
fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {
self.0.string("-");
self.0.string(&"-".into());
}
fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {
self.0.string(ELLIPSIS);
self.0.string(&ELLIPSIS.into());
}
fn prev_page_ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {
self.0.string(ELLIPSIS);
self.0.string(&ELLIPSIS.into());
}
fn line_break(&mut self, _cursor: Point) {
self.0.string("\n");
self.0.string(&"\n".into());
}
}
}

@ -222,7 +222,7 @@ pub mod trace {
impl<T: ParagraphSource> crate::trace::Trace for Paragraphs<T> {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.string("component", "Paragraphs");
t.string("component", "Paragraphs".into());
t.in_list("paragraphs", &|par_list| {
Self::foreach_visible(
&self.source,

@ -1,6 +1,9 @@
use crate::ui::{
display::{Color, Font},
geometry::{Alignment, Rect},
use crate::{
strutil::TString,
ui::{
display::{Color, Font},
geometry::{Alignment, Rect},
},
};
use super::{
@ -17,7 +20,7 @@ use super::{
/// If it does not fit, returns `None`.
pub fn text_multiline(
area: Rect,
text: &str,
text: TString<'_>,
font: Font,
fg_color: Color,
bg_color: Color,
@ -27,7 +30,7 @@ pub fn text_multiline(
let text_layout = TextLayout::new(text_style)
.with_bounds(area)
.with_align(alignment);
let layout_fit = text_layout.render_text(text);
let layout_fit = text.map(|t| text_layout.render_text(t));
match layout_fit {
LayoutFit::Fitting { height, .. } => Some(area.split_top(height).1),
LayoutFit::OutOfBounds { .. } => None,
@ -38,7 +41,7 @@ pub fn text_multiline(
/// area.
pub fn text_multiline_bottom(
area: Rect,
text: &str,
text: TString<'_>,
font: Font,
fg_color: Color,
bg_color: Color,
@ -50,16 +53,16 @@ pub fn text_multiline_bottom(
.with_align(alignment);
// When text fits the area, displaying it in the bottom part.
// When not, render it "normally".
match text_layout.fit_text(text) {
text.map(|t| match text_layout.fit_text(t) {
LayoutFit::Fitting { height, .. } => {
let (top, bottom) = area.split_bottom(height);
text_layout = text_layout.with_bounds(bottom);
text_layout.render_text(text);
text_layout.render_text(t);
Some(top)
}
LayoutFit::OutOfBounds { .. } => {
text_layout.render_text(text);
text_layout.render_text(t);
None
}
}
})
}

@ -27,6 +27,7 @@ use crate::ui::geometry::Alignment2D;
use crate::{time::Duration, trezorhal::time};
use crate::{
strutil::TString,
trezorhal::{buffers, display, uzlib::UzlibContext},
ui::lerp::Lerp,
};
@ -346,39 +347,39 @@ pub fn clear() {
display::clear();
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct TextOverlay<T> {
#[derive(Clone)]
pub struct TextOverlay {
area: Rect,
text: T,
text: TString<'static>,
font: Font,
max_height: i16,
baseline: i16,
}
impl<T: AsRef<str>> TextOverlay<T> {
pub fn new(text: T, font: Font) -> Self {
impl TextOverlay {
pub fn new<T: Into<TString<'static>>>(text: T, font: Font) -> Self {
let area = Rect::zero();
Self {
area,
text,
text: text.into(),
font,
max_height: font.max_height(),
baseline: font.text_baseline(),
}
}
pub fn set_text(&mut self, text: T) {
self.text = text;
pub fn set_text<T: Into<TString<'static>>>(&mut self, text: T) {
self.text = text.into();
}
pub fn get_text(&self) -> &T {
&self.text
pub fn get_text(&self) -> TString<'static> {
self.text
}
// baseline relative to the underlying render area
pub fn place(&mut self, baseline: Point) {
let text_width = self.font.text_width(self.text.as_ref());
let text_width = self.text.map(|t| self.font.text_width(t));
let text_height = self.font.text_height();
let text_area_start = baseline + Offset::new(-(text_width / 2), -text_height);
@ -397,30 +398,28 @@ impl<T: AsRef<str>> TextOverlay<T> {
let p_rel = Point::new(p.x - self.area.x0, p.y - self.area.y0);
for g in self
.text
.as_ref()
.chars()
.map(|c| self.font.get_glyph(c))
{
let top = self.max_height - self.baseline - g.bearing_y;
let char_area = Rect::new(
Point::new(tot_adv + g.bearing_x, top),
Point::new(tot_adv + g.bearing_x + g.width, top + g.height),
);
let color = self.text.map(|t| {
for g in t.chars().map(|c| self.font.get_glyph(c)) {
let top = self.max_height - self.baseline - g.bearing_y;
let char_area = Rect::new(
Point::new(tot_adv + g.bearing_x, top),
Point::new(tot_adv + g.bearing_x + g.width, top + g.height),
);
tot_adv += g.adv;
tot_adv += g.adv;
if !char_area.contains(p_rel) {
continue;
}
if !char_area.contains(p_rel) {
continue;
}
let p_inner = p_rel - char_area.top_left();
let overlay_data = g.get_pixel_data(p_inner);
return Color::lerp(underlying, fg, overlay_data as f32 / 15_f32);
}
let p_inner = p_rel - char_area.top_left();
let overlay_data = g.get_pixel_data(p_inner);
return Some(Color::lerp(underlying, fg, overlay_data as f32 / 15_f32));
}
None
});
underlying
color.unwrap_or(underlying)
}
}
@ -995,9 +994,9 @@ fn rect_rounded2_get_pixel(
/// Optionally draws a text inside the rectangle and adjusts its color to match
/// the fill. The coordinates of the text are specified in the TextOverlay
/// struct.
pub fn bar_with_text_and_fill<T: AsRef<str>>(
pub fn bar_with_text_and_fill(
area: Rect,
overlay: Option<&TextOverlay<T>>,
overlay: Option<&TextOverlay>,
fg_color: Color,
bg_color: Color,
fill_from: i16,

@ -32,7 +32,7 @@ impl ReturnToC for IntroMsg {
pub struct Intro<'a> {
bg: Pad,
title: Child<Label<&'a str>>,
buttons: Child<ButtonController<&'static str>>,
buttons: Child<ButtonController>,
text: Child<Label<&'a str>>,
warn: Option<Child<Label<&'a str>>>,
}
@ -43,8 +43,8 @@ impl<'a> Intro<'a> {
bg: Pad::with_background(BLD_BG).with_clear(),
title: Child::new(Label::centered(title, TEXT_NORMAL).vertically_centered()),
buttons: Child::new(ButtonController::new(ButtonLayout::text_none_text(
LEFT_BUTTON_TEXT,
RIGHT_BUTTON_TEXT,
LEFT_BUTTON_TEXT.into(),
RIGHT_BUTTON_TEXT.into(),
))),
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
warn: (!fw_ok).then_some(Child::new(

@ -51,7 +51,7 @@ impl MenuChoice {
}
}
impl Choice<&'static str> for MenuChoice {
impl Choice for MenuChoice {
fn paint_center(&self, _area: Rect, _inverse: bool) {
// Icon on top and two lines of text below
self.icon.draw(
@ -71,8 +71,8 @@ impl Choice<&'static str> for MenuChoice {
);
}
fn btn_layout(&self) -> ButtonLayout<&'static str> {
ButtonLayout::arrow_armed_arrow("SELECT")
fn btn_layout(&self) -> ButtonLayout {
ButtonLayout::arrow_armed_arrow("SELECT".into())
}
}
@ -99,7 +99,7 @@ impl MenuChoiceFactory {
}
}
impl ChoiceFactory<&'static str> for MenuChoiceFactory {
impl ChoiceFactory for MenuChoiceFactory {
type Action = MenuMsg;
type Item = MenuChoice;
@ -129,7 +129,7 @@ impl ChoiceFactory<&'static str> for MenuChoiceFactory {
pub struct Menu {
pad: Pad,
choice_page: Child<ChoicePage<MenuChoiceFactory, &'static str, MenuMsg>>,
choice_page: Child<ChoicePage<MenuChoiceFactory, MenuMsg>>,
}
impl Menu {

@ -2,14 +2,14 @@ use heapless::Vec;
use crate::{
error::Error,
strutil::StringType,
micropython::buffer::StrBuffer,
translations::TR,
ui::{
component::{
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt},
Child, Component, Event, EventCtx, Pad, Paginate, Qr,
},
geometry::Rect,
translations::tr,
},
};
@ -20,30 +20,24 @@ use super::{
const MAX_XPUBS: usize = 16;
const QR_BORDER: i16 = 3;
pub struct AddressDetails<T>
where
T: StringType,
{
pub struct AddressDetails {
qr_code: Qr,
details_view: Paragraphs<ParagraphVecShort<T>>,
xpub_view: Frame<Paragraphs<Paragraph<T>>, T>,
xpubs: Vec<(T, T), MAX_XPUBS>,
details_view: Paragraphs<ParagraphVecShort<StrBuffer>>,
xpub_view: Frame<Paragraphs<Paragraph<StrBuffer>>, StrBuffer>,
xpubs: Vec<(StrBuffer, StrBuffer), MAX_XPUBS>,
current_page: usize,
current_subpage: usize,
area: Rect,
pad: Pad,
buttons: Child<ButtonController<T>>,
buttons: Child<ButtonController>,
}
impl<T> AddressDetails<T>
where
T: StringType + Clone,
{
impl AddressDetails {
pub fn new(
qr_address: T,
qr_address: StrBuffer,
case_sensitive: bool,
account: Option<T>,
path: Option<T>,
account: Option<StrBuffer>,
path: Option<StrBuffer>,
) -> Result<Self, Error> {
let qr_code = Qr::new(qr_address, case_sensitive)?.with_border(QR_BORDER);
let details_view = {
@ -51,14 +45,14 @@ where
if let Some(account) = account {
para.add(Paragraph::new(
&theme::TEXT_BOLD,
tr("words__account_colon").into(),
TR::words__account_colon.try_into()?,
));
para.add(Paragraph::new(&theme::TEXT_MONO, account));
para.add(Paragraph::new(&theme::TEXT_MONO, account.into()));
}
if let Some(path) = path {
para.add(Paragraph::new(
&theme::TEXT_BOLD,
tr("address_details__derivation_path").into(),
TR::address_details__derivation_path.try_into()?,
));
para.add(Paragraph::new(&theme::TEXT_MONO, path));
}
@ -83,7 +77,7 @@ where
Ok(result)
}
pub fn add_xpub(&mut self, title: T, xpub: T) -> Result<(), Error> {
pub fn add_xpub(&mut self, title: StrBuffer, xpub: StrBuffer) -> Result<(), Error> {
self.xpubs
.push((title, xpub))
.map_err(|_| Error::OutOfRange)
@ -118,7 +112,7 @@ where
/// last page. On xpub pages there is SHOW ALL middle button when it
/// cannot fit one page. On xpub subpages there are wide arrows to
/// scroll.
fn get_button_layout(&mut self) -> ButtonLayout<T> {
fn get_button_layout(&mut self) -> ButtonLayout {
let (left, middle, right) = if self.is_in_subpage() {
let left = Some(ButtonDetails::up_arrow_icon_wide());
let right = if self.is_last_subpage() {
@ -130,7 +124,7 @@ where
} else {
let left = Some(ButtonDetails::left_arrow_icon());
let middle = if self.is_xpub_page() && self.subpages_in_current_page() > 1 {
Some(ButtonDetails::armed_text(tr("buttons__show_all").into()))
Some(ButtonDetails::armed_text(TR::buttons__show_all.into()))
} else {
None
};
@ -182,10 +176,7 @@ where
}
}
impl<T> Component for AddressDetails<T>
where
T: StringType + Clone,
{
impl Component for AddressDetails {
type Msg = ();
fn place(&mut self, bounds: Rect) -> Rect {
@ -272,10 +263,7 @@ where
}
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for AddressDetails<T>
where
T: StringType + Clone,
{
impl crate::trace::Trace for AddressDetails {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("AddressDetails");
match self.current_page {

@ -1,5 +1,6 @@
use crate::{
strutil::StringType,
micropython::buffer::StrBuffer,
strutil::TString,
ui::{
component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad},
display::{self, Color, Font},
@ -20,40 +21,40 @@ pub enum ConfirmMsg {
Confirm = 2,
}
pub struct Confirm<T: StringType, U> {
pub struct Confirm<U> {
bg: Pad,
bg_color: Color,
title: &'static str,
title: TString<'static>,
message: Child<Label<U>>,
alert: Option<Label<T>>,
info_title: Option<T>,
alert: Option<Label<U>>,
info_title: Option<TString<'static>>,
info_text: Option<Label<U>>,
button_text: T,
buttons: ButtonController<T>,
button_text: TString<'static>,
buttons: ButtonController,
/// Whether we are on the info screen (optional extra screen)
showing_info_screen: bool,
two_btn_confirm: bool,
}
impl<T, U> Confirm<T, U>
impl<U> Confirm<U>
where
T: StringType + Clone,
U: AsRef<str>,
{
pub fn new(
pub fn new<T: Into<TString<'static>>>(
bg_color: Color,
title: &'static str,
title: T,
message: Label<U>,
alert: Option<Label<T>>,
alert: Option<Label<U>>,
button_text: T,
two_btn_confirm: bool,
) -> Self {
let button_text = button_text.into();
let btn_layout =
Self::get_button_layout_general(false, button_text.clone(), false, two_btn_confirm);
Self {
bg: Pad::with_background(bg_color).with_clear(),
bg_color,
title,
title: title.into(),
message: Child::new(message),
alert,
info_title: None,
@ -66,8 +67,12 @@ where
}
/// Adding optional info screen
pub fn with_info_screen(mut self, info_title: T, info_text: Label<U>) -> Self {
self.info_title = Some(info_title);
pub fn with_info_screen<T: Into<TString<'static>>>(
mut self,
info_title: T,
info_text: Label<U>,
) -> Self {
self.info_title = Some(info_title.into());
self.info_text = Some(info_text);
self.buttons = ButtonController::new(self.get_button_layout());
self
@ -77,7 +82,7 @@ where
self.info_title.is_some()
}
fn get_button_layout(&self) -> ButtonLayout<T> {
fn get_button_layout(&self) -> ButtonLayout {
Self::get_button_layout_general(
self.showing_info_screen,
self.button_text.clone(),
@ -89,10 +94,10 @@ where
/// Not relying on self here, to call it in constructor.
fn get_button_layout_general(
showing_info_screen: bool,
button_text: T,
button_text: TString<'static>,
has_info_screen: bool,
two_btn_confirm: bool,
) -> ButtonLayout<T> {
) -> ButtonLayout {
if showing_info_screen {
ButtonLayout::arrow_none_none()
} else if has_info_screen {
@ -121,9 +126,8 @@ where
}
}
impl<T, U> Component for Confirm<T, U>
impl<U> Component for Confirm<U>
where
T: StringType + Clone,
U: AsRef<str>,
{
type Msg = ConfirmMsg;
@ -204,16 +208,18 @@ where
fn paint(&mut self) {
self.bg.paint();
let display_top_left = |text: &str| {
display::text_top_left(Point::zero(), text, Font::BOLD, WHITE, self.bg_color);
let display_top_left = |text: &TString<'static>| {
text.map(|t| {
display::text_top_left(Point::zero(), t, Font::BOLD, WHITE, self.bg_color)
});
};
// We are either on the info screen or on the "main" screen
if self.showing_info_screen {
display_top_left(unwrap!(self.info_title.clone()).as_ref());
self.info_title.map(|title| display_top_left(&title));
self.info_text.paint();
} else {
display_top_left(self.title);
display_top_left(&self.title);
self.message.paint();
self.alert.paint();
}
@ -227,9 +233,8 @@ where
}
#[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for Confirm<T, U>
impl<U> crate::trace::Trace for Confirm<U>
where
T: StringType + Clone,
U: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {

@ -1,5 +1,5 @@
use crate::{
strutil::StringType,
strutil::{StringType, TString},
time::Duration,
ui::{
component::{Component, Event, EventCtx, Never},
@ -20,22 +20,16 @@ pub enum ButtonPos {
Right,
}
pub struct Button<T>
where
T: StringType,
{
pub struct Button {
bounds: Rect,
pos: ButtonPos,
content: ButtonContent<T>,
content: ButtonContent,
styles: ButtonStyleSheet,
state: State,
}
impl<T> Button<T>
where
T: StringType,
{
pub fn new(pos: ButtonPos, content: ButtonContent<T>, styles: ButtonStyleSheet) -> Self {
impl Button {
pub fn new(pos: ButtonPos, content: ButtonContent, styles: ButtonStyleSheet) -> Self {
Self {
pos,
content,
@ -45,7 +39,7 @@ where
}
}
pub fn from_button_details(pos: ButtonPos, btn_details: ButtonDetails<T>) -> Self {
pub fn from_button_details(pos: ButtonPos, btn_details: ButtonDetails) -> Self {
// Deciding between text and icon
let style = btn_details.style();
match btn_details.content {
@ -54,7 +48,7 @@ where
}
}
pub fn with_text(pos: ButtonPos, text: T, styles: ButtonStyleSheet) -> Self {
pub fn with_text(pos: ButtonPos, text: TString<'static>, styles: ButtonStyleSheet) -> Self {
Self::new(pos, ButtonContent::Text(text), styles)
}
@ -62,7 +56,7 @@ where
Self::new(pos, ButtonContent::Icon(image), styles)
}
pub fn content(&self) -> &ButtonContent<T> {
pub fn content(&self) -> &ButtonContent {
&self.content
}
@ -79,7 +73,7 @@ where
}
/// Changing the text content of the button.
pub fn set_text(&mut self, text: T) {
pub fn set_text(&mut self, text: TString<'static>) {
self.content = ButtonContent::Text(text);
}
@ -117,7 +111,7 @@ where
} else {
match &self.content {
ButtonContent::Text(text) => {
let text_width = style.font.visible_text_width(text.as_ref());
let text_width = text.map(|t| style.font.visible_text_width(t));
if style.with_outline {
text_width + 2 * theme::BUTTON_OUTLINE
} else if style.with_arms {
@ -166,7 +160,7 @@ where
// Centering the text in case of fixed width.
if let ButtonContent::Text(text) = &self.content {
if let Some(fixed_width) = style.fixed_width {
let diff = fixed_width - style.font.visible_text_width(text.as_ref());
let diff = fixed_width - text.map(|t| style.font.visible_text_width(t));
offset_x = diff / 2;
}
}
@ -175,10 +169,7 @@ where
}
}
impl<T> Component for Button<T>
where
T: StringType,
{
impl Component for Button {
type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect {
@ -227,16 +218,15 @@ where
// Painting the content
match &self.content {
ButtonContent::Text(text) => {
ButtonContent::Text(text) => text.map(|t| {
display::text_left(
self.get_text_baseline(style)
- Offset::x(style.font.start_x_bearing(text.as_ref())),
text.as_ref(),
self.get_text_baseline(style) - Offset::x(style.font.start_x_bearing(t)),
t,
style.font,
fg_color,
bg_color,
);
}
}),
ButtonContent::Icon(icon) => {
// Allowing for possible offset of the area from current style
let icon_area = area.translate(style.offset);
@ -274,8 +264,8 @@ enum State {
}
#[derive(Clone)]
pub enum ButtonContent<T> {
Text(T),
pub enum ButtonContent {
Text(TString<'static>),
Icon(Icon),
}
@ -342,8 +332,8 @@ impl ButtonStyleSheet {
/// Describing the button on the screen - only visuals.
#[derive(Clone)]
pub struct ButtonDetails<T> {
pub content: ButtonContent<T>,
pub struct ButtonDetails {
pub content: ButtonContent,
pub duration: Option<Duration>,
with_outline: bool,
with_arms: bool,
@ -352,12 +342,9 @@ pub struct ButtonDetails<T> {
pub send_long_press: bool,
}
impl<T> ButtonDetails<T>
where
T: StringType,
{
impl ButtonDetails {
/// Text button.
pub fn text(text: T) -> Self {
pub fn text(text: TString<'static>) -> Self {
Self {
content: ButtonContent::Text(text),
duration: None,
@ -383,17 +370,17 @@ where
}
/// Resolves text and finds possible icon names.
pub fn from_text_possible_icon(text: T) -> Self {
match text.as_ref() {
"" => Self::cancel_icon(),
"<" => Self::left_arrow_icon(),
"^" => Self::up_arrow_icon(),
pub fn from_text_possible_icon(text: TString<'static>) -> Self {
match text {
TString::Str("") => Self::cancel_icon(),
TString::Str("<") => Self::left_arrow_icon(),
TString::Str("^") => Self::up_arrow_icon(),
_ => Self::text(text),
}
}
/// Text with arms signalling double press.
pub fn armed_text(text: T) -> Self {
pub fn armed_text(text: TString<'static>) -> Self {
Self::text(text).with_arms()
}
@ -485,20 +472,17 @@ where
/// Holding the button details for all three possible buttons.
#[derive(Clone)]
pub struct ButtonLayout<T> {
pub btn_left: Option<ButtonDetails<T>>,
pub btn_middle: Option<ButtonDetails<T>>,
pub btn_right: Option<ButtonDetails<T>>,
pub struct ButtonLayout {
pub btn_left: Option<ButtonDetails>,
pub btn_middle: Option<ButtonDetails>,
pub btn_right: Option<ButtonDetails>,
}
impl<T> ButtonLayout<T>
where
T: StringType,
{
impl ButtonLayout {
pub fn new(
btn_left: Option<ButtonDetails<T>>,
btn_middle: Option<ButtonDetails<T>>,
btn_right: Option<ButtonDetails<T>>,
btn_left: Option<ButtonDetails>,
btn_middle: Option<ButtonDetails>,
btn_right: Option<ButtonDetails>,
) -> Self {
Self {
btn_left,
@ -514,7 +498,7 @@ where
}
/// Arrows at sides, armed text in the middle.
pub fn arrow_armed_arrow(text: T) -> Self {
pub fn arrow_armed_arrow(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
Some(ButtonDetails::armed_text(text)),
@ -523,7 +507,7 @@ where
}
/// Left cancel, armed text and next right arrow.
pub fn cancel_armed_arrow(text: T) -> Self {
pub fn cancel_armed_arrow(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::cancel_icon()),
Some(ButtonDetails::armed_text(text)),
@ -532,7 +516,7 @@ where
}
/// Middle armed text and next right arrow.
pub fn none_armed_arrow(text: T) -> Self {
pub fn none_armed_arrow(text: TString<'static>) -> Self {
Self::new(
None,
Some(ButtonDetails::armed_text(text)),
@ -541,7 +525,7 @@ where
}
/// Left text, armed text and right info icon/text.
pub fn text_armed_info(left: T, middle: T) -> Self {
pub fn text_armed_info(left: TString<'static>, middle: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::from_text_possible_icon(left)),
Some(ButtonDetails::armed_text(middle)),
@ -550,7 +534,7 @@ where
}
/// Left cancel, armed text and right info icon/text.
pub fn cancel_armed_info(middle: T) -> Self {
pub fn cancel_armed_info(middle: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::cancel_icon()),
Some(ButtonDetails::armed_text(middle)),
@ -559,7 +543,7 @@ where
}
/// Left cancel, armed text and blank on right.
pub fn cancel_armed_none(middle: T) -> Self {
pub fn cancel_armed_none(middle: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::cancel_icon()),
Some(ButtonDetails::armed_text(middle)),
@ -568,7 +552,7 @@ where
}
/// Left back arrow and middle armed text.
pub fn arrow_armed_none(text: T) -> Self {
pub fn arrow_armed_none(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
Some(ButtonDetails::armed_text(text)),
@ -577,7 +561,7 @@ where
}
/// Left and right texts.
pub fn text_none_text(left: T, right: T) -> Self {
pub fn text_none_text(left: TString<'static>, right: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::from_text_possible_icon(left)),
None,
@ -586,7 +570,7 @@ where
}
/// Left text and right arrow.
pub fn text_none_arrow(text: T) -> Self {
pub fn text_none_arrow(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::from_text_possible_icon(text)),
None,
@ -595,7 +579,7 @@ where
}
/// Left text and WIDE right arrow.
pub fn text_none_arrow_wide(text: T) -> Self {
pub fn text_none_arrow_wide(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::from_text_possible_icon(text)),
None,
@ -604,7 +588,7 @@ where
}
/// Only right text.
pub fn none_none_text(text: T) -> Self {
pub fn none_none_text(text: TString<'static>) -> Self {
Self::new(
None,
None,
@ -622,7 +606,7 @@ where
}
/// Left arrow and right text.
pub fn arrow_none_text(text: T) -> Self {
pub fn arrow_none_text(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
None,
@ -631,7 +615,7 @@ where
}
/// Up arrow left and right text.
pub fn up_arrow_none_text(text: T) -> Self {
pub fn up_arrow_none_text(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::up_arrow_icon()),
None,
@ -667,7 +651,7 @@ where
}
/// Up arrow on left, middle text and info on the right.
pub fn up_arrow_armed_info(text: T) -> Self {
pub fn up_arrow_armed_info(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::up_arrow_icon()),
Some(ButtonDetails::armed_text(text)),
@ -685,7 +669,7 @@ where
}
/// Cancel cross on left and text on the right.
pub fn cancel_none_text(text: T) -> Self {
pub fn cancel_none_text(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::cancel_icon()),
None,
@ -694,7 +678,7 @@ where
}
/// Cancel cross on left and hold-to-confirm text on the right.
pub fn cancel_none_htc(text: T) -> Self {
pub fn cancel_none_htc(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::cancel_icon()),
None,
@ -703,7 +687,7 @@ where
}
/// Arrow back on left and hold-to-confirm text on the right.
pub fn arrow_none_htc(text: T) -> Self {
pub fn arrow_none_htc(text: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
None,
@ -712,12 +696,12 @@ where
}
/// Only armed text in the middle.
pub fn none_armed_none(text: T) -> Self {
pub fn none_armed_none(text: TString<'static>) -> Self {
Self::new(None, Some(ButtonDetails::armed_text(text)), None)
}
/// HTC on both sides.
pub fn htc_none_htc(left: T, right: T) -> Self {
pub fn htc_none_htc(left: TString<'static>, right: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::text(left).with_default_duration()),
None,
@ -930,30 +914,30 @@ impl ButtonActions {
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T: StringType> crate::trace::Trace for Button<T> {
impl crate::trace::Trace for Button {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Button");
match &self.content {
ButtonContent::Text(text) => t.string("text", text.as_ref()),
match self.content {
ButtonContent::Text(text) => t.string("text", text),
ButtonContent::Icon(icon) => {
t.null("text");
t.string("icon", icon.name);
t.string("icon", icon.name.into());
}
}
}
}
#[cfg(feature = "ui_debug")]
impl<T: StringType> crate::trace::Trace for ButtonDetails<T> {
impl crate::trace::Trace for ButtonDetails {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("ButtonDetails");
match &self.content {
match self.content {
ButtonContent::Text(text) => {
t.string("text", text.as_ref());
t.string("text", text);
}
ButtonContent::Icon(icon) => {
t.null("text");
t.string("icon", icon.name);
t.string("icon", icon.name.into());
}
}
if let Some(duration) = &self.duration {

@ -49,20 +49,14 @@ pub enum ButtonControllerMsg {
}
/// Defines what kind of button should be currently used.
pub enum ButtonType<T>
where
T: StringType,
{
Button(Button<T>),
HoldToConfirm(HoldToConfirm<T>),
pub enum ButtonType {
Button(Button),
HoldToConfirm(HoldToConfirm),
Nothing,
}
impl<T> ButtonType<T>
where
T: StringType,
{
pub fn from_button_details(pos: ButtonPos, btn_details: Option<ButtonDetails<T>>) -> Self {
impl ButtonType {
pub fn from_button_details(pos: ButtonPos, btn_details: Option<ButtonDetails>) -> Self {
if let Some(btn_details) = btn_details {
if btn_details.duration.is_some() {
Self::HoldToConfirm(HoldToConfirm::from_button_details(pos, btn_details))
@ -104,12 +98,9 @@ where
///
/// Users have a choice of a normal button or Hold-to-confirm button.
/// `button_type` specified what from those two is used, if anything.
pub struct ButtonContainer<T>
where
T: StringType,
{
pub struct ButtonContainer {
pos: ButtonPos,
button_type: ButtonType<T>,
button_type: ButtonType,
/// Holds the timestamp of when the button was pressed.
pressed_since: Option<Instant>,
/// How long the button should be pressed to send `long_press=true` in
@ -122,13 +113,10 @@ where
send_long_press: bool,
}
impl<T> ButtonContainer<T>
where
T: StringType,
{
impl ButtonContainer {
/// Supplying `None` as `btn_details` marks the button inactive
/// (it can be later activated in `set()`).
pub fn new(pos: ButtonPos, btn_details: Option<ButtonDetails<T>>) -> Self {
pub fn new(pos: ButtonPos, btn_details: Option<ButtonDetails>) -> Self {
const DEFAULT_LONG_PRESS_MS: u32 = 1000;
let send_long_press = btn_details
.as_ref()
@ -146,7 +134,7 @@ where
/// Changing the state of the button.
///
/// Passing `None` as `btn_details` will mark the button as inactive.
pub fn set(&mut self, btn_details: Option<ButtonDetails<T>>, button_area: Rect) {
pub fn set(&mut self, btn_details: Option<ButtonDetails>, button_area: Rect) {
self.send_long_press = btn_details
.as_ref()
.map_or(false, |btn| btn.send_long_press);
@ -254,14 +242,11 @@ where
///
/// There is optional complexity with IgnoreButtonDelay, which gets executed
/// only in cases where clients request it.
pub struct ButtonController<T>
where
T: StringType,
{
pub struct ButtonController {
pad: Pad,
left_btn: ButtonContainer<T>,
middle_btn: ButtonContainer<T>,
right_btn: ButtonContainer<T>,
left_btn: ButtonContainer,
middle_btn: ButtonContainer,
right_btn: ButtonContainer,
state: ButtonState,
/// Button area is needed so the buttons
/// can be "re-placed" after their text is changed
@ -271,11 +256,8 @@ where
ignore_btn_delay: Option<IgnoreButtonDelay>,
}
impl<T> ButtonController<T>
where
T: StringType,
{
pub fn new(btn_layout: ButtonLayout<T>) -> Self {
impl ButtonController {
pub fn new(btn_layout: ButtonLayout) -> Self {
Self {
pad: Pad::with_background(theme::BG).with_clear(),
left_btn: ButtonContainer::new(ButtonPos::Left, btn_layout.btn_left),
@ -295,7 +277,7 @@ where
}
/// Updating all the three buttons to the wanted states.
pub fn set(&mut self, btn_layout: ButtonLayout<T>) {
pub fn set(&mut self, btn_layout: ButtonLayout) {
self.pad.clear();
self.left_btn.set(btn_layout.btn_left, self.button_area);
self.middle_btn.set(btn_layout.btn_middle, self.button_area);
@ -397,10 +379,7 @@ where
}
}
impl<T> Component for ButtonController<T>
where
T: StringType,
{
impl Component for ButtonController {
type Msg = ButtonControllerMsg;
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
@ -763,7 +742,7 @@ impl Component for AutomaticMover {
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T: StringType> crate::trace::Trace for ButtonContainer<T> {
impl crate::trace::Trace for ButtonContainer {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
if let ButtonType::Button(btn) = &self.button_type {
btn.trace(t);
@ -774,7 +753,7 @@ impl<T: StringType> crate::trace::Trace for ButtonContainer<T> {
}
#[cfg(feature = "ui_debug")]
impl<T: StringType> crate::trace::Trace for ButtonController<T> {
impl crate::trace::Trace for ButtonController {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("ButtonController");
t.child("left_btn", &self.left_btn);

@ -2,6 +2,7 @@ use core::mem;
use crate::{
strutil::StringType,
translations::TR,
ui::{
component::{
base::Never,
@ -10,7 +11,6 @@ use crate::{
},
display::{self, Font},
geometry::{Alignment, Insets, Rect},
translations::tr,
util::animation_disabled,
},
};
@ -88,7 +88,7 @@ where
if self.indeterminate {
text_multiline(
self.area,
tr("coinjoin__title_progress"),
TR::coinjoin__title_progress.into(),
Font::BOLD,
theme::FG,
theme::BG,
@ -113,7 +113,7 @@ where
// BOTTOM
let top_rest = text_multiline_bottom(
self.area,
tr("coinjoin__do_not_disconnect"),
TR::coinjoin__do_not_disconnect.into(),
Font::BOLD,
theme::FG,
theme::BG,
@ -122,7 +122,7 @@ where
if let Some(rest) = top_rest {
text_multiline_bottom(
rest.inset(Insets::bottom(FOOTER_TEXT_MARGIN)),
self.text.as_ref(),
self.text.as_ref().into(),
Font::NORMAL,
theme::FG,
theme::BG,
@ -139,8 +139,8 @@ where
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("CoinJoinProgress");
t.string("header", tr("coinjoin__title_progress"));
t.string("text", self.text.as_ref());
t.string("footer", tr("coinjoin__do_not_disconnect"));
t.string("header", TR::coinjoin__title_progress.into());
t.string("text", self.text.as_ref().into());
t.string("footer", TR::coinjoin__do_not_disconnect.into());
}
}

@ -6,23 +6,23 @@ use crate::ui::{
use super::theme;
/// Display white text on black background
pub fn display_left<T: AsRef<str>>(baseline: Point, text: &T, font: Font) {
pub fn display_left<T: AsRef<str>>(baseline: Point, text: T, font: Font) {
display::text_left(baseline, text.as_ref(), font, theme::FG, theme::BG);
}
/// Display black text on white background
pub fn display_inverse<T: AsRef<str>>(baseline: Point, text: &T, font: Font) {
pub fn display_inverse<T: AsRef<str>>(baseline: Point, text: T, font: Font) {
display::text_left(baseline, text.as_ref(), font, theme::BG, theme::FG);
}
/// Display white text on black background,
/// centered around a baseline Point
pub fn display_center<T: AsRef<str>>(baseline: Point, text: &T, font: Font) {
pub fn display_center<T: AsRef<str>>(baseline: Point, text: T, font: Font) {
display::text_center(baseline, text.as_ref(), font, theme::FG, theme::BG);
}
/// Display white text on black background,
/// with right boundary at a baseline Point
pub fn display_right<T: AsRef<str>>(baseline: Point, text: &T, font: Font) {
pub fn display_right<T: AsRef<str>>(baseline: Point, text: T, font: Font) {
display::text_right(baseline, text.as_ref(), font, theme::FG, theme::BG);
}

@ -26,7 +26,7 @@ where
content_area: Rect,
title_area: Rect,
pad: Pad,
buttons: Child<ButtonController<T>>,
buttons: Child<ButtonController>,
page_counter: usize,
return_confirmed_index: bool,
show_scrollbar: bool,

@ -79,7 +79,7 @@ where
T: StringType + Clone,
{
formatted: FormattedText<T>,
btn_layout: ButtonLayout<T>,
btn_layout: ButtonLayout,
btn_actions: ButtonActions,
current_page: usize,
page_count: usize,
@ -93,7 +93,7 @@ where
T: StringType + Clone,
{
pub fn new(
btn_layout: ButtonLayout<T>,
btn_layout: ButtonLayout,
btn_actions: ButtonActions,
formatted: FormattedText<T>,
) -> Self {
@ -137,7 +137,7 @@ where
bounds
}
pub fn btn_layout(&self) -> ButtonLayout<T> {
pub fn btn_layout(&self) -> ButtonLayout {
// When we are in pagination inside this flow,
// show the up and down arrows on appropriate sides.
let current = self.btn_layout.clone();
@ -235,7 +235,7 @@ where
t.component("Page");
if let Some(title) = &self.title {
// Not calling it "title" as that is already traced by FlowPage
t.string("page_title", title.as_ref());
t.string("page_title", title.as_ref().into());
}
t.int("active_page", self.current_page as i64);
t.int("page_count", self.page_count as i64);

@ -1,5 +1,5 @@
use crate::{
strutil::StringType,
strutil::{StringType, TString},
time::{Duration, Instant},
ui::{
component::{Component, Event, EventCtx},
@ -18,21 +18,21 @@ pub enum HoldToConfirmMsg {
FailedToConfirm,
}
pub struct HoldToConfirm<T>
where
T: StringType,
{
pub struct HoldToConfirm {
pos: ButtonPos,
loader: Loader<T>,
loader: Loader,
text_width: i16,
}
impl<T> HoldToConfirm<T>
where
T: StringType,
{
pub fn text(pos: ButtonPos, text: T, styles: LoaderStyleSheet, duration: Duration) -> Self {
let text_width = styles.normal.font.visible_text_width(text.as_ref());
impl HoldToConfirm {
pub fn text<T: Into<TString<'static>>>(
pos: ButtonPos,
text: T,
styles: LoaderStyleSheet,
duration: Duration,
) -> Self {
let text = text.into();
let text_width = text.map(|t| styles.normal.font.visible_text_width(t));
Self {
pos,
loader: Loader::text(text, styles).with_growing_duration(duration),
@ -40,7 +40,7 @@ where
}
}
pub fn from_button_details(pos: ButtonPos, btn_details: ButtonDetails<T>) -> Self {
pub fn from_button_details(pos: ButtonPos, btn_details: ButtonDetails) -> Self {
let duration = btn_details
.duration
.unwrap_or_else(|| Duration::from_millis(DEFAULT_DURATION_MS));
@ -53,7 +53,8 @@ where
}
/// Updating the text of the component and re-placing it.
pub fn set_text(&mut self, text: T, button_area: Rect) {
pub fn set_text<T: Into<TString<'static>>>(&mut self, text: T, button_area: Rect) {
let text = text.into();
self.text_width = self.loader.get_text_width(&text);
self.loader.set_text(text);
self.place(button_area);
@ -71,7 +72,7 @@ where
self.loader.get_duration()
}
pub fn get_text(&self) -> &T {
pub fn get_text(&self) -> TString<'static> {
self.loader.get_text()
}
@ -85,10 +86,7 @@ where
}
}
impl<T> Component for HoldToConfirm<T>
where
T: StringType,
{
impl Component for HoldToConfirm {
type Msg = HoldToConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect {
@ -129,10 +127,7 @@ where
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for HoldToConfirm<T>
where
T: StringType,
{
impl crate::trace::Trace for HoldToConfirm {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("HoldToConfirm");
t.child("loader", &self.loader);

@ -1,5 +1,8 @@
use crate::{
error::Error,
micropython::buffer::StrBuffer,
strutil::StringType,
translations::TR,
trezorhal::usb::usb_configured,
ui::{
component::{Child, Component, Event, EventCtx, Label},
@ -12,7 +15,6 @@ use crate::{
event::USBEvent,
geometry::{Alignment2D, Insets, Offset, Point, Rect},
layout::util::get_user_custom_image,
translations::tr,
},
};
@ -53,7 +55,7 @@ where
label: Label<T>,
notification: Option<(T, u8)>,
/// Used for HTC functionality to lock device from homescreen
invisible_buttons: Child<ButtonController<T>>,
invisible_buttons: Child<ButtonController>,
}
impl<T> Homescreen<T>
@ -87,11 +89,8 @@ where
if !usb_configured() {
self.fill_notification_background();
// TODO: fill warning icons here as well?
display_center(
baseline,
&tr("homescreen__title_no_usb_connection"),
NOTIFICATION_FONT,
);
TR::homescreen__title_no_usb_connection
.map_translated(|t| display_center(baseline, t, NOTIFICATION_FONT));
} else if let Some((notification, _level)) = &self.notification {
self.fill_notification_background();
display_center(baseline, &notification.as_ref(), NOTIFICATION_FONT);
@ -183,9 +182,9 @@ where
T: StringType,
{
label: Child<Label<T>>,
instruction: Child<Label<T>>,
instruction: Child<Label<StrBuffer>>,
/// Used for unlocking the device from lockscreen
invisible_buttons: Child<ButtonController<T>>,
invisible_buttons: Child<ButtonController>,
/// Display coinjoin icon?
coinjoin_icon: Option<Icon>,
/// Screensaver mode (keep screen black)
@ -196,22 +195,25 @@ impl<T> Lockscreen<T>
where
T: StringType + Clone,
{
pub fn new(label: T, bootscreen: bool, coinjoin_authorized: bool) -> Self {
pub fn new(label: T, bootscreen: bool, coinjoin_authorized: bool) -> Result<Self, Error> {
// Buttons will not be visible, we only need all three of them to be present,
// so that even middle-click triggers the event.
let invisible_btn_layout = ButtonLayout::arrow_armed_arrow("".into());
let instruction_str = if bootscreen {
tr("homescreen__click_to_connect")
TR::homescreen__click_to_connect
} else {
tr("homescreen__click_to_unlock")
TR::homescreen__click_to_unlock
};
Lockscreen {
Ok(Lockscreen {
label: Child::new(Label::centered(label, theme::TEXT_BIG)),
instruction: Child::new(Label::centered(instruction_str.into(), theme::TEXT_NORMAL)),
instruction: Child::new(Label::centered(
instruction_str.try_into()?,
theme::TEXT_NORMAL,
)),
invisible_buttons: Child::new(ButtonController::new(invisible_btn_layout)),
coinjoin_icon: coinjoin_authorized.then_some(theme::ICON_COINJOIN),
screensaver: !bootscreen,
}
})
}
}
@ -265,7 +267,7 @@ where
{
title: Child<Label<T>>,
buffer_func: F,
buttons: Child<ButtonController<T>>,
buttons: Child<ButtonController>,
}
impl<T, F> ConfirmHomescreen<T, F>
@ -273,7 +275,7 @@ where
T: StringType + Clone,
{
pub fn new(title: T, buffer_func: F) -> Self {
let btn_layout = ButtonLayout::cancel_none_text(tr("buttons__change").into());
let btn_layout = ButtonLayout::cancel_none_text(TR::buttons__change.into());
ConfirmHomescreen {
title: Child::new(Label::centered(title, theme::TEXT_BOLD)),
buffer_func,

@ -13,7 +13,7 @@ use super::super::{
const DEFAULT_ITEMS_DISTANCE: i16 = 10;
pub trait Choice<T: StringType> {
pub trait Choice {
// Only `paint_center` is required, the rest is optional
// and therefore has a default implementation.
fn paint_center(&self, area: Rect, inverse: bool);
@ -26,7 +26,7 @@ pub trait Choice<T: StringType> {
0
}
fn btn_layout(&self) -> ButtonLayout<T>;
fn btn_layout(&self) -> ButtonLayout;
/// Whether it is possible to do the middle action event without
/// releasing the button - after long-press duration is reached.
@ -44,9 +44,9 @@ pub trait Choice<T: StringType> {
/// but offers a "lazy-loading" way of requesting the
/// items only when they are needed, one-by-one.
/// This way, no more than one item is stored in memory at any time.
pub trait ChoiceFactory<T: StringType> {
pub trait ChoiceFactory {
type Action;
type Item: Choice<T>;
type Item: Choice;
fn count(&self) -> usize;
fn get(&self, index: usize) -> (Self::Item, Self::Action);
@ -65,14 +65,13 @@ pub trait ChoiceFactory<T: StringType> {
///
/// `is_carousel` can be used to make the choice page "infinite" -
/// after reaching one end, users will appear at the other end.
pub struct ChoicePage<F, T, A>
pub struct ChoicePage<F, A>
where
F: ChoiceFactory<T, Action = A>,
T: StringType,
F: ChoiceFactory<Action = A>,
{
choices: F,
pad: Pad,
buttons: Child<ButtonController<T>>,
buttons: Child<ButtonController>,
page_counter: usize,
/// How many pixels are between the items.
items_distance: i16,
@ -95,10 +94,9 @@ where
animated_steps_to_do: i16,
}
impl<F, T, A> ChoicePage<F, T, A>
impl<F, A> ChoicePage<F, A>
where
F: ChoiceFactory<T, Action = A>,
T: StringType + Clone,
F: ChoiceFactory<Action = A>,
{
pub fn new(choices: F) -> Self {
let initial_btn_layout = choices.get(0).0.btn_layout();
@ -281,12 +279,12 @@ where
}
/// Getting the choice on the current index
fn get_current_choice(&self) -> (<F as ChoiceFactory<T>>::Item, A) {
fn get_current_choice(&self) -> (<F as ChoiceFactory>::Item, A) {
self.choices.get(self.page_counter)
}
/// Getting the current item
pub fn get_current_item(&self) -> <F as ChoiceFactory<T>>::Item {
pub fn get_current_item(&self) -> <F as ChoiceFactory>::Item {
self.get_current_choice().0
}
@ -482,10 +480,9 @@ where
}
}
impl<F, T, A> Component for ChoicePage<F, T, A>
impl<F, A> Component for ChoicePage<F, A>
where
F: ChoiceFactory<T, Action = A>,
T: StringType + Clone,
F: ChoiceFactory<Action = A>,
{
type Msg = (A, bool);
@ -597,11 +594,10 @@ where
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<F, T, A> crate::trace::Trace for ChoicePage<F, T, A>
impl<F, A> crate::trace::Trace for ChoicePage<F, A>
where
F: ChoiceFactory<T, Action = A>,
T: StringType + Clone,
<F as ChoiceFactory<T>>::Item: crate::trace::Trace,
F: ChoiceFactory<Action = A>,
<F as ChoiceFactory>::Item: crate::trace::Trace,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("ChoicePage");

@ -14,16 +14,16 @@ const ICON_RIGHT_PADDING: i16 = 2;
/// Simple string component used as a choice item.
#[derive(Clone)]
pub struct ChoiceItem<T: StringType> {
pub struct ChoiceItem {
text: ShortString,
icon: Option<Icon>,
btn_layout: ButtonLayout<T>,
btn_layout: ButtonLayout,
font: Font,
middle_action_without_release: bool,
}
impl<T: StringType> ChoiceItem<T> {
pub fn new<U: AsRef<str>>(text: U, btn_layout: ButtonLayout<T>) -> Self {
impl ChoiceItem {
pub fn new<U: AsRef<str>>(text: U, btn_layout: ButtonLayout) -> Self {
Self {
text: String::from(text.as_ref()),
icon: None,
@ -55,17 +55,17 @@ impl<T: StringType> ChoiceItem<T> {
}
/// Setting left button.
pub fn set_left_btn(&mut self, btn_left: Option<ButtonDetails<T>>) {
pub fn set_left_btn(&mut self, btn_left: Option<ButtonDetails>) {
self.btn_layout.btn_left = btn_left;
}
/// Setting middle button.
pub fn set_middle_btn(&mut self, btn_middle: Option<ButtonDetails<T>>) {
pub fn set_middle_btn(&mut self, btn_middle: Option<ButtonDetails>) {
self.btn_layout.btn_middle = btn_middle;
}
/// Setting right button.
pub fn set_right_btn(&mut self, btn_right: Option<ButtonDetails<T>>) {
pub fn set_right_btn(&mut self, btn_right: Option<ButtonDetails>) {
self.btn_layout.btn_right = btn_right;
}
@ -87,10 +87,7 @@ impl<T: StringType> ChoiceItem<T> {
}
}
impl<T> Choice<T> for ChoiceItem<T>
where
T: StringType + Clone,
{
impl Choice for ChoiceItem {
/// Painting the item as the main choice in the middle.
/// Showing both the icon and text, if the icon is available.
fn paint_center(&self, area: Rect, inverse: bool) {
@ -125,7 +122,7 @@ where
}
/// Getting current button layout.
fn btn_layout(&self) -> ButtonLayout<T> {
fn btn_layout(&self) -> ButtonLayout {
self.btn_layout.clone()
}
@ -196,9 +193,9 @@ fn paint_text_icon(
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T: StringType> crate::trace::Trace for ChoiceItem<T> {
impl crate::trace::Trace for ChoiceItem {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("ChoiceItem");
t.string("content", self.text.as_ref());
t.string("content", self.text.as_str().into());
}
}

@ -1,9 +1,8 @@
use crate::{
strutil::StringType,
translations::TR,
ui::{
component::{Component, Event, EventCtx},
geometry::Rect,
translations::tr,
},
};
@ -21,9 +20,9 @@ impl ChoiceFactoryNumberInput {
}
}
impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryNumberInput {
impl ChoiceFactory for ChoiceFactoryNumberInput {
type Action = u32;
type Item = ChoiceItem<T>;
type Item = ChoiceItem;
fn count(&self) -> usize {
(self.max - self.min + 1) as usize
@ -34,7 +33,7 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryNumberInput {
let text: String<10> = String::from(num);
let mut choice_item = ChoiceItem::new(
text,
ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()),
ButtonLayout::arrow_armed_arrow(TR::buttons__select.into()),
);
// Disabling prev/next buttons for the first/last choice.
@ -42,7 +41,7 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryNumberInput {
if choice_index == 0 {
choice_item.set_left_btn(None);
}
if choice_index == <ChoiceFactoryNumberInput as ChoiceFactory<T>>::count(self) - 1 {
if choice_index == <ChoiceFactoryNumberInput as ChoiceFactory>::count(self) - 1 {
choice_item.set_right_btn(None);
}
@ -52,15 +51,12 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryNumberInput {
/// Simple wrapper around `ChoicePage` that allows for
/// inputting a list of values and receiving the chosen one.
pub struct NumberInput<T: StringType + Clone> {
choice_page: ChoicePage<ChoiceFactoryNumberInput, T, u32>,
pub struct NumberInput {
choice_page: ChoicePage<ChoiceFactoryNumberInput, u32>,
min: u32,
}
impl<T> NumberInput<T>
where
T: StringType + Clone,
{
impl NumberInput {
pub fn new(min: u32, max: u32, init_value: u32) -> Self {
let choices = ChoiceFactoryNumberInput::new(min, max);
let initial_page = init_value - min;
@ -71,10 +67,7 @@ where
}
}
impl<T> Component for NumberInput<T>
where
T: StringType + Clone,
{
impl Component for NumberInput {
type Msg = u32;
fn place(&mut self, bounds: Rect) -> Rect {
@ -93,10 +86,7 @@ where
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for NumberInput<T>
where
T: StringType + Clone,
{
impl crate::trace::Trace for NumberInput {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("NumberInput");
t.child("choice_page", &self.choice_page);

@ -1,11 +1,12 @@
use crate::{
strutil::StringType,
micropython::buffer::StrBuffer,
strutil::TString,
translations::TR,
trezorhal::random,
ui::{
component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx},
display::Icon,
geometry::Rect,
translations::tr,
util::char_to_string,
},
};
@ -44,80 +45,71 @@ const DIGITS_INDEX: usize = 5;
const SPECIAL_INDEX: usize = 6;
const SPACE_INDEX: usize = 7;
#[derive(Copy, Clone)]
#[derive(Clone)]
struct MenuItem {
text: &'static str,
text: TString<'static>,
action: PassphraseAction,
icon: Option<Icon>,
show_confirm: bool,
without_release: bool,
translate: bool,
}
const MENU: [MenuItem; MENU_LENGTH] = [
MenuItem {
text: "inputs__show",
text: TR::inputs__show.as_tstring(),
action: PassphraseAction::Show,
icon: Some(theme::ICON_EYE),
show_confirm: true,
without_release: false,
translate: true,
},
MenuItem {
text: "CANCEL_OR_DELETE",
text: TString::from_str("CANCEL OR DELETE"),
action: PassphraseAction::CancelOrDelete,
icon: None,
show_confirm: true,
without_release: true,
translate: false,
},
MenuItem {
text: "inputs__enter",
text: TR::inputs__enter.as_tstring(),
action: PassphraseAction::Enter,
icon: Some(theme::ICON_TICK),
show_confirm: true,
without_release: false,
translate: true,
},
MenuItem {
text: "abc",
text: TString::from_str("abc"),
action: PassphraseAction::Category(ChoiceCategory::LowercaseLetter),
icon: None,
show_confirm: false,
without_release: false,
translate: false,
},
MenuItem {
text: "ABC",
text: TString::from_str("ABC"),
action: PassphraseAction::Category(ChoiceCategory::UppercaseLetter),
icon: None,
show_confirm: false,
without_release: false,
translate: false,
},
MenuItem {
text: "123",
text: TString::from_str("123"),
action: PassphraseAction::Category(ChoiceCategory::Digit),
icon: None,
show_confirm: false,
without_release: false,
translate: false,
},
MenuItem {
text: "#$!",
text: TString::from_str("#$!"),
action: PassphraseAction::Category(ChoiceCategory::SpecialSymbol),
icon: None,
show_confirm: false,
without_release: false,
translate: false,
},
MenuItem {
text: "inputs__space",
text: TR::inputs__space.as_tstring(),
action: PassphraseAction::Character(' '),
icon: Some(theme::ICON_SPACE),
show_confirm: false,
without_release: false,
translate: true,
},
];
@ -190,35 +182,30 @@ impl ChoiceFactoryPassphrase {
}
/// MENU choices with accept and cancel hold-to-confirm side buttons.
fn get_menu_item<T: StringType>(
&self,
choice_index: usize,
) -> (ChoiceItem<T>, PassphraseAction) {
fn get_menu_item(&self, choice_index: usize) -> (ChoiceItem, PassphraseAction) {
#[allow(const_item_mutation)]
let current_item = &mut MENU[choice_index];
// More options for CANCEL/DELETE button
let mut current_item = MENU[choice_index];
if matches!(current_item.action, PassphraseAction::CancelOrDelete) {
if self.is_empty {
current_item.text = tr("inputs__cancel");
current_item.text = TR::inputs__cancel.into();
current_item.icon = Some(theme::ICON_CANCEL);
} else {
current_item.text = tr("inputs__delete");
current_item.text = TR::inputs__delete.into();
current_item.icon = Some(theme::ICON_DELETE);
}
}
// Translating when needed
if current_item.translate {
current_item.text = tr(current_item.text);
}
let mut menu_item = ChoiceItem::new(
current_item.text,
ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()),
);
let mut menu_item = current_item.text.map(|t| {
ChoiceItem::new(
t,
ButtonLayout::arrow_armed_arrow(TR::buttons__select.into()),
)
});
// Action buttons have different middle button text
if current_item.show_confirm {
let confirm_btn = ButtonDetails::armed_text(tr("buttons__confirm").into());
let confirm_btn = ButtonDetails::armed_text(TR::buttons__confirm.into());
menu_item.set_middle_btn(Some(confirm_btn));
}
@ -235,17 +222,16 @@ impl ChoiceFactoryPassphrase {
/// Character choices with a BACK to MENU choice at the end (visible from
/// start) to return back
fn get_character_item<T: StringType>(
&self,
choice_index: usize,
) -> (ChoiceItem<T>, PassphraseAction) {
fn get_character_item(&self, choice_index: usize) -> (ChoiceItem, PassphraseAction) {
if is_menu_choice(&self.current_category, choice_index) {
(
ChoiceItem::new(
tr("inputs__back"),
ButtonLayout::arrow_armed_arrow(tr("inputs__return").into()),
)
.with_icon(theme::ICON_ARROW_BACK_UP),
TR::inputs__back.map_translated(|t| {
ChoiceItem::new(
t,
ButtonLayout::arrow_armed_arrow(TR::inputs__return.into()),
)
.with_icon(theme::ICON_ARROW_BACK_UP)
}),
PassphraseAction::Menu,
)
} else {
@ -253,7 +239,7 @@ impl ChoiceFactoryPassphrase {
(
ChoiceItem::new(
char_to_string(ch),
ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()),
ButtonLayout::arrow_armed_arrow(TR::buttons__select.into()),
),
PassphraseAction::Character(ch),
)
@ -261,9 +247,9 @@ impl ChoiceFactoryPassphrase {
}
}
impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryPassphrase {
impl ChoiceFactory for ChoiceFactoryPassphrase {
type Action = PassphraseAction;
type Item = ChoiceItem<T>;
type Item = ChoiceItem;
fn count(&self) -> usize {
let length = get_category_length(&self.current_category);
@ -282,8 +268,8 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryPassphrase {
}
/// Component for entering a passphrase.
pub struct PassphraseEntry<T: StringType + Clone> {
choice_page: ChoicePage<ChoiceFactoryPassphrase, T, PassphraseAction>,
pub struct PassphraseEntry {
choice_page: ChoicePage<ChoiceFactoryPassphrase, PassphraseAction>,
passphrase_dots: Child<ChangingTextLine<String<MAX_PASSPHRASE_LENGTH>>>,
show_plain_passphrase: bool,
show_last_digit: bool,
@ -291,10 +277,7 @@ pub struct PassphraseEntry<T: StringType + Clone> {
current_category: ChoiceCategory,
}
impl<T> PassphraseEntry<T>
where
T: StringType + Clone,
{
impl PassphraseEntry {
pub fn new() -> Self {
Self {
choice_page: ChoicePage::new(ChoiceFactoryPassphrase::new(ChoiceCategory::Menu, true))
@ -385,10 +368,7 @@ where
}
}
impl<T> Component for PassphraseEntry<T>
where
T: StringType + Clone,
{
impl Component for PassphraseEntry {
type Msg = CancelConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect {
@ -474,17 +454,14 @@ where
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for PassphraseEntry<T>
where
T: StringType + Clone,
{
impl crate::trace::Trace for PassphraseEntry {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("PassphraseKeyboard");
t.string("passphrase", self.textbox.content());
t.string("passphrase", self.textbox.content().into());
t.string(
"current_category",
match self.current_category {
ChoiceCategory::Menu => "MENU",
ChoiceCategory::Menu => "MENU".into(),
ChoiceCategory::LowercaseLetter => MENU[LOWERCASE_INDEX].text,
ChoiceCategory::UppercaseLetter => MENU[UPPERCASE_INDEX].text,
ChoiceCategory::Digit => MENU[DIGITS_INDEX].text,

@ -1,11 +1,11 @@
use crate::{
strutil::StringType,
strutil::{StringType, TString},
translations::TR,
trezorhal::random,
ui::{
component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx},
display::{Font, Icon},
geometry::Rect,
translations::tr,
},
};
@ -23,45 +23,65 @@ enum PinAction {
Digit(char),
}
struct PinChoice {
text: TString<'static>,
action: PinAction,
icon: Option<Icon>,
without_release: bool,
}
impl PinChoice {
pub const fn new(
text: TString<'static>,
action: PinAction,
icon: Option<Icon>,
without_release: bool,
) -> Self {
Self {
text,
action,
icon,
without_release,
}
}
}
const MAX_PIN_LENGTH: usize = 50;
const EMPTY_PIN_STR: &str = "_";
const CHOICE_LENGTH: usize = 13;
const NUMBER_START_INDEX: usize = 3;
/// Text, action, icon, without_release, translate
const CHOICES: [(&str, PinAction, Option<Icon>, bool, bool); CHOICE_LENGTH] = [
const CHOICES: [PinChoice; CHOICE_LENGTH] = [
// DELETE should be triggerable without release (after long-press)
(
"inputs__delete",
PinChoice::new(
TR::inputs__delete.as_tstring(),
PinAction::Delete,
Some(theme::ICON_DELETE),
true, // without_release
true,
),
(
"inputs__show",
PinChoice::new(
TR::inputs__show.as_tstring(),
PinAction::Show,
Some(theme::ICON_EYE),
false,
true,
),
(
"inputs__enter",
PinChoice::new(
TR::inputs__enter.as_tstring(),
PinAction::Enter,
Some(theme::ICON_TICK),
false,
true,
),
("0", PinAction::Digit('0'), None, false, false),
("1", PinAction::Digit('1'), None, false, false),
("2", PinAction::Digit('2'), None, false, false),
("3", PinAction::Digit('3'), None, false, false),
("4", PinAction::Digit('4'), None, false, false),
("5", PinAction::Digit('5'), None, false, false),
("6", PinAction::Digit('6'), None, false, false),
("7", PinAction::Digit('7'), None, false, false),
("8", PinAction::Digit('8'), None, false, false),
("9", PinAction::Digit('9'), None, false, false),
PinChoice::new(TString::from_str("0"), PinAction::Digit('0'), None, false),
PinChoice::new(TString::from_str("1"), PinAction::Digit('1'), None, false),
PinChoice::new(TString::from_str("2"), PinAction::Digit('2'), None, false),
PinChoice::new(TString::from_str("3"), PinAction::Digit('3'), None, false),
PinChoice::new(TString::from_str("4"), PinAction::Digit('4'), None, false),
PinChoice::new(TString::from_str("5"), PinAction::Digit('5'), None, false),
PinChoice::new(TString::from_str("6"), PinAction::Digit('6'), None, false),
PinChoice::new(TString::from_str("7"), PinAction::Digit('7'), None, false),
PinChoice::new(TString::from_str("8"), PinAction::Digit('8'), None, false),
PinChoice::new(TString::from_str("9"), PinAction::Digit('9'), None, false),
];
fn get_random_digit_position() -> usize {
@ -70,40 +90,37 @@ fn get_random_digit_position() -> usize {
struct ChoiceFactoryPIN;
impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryPIN {
impl ChoiceFactory for ChoiceFactoryPIN {
type Action = PinAction;
type Item = ChoiceItem<T>;
type Item = ChoiceItem;
fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) {
let (mut choice_str, action, icon, without_release, translate) = CHOICES[choice_index];
// Translating when needed
if translate {
choice_str = tr(choice_str);
}
let choice = &CHOICES[choice_index];
let mut choice_item = ChoiceItem::new(
choice_str,
ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()),
);
let mut choice_item = choice.text.map(|t| {
ChoiceItem::new(
t,
ButtonLayout::arrow_armed_arrow(TR::buttons__select.into()),
)
});
// Action buttons have different middle button text
if !matches!(action, PinAction::Digit(_)) {
let confirm_btn = ButtonDetails::armed_text(tr("buttons__confirm").into());
if !matches!(choice.action, PinAction::Digit(_)) {
let confirm_btn = ButtonDetails::armed_text(TR::buttons__confirm.into());
choice_item.set_middle_btn(Some(confirm_btn));
}
// Making middle button create LongPress events
if without_release {
if choice.without_release {
choice_item = choice_item.with_middle_action_without_release();
}
// Adding icons for appropriate items
if let Some(icon) = icon {
if let Some(icon) = choice.icon {
choice_item = choice_item.with_icon(icon);
}
(choice_item, action)
(choice_item, choice.action)
}
fn count(&self) -> usize {
@ -113,7 +130,7 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryPIN {
/// Component for entering a PIN.
pub struct PinEntry<T: StringType + Clone> {
choice_page: ChoicePage<ChoiceFactoryPIN, T, PinAction>,
choice_page: ChoicePage<ChoiceFactoryPIN, PinAction>,
header_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
prompt: T,
@ -138,7 +155,7 @@ where
let (showing_real_prompt, header_line_content, pin_line_content) = if show_subprompt {
(
false,
String::from(tr("pin__title_wrong_pin")),
TR::pin__title_wrong_pin.map_translated(|t| String::from(t)),
String::from(subprompt.as_ref()),
)
} else {
@ -323,8 +340,8 @@ where
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("PinKeyboard");
t.string("subprompt", self.subprompt.as_ref());
t.string("pin", self.textbox.content());
t.string("subprompt", self.subprompt.as_ref().into());
t.string("pin", self.textbox.content().into());
t.child("choice_page", &self.choice_page);
}
}

@ -1,9 +1,9 @@
use crate::{
strutil::StringType,
strutil::TString,
translations::TR,
ui::{
component::{Component, Event, EventCtx},
geometry::Rect,
translations::tr,
},
};
@ -14,24 +14,24 @@ use heapless::Vec;
// as would be via `const N: usize` generics.
const MAX_LENGTH: usize = 5;
struct ChoiceFactorySimple<T: StringType> {
choices: Vec<T, MAX_LENGTH>,
struct ChoiceFactorySimple {
choices: Vec<TString<'static>, MAX_LENGTH>,
carousel: bool,
}
impl<T: StringType> ChoiceFactorySimple<T> {
fn new(choices: Vec<T, MAX_LENGTH>, carousel: bool) -> Self {
impl ChoiceFactorySimple {
fn new(choices: Vec<TString<'static>, MAX_LENGTH>, carousel: bool) -> Self {
Self { choices, carousel }
}
fn get_string(&self, choice_index: usize) -> &str {
self.choices[choice_index].as_ref()
fn get_string(&self, choice_index: usize) -> TString<'static> {
self.choices[choice_index]
}
}
impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactorySimple<T> {
impl ChoiceFactory for ChoiceFactorySimple {
type Action = usize;
type Item = ChoiceItem<T>;
type Item = ChoiceItem;
fn count(&self) -> usize {
self.choices.len()
@ -39,10 +39,12 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactorySimple<T> {
fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) {
let text = &self.choices[choice_index];
let mut choice_item = ChoiceItem::new(
text,
ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()),
);
let mut choice_item = text.map(|t| {
ChoiceItem::new(
t,
ButtonLayout::arrow_armed_arrow(TR::buttons__select.into()),
)
});
// Disabling prev/next buttons for the first/last choice when not in carousel.
// (could be done to the same button if there is only one)
@ -61,19 +63,13 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactorySimple<T> {
/// Simple wrapper around `ChoicePage` that allows for
/// inputting a list of values and receiving the chosen one.
pub struct SimpleChoice<T>
where
T: StringType + Clone,
{
choice_page: ChoicePage<ChoiceFactorySimple<T>, T, usize>,
pub struct SimpleChoice {
choice_page: ChoicePage<ChoiceFactorySimple, usize>,
pub return_index: bool,
}
impl<T> SimpleChoice<T>
where
T: StringType + Clone,
{
pub fn new(str_choices: Vec<T, MAX_LENGTH>, carousel: bool) -> Self {
impl SimpleChoice {
pub fn new(str_choices: Vec<TString<'static>, MAX_LENGTH>, carousel: bool) -> Self {
let choices = ChoiceFactorySimple::new(str_choices, carousel);
Self {
choice_page: ChoicePage::new(choices).with_carousel(carousel),
@ -100,15 +96,12 @@ where
}
/// Translating the resulting index into actual string choice.
pub fn result_by_index(&self, index: usize) -> &str {
pub fn result_by_index(&self, index: usize) -> TString<'static> {
self.choice_page.choice_factory().get_string(index)
}
}
impl<T> Component for SimpleChoice<T>
where
T: StringType + Clone,
{
impl Component for SimpleChoice {
type Msg = usize;
fn place(&mut self, bounds: Rect) -> Rect {
@ -127,10 +120,7 @@ where
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for SimpleChoice<T>
where
T: StringType + Clone,
{
impl crate::trace::Trace for SimpleChoice {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("SimpleChoice");
t.child("choice_page", &self.choice_page);

@ -1,10 +1,9 @@
use crate::{
strutil::StringType,
translations::TR,
trezorhal::{random, wordlist::Wordlist},
ui::{
component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx},
geometry::Rect,
translations::tr,
util::char_to_string,
},
};
@ -77,9 +76,9 @@ impl ChoiceFactoryWordlist {
}
}
impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryWordlist {
impl ChoiceFactory for ChoiceFactoryWordlist {
type Action = WordlistAction;
type Item = ChoiceItem<T>;
type Item = ChoiceItem;
fn count(&self) -> usize {
// Accounting for the DELETE option (+1)
@ -95,12 +94,14 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryWordlist {
// (is a requirement for WORDS, doing it for LETTERS as well to unite it)
if choice_index == DELETE_INDEX {
return (
ChoiceItem::new(
tr("inputs__delete"),
ButtonLayout::arrow_armed_arrow(tr("buttons__confirm").into()),
)
.with_icon(theme::ICON_DELETE)
.with_middle_action_without_release(),
TR::inputs__delete.map_translated(|t| {
ChoiceItem::new(
t,
ButtonLayout::arrow_armed_arrow(TR::buttons__confirm.into()),
)
.with_icon(theme::ICON_DELETE)
.with_middle_action_without_release()
}),
WordlistAction::Delete,
);
}
@ -111,7 +112,7 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryWordlist {
(
ChoiceItem::new(
word,
ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()),
ButtonLayout::arrow_armed_arrow(TR::buttons__select.into()),
),
WordlistAction::Word(word),
)
@ -124,7 +125,7 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryWordlist {
(
ChoiceItem::new(
char_to_string(letter),
ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()),
ButtonLayout::arrow_armed_arrow(TR::buttons__select.into()),
),
WordlistAction::Letter(letter),
)
@ -133,21 +134,18 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryWordlist {
}
/// Component for entering a mnemonic from a wordlist - BIP39 or SLIP39.
pub struct WordlistEntry<T: StringType + Clone> {
choice_page: ChoicePage<ChoiceFactoryWordlist, T, WordlistAction>,
pub struct WordlistEntry {
choice_page: ChoicePage<ChoiceFactoryWordlist, WordlistAction>,
chosen_letters: Child<ChangingTextLine<String<{ MAX_WORD_LENGTH + 1 }>>>,
textbox: TextBox<MAX_WORD_LENGTH>,
offer_words: bool,
wordlist_type: WordlistType,
}
impl<T> WordlistEntry<T>
where
T: StringType + Clone,
{
impl WordlistEntry {
pub fn new(wordlist_type: WordlistType) -> Self {
let choices = ChoiceFactoryWordlist::new(wordlist_type, "");
let choices_count = <ChoiceFactoryWordlist as ChoiceFactory<T>>::count(&choices);
let choices_count = <ChoiceFactoryWordlist as ChoiceFactory>::count(&choices);
Self {
// Starting at random letter position
choice_page: ChoicePage::new(choices)
@ -177,7 +175,7 @@ where
if self.offer_words {
INITIAL_PAGE_COUNTER
} else {
let choices_count = <ChoiceFactoryWordlist as ChoiceFactory<T>>::count(new_choices);
let choices_count = <ChoiceFactoryWordlist as ChoiceFactory>::count(new_choices);
// There should be always DELETE and at least one letter
assert!(choices_count > 1);
if choices_count == 2 {
@ -189,8 +187,7 @@ where
loop {
let random_position = get_random_position(choices_count);
let current_action =
<ChoiceFactoryWordlist as ChoiceFactory<T>>::get(new_choices, random_position)
.1;
<ChoiceFactoryWordlist as ChoiceFactory>::get(new_choices, random_position).1;
if let WordlistAction::Letter(current_letter) = current_action {
if let Some(last_letter) = self.get_last_textbox_letter() {
if current_letter == last_letter {
@ -227,10 +224,7 @@ where
}
}
impl<T> Component for WordlistEntry<T>
where
T: StringType + Clone,
{
impl Component for WordlistEntry {
type Msg = &'static str;
fn place(&mut self, bounds: Rect) -> Rect {
@ -274,13 +268,10 @@ where
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for WordlistEntry<T>
where
T: StringType + Clone,
{
impl crate::trace::Trace for WordlistEntry {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("MnemonicKeyboard"); // unified with TT
t.string("textbox", self.textbox.content());
t.string("textbox", self.textbox.content().into());
if self.offer_words {
t.bool("word_choices", true);

@ -1,5 +1,5 @@
use crate::{
strutil::StringType,
strutil::{StringType, TString},
time::{Duration, Instant},
ui::{
animation::Animation,
@ -27,25 +27,19 @@ enum State {
Grown,
}
pub struct Loader<T>
where
T: StringType,
{
pub struct Loader {
area: Rect,
state: State,
growing_duration: Duration,
shrinking_duration: Duration,
text_overlay: display::TextOverlay<T>,
text_overlay: display::TextOverlay,
styles: LoaderStyleSheet,
}
impl<T> Loader<T>
where
T: StringType,
{
impl Loader {
pub const SIZE: Offset = Offset::new(120, 120);
pub fn new(text_overlay: display::TextOverlay<T>, styles: LoaderStyleSheet) -> Self {
pub fn new(text_overlay: display::TextOverlay, styles: LoaderStyleSheet) -> Self {
Self {
area: Rect::zero(),
state: State::Initial,
@ -56,8 +50,8 @@ where
}
}
pub fn text(text: T, styles: LoaderStyleSheet) -> Self {
let text_overlay = display::TextOverlay::new(text, styles.normal.font);
pub fn text<T: Into<TString<'static>>>(text: T, styles: LoaderStyleSheet) -> Self {
let text_overlay = display::TextOverlay::new(text.into(), styles.normal.font);
Self::new(text_overlay, styles)
}
@ -76,18 +70,18 @@ where
self.growing_duration
}
pub fn get_text(&self) -> &T {
pub fn get_text(&self) -> TString<'static> {
self.text_overlay.get_text()
}
/// Change the text of the loader.
pub fn set_text(&mut self, text: T) {
self.text_overlay.set_text(text);
pub fn set_text<T: Into<TString<'static>>>(&mut self, text: T) {
self.text_overlay.set_text(text.into());
}
/// Return width of given text according to current style.
pub fn get_text_width(&self, text: &T) -> i16 {
self.styles.normal.font.text_width(text.as_ref())
pub fn get_text_width(&self, text: &TString<'static>) -> i16 {
text.map(|t| self.styles.normal.font.text_width(t))
}
pub fn start_growing(&mut self, ctx: &mut EventCtx, now: Instant) {
@ -171,10 +165,7 @@ where
}
}
impl<T> Component for Loader<T>
where
T: StringType,
{
impl Component for Loader {
type Msg = LoaderMsg;
fn place(&mut self, bounds: Rect) -> Rect {
@ -259,13 +250,10 @@ impl LoaderStyleSheet {
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Loader<T>
where
T: StringType,
{
impl crate::trace::Trace for Loader {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Loader");
t.string("text", self.get_text().as_ref());
t.string("text", self.get_text());
t.int("duration", self.get_duration().to_millis() as i64);
}
}

@ -1,10 +1,9 @@
use crate::{
strutil::StringType,
translations::TR,
ui::{
component::{Child, Component, ComponentExt, Event, EventCtx, Pad, PageMsg, Paginate},
display::Color,
geometry::{Insets, Rect},
translations::tr,
},
};
@ -13,30 +12,28 @@ use super::{
ButtonDetails, ButtonLayout, ButtonPos,
};
pub struct ButtonPage<T, U>
pub struct ButtonPage<T>
where
T: Component + Paginate,
U: StringType,
{
page_count: usize,
active_page: usize,
content: Child<T>,
pad: Pad,
/// Left button of the first screen
cancel_btn_details: Option<ButtonDetails<U>>,
cancel_btn_details: Option<ButtonDetails>,
/// Right button of the last screen
confirm_btn_details: Option<ButtonDetails<U>>,
confirm_btn_details: Option<ButtonDetails>,
/// Left button of every screen
back_btn_details: Option<ButtonDetails<U>>,
back_btn_details: Option<ButtonDetails>,
/// Right button of every screen apart the last one
next_btn_details: Option<ButtonDetails<U>>,
buttons: Child<ButtonController<U>>,
next_btn_details: Option<ButtonDetails>,
buttons: Child<ButtonController>,
}
impl<T, U> ButtonPage<T, U>
impl<T> ButtonPage<T>
where
T: Component + Paginate,
U: StringType + Clone,
{
pub fn new(content: T, background: Color) -> Self {
Self {
@ -45,7 +42,7 @@ where
content: Child::new(content),
pad: Pad::with_background(background).with_clear(),
cancel_btn_details: Some(ButtonDetails::cancel_icon()),
confirm_btn_details: Some(ButtonDetails::text(tr("buttons__confirm").into())),
confirm_btn_details: Some(ButtonDetails::text(TR::buttons__confirm.into())),
back_btn_details: Some(ButtonDetails::up_arrow_icon()),
next_btn_details: Some(ButtonDetails::down_arrow_icon_wide()),
// Setting empty layout for now, we do not yet know the page count.
@ -55,22 +52,22 @@ where
}
}
pub fn with_cancel_btn(mut self, btn_details: Option<ButtonDetails<U>>) -> Self {
pub fn with_cancel_btn(mut self, btn_details: Option<ButtonDetails>) -> Self {
self.cancel_btn_details = btn_details;
self
}
pub fn with_confirm_btn(mut self, btn_details: Option<ButtonDetails<U>>) -> Self {
pub fn with_confirm_btn(mut self, btn_details: Option<ButtonDetails>) -> Self {
self.confirm_btn_details = btn_details;
self
}
pub fn with_back_btn(mut self, btn_details: Option<ButtonDetails<U>>) -> Self {
pub fn with_back_btn(mut self, btn_details: Option<ButtonDetails>) -> Self {
self.back_btn_details = btn_details;
self
}
pub fn with_next_btn(mut self, btn_details: Option<ButtonDetails<U>>) -> Self {
pub fn with_next_btn(mut self, btn_details: Option<ButtonDetails>) -> Self {
self.next_btn_details = btn_details;
self
}
@ -119,14 +116,14 @@ where
});
}
fn get_button_layout(&self, has_prev: bool, has_next: bool) -> ButtonLayout<U> {
fn get_button_layout(&self, has_prev: bool, has_next: bool) -> ButtonLayout {
let btn_left = self.get_left_button_details(!has_prev);
let btn_right = self.get_right_button_details(has_next);
ButtonLayout::new(btn_left, None, btn_right)
}
/// Get the left button details, depending whether the page is first or not.
fn get_left_button_details(&self, is_first: bool) -> Option<ButtonDetails<U>> {
fn get_left_button_details(&self, is_first: bool) -> Option<ButtonDetails> {
if is_first {
self.cancel_btn_details.clone()
} else {
@ -136,7 +133,7 @@ where
/// Get the right button details, depending on whether there is a next
/// page.
fn get_right_button_details(&self, has_next_page: bool) -> Option<ButtonDetails<U>> {
fn get_right_button_details(&self, has_next_page: bool) -> Option<ButtonDetails> {
if has_next_page {
self.next_btn_details.clone()
} else {
@ -145,10 +142,9 @@ where
}
}
impl<T, U> ScrollableContent for ButtonPage<T, U>
impl<T> ScrollableContent for ButtonPage<T>
where
T: Component + Paginate,
U: StringType,
{
fn page_count(&self) -> usize {
self.page_count
@ -158,10 +154,9 @@ where
}
}
impl<T, U> Component for ButtonPage<T, U>
impl<T> Component for ButtonPage<T>
where
T: Component + Paginate,
U: StringType + Clone,
{
type Msg = PageMsg<T::Msg>;
@ -225,10 +220,9 @@ where
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for ButtonPage<T, U>
impl<T> crate::trace::Trace for ButtonPage<T>
where
T: crate::trace::Trace + Paginate + Component,
U: StringType + Clone,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("ButtonPage");

@ -30,7 +30,7 @@ where
result_anim: Child<ResultAnim>,
headline: Option<Label<&'static str>>,
text: Child<Paragraphs<Paragraph<T>>>,
buttons: Option<Child<ButtonController<T>>>,
buttons: Option<Child<ButtonController>>,
autoclose: bool,
}

@ -1,12 +1,12 @@
use crate::{
strutil::StringType,
translations::TR,
ui::{
component::{
text::util::text_multiline, Child, Component, Event, EventCtx, Never, Paginate,
},
display::Font,
geometry::{Alignment, Offset, Rect},
translations::tr,
},
};
@ -70,19 +70,24 @@ where
}
fn get_final_text(&self) -> String<50> {
build_string!(
50,
tr("share_words__wrote_down_all"),
inttostr!(self.share_words.len() as u8),
tr("share_words__words_in_order")
)
TR::share_words__wrote_down_all.map_translated(|wrote_down_all| {
TR::share_words__words_in_order.map_translated(|in_order| {
build_string!(
50,
wrote_down_all,
inttostr!(self.share_words.len() as u8),
in_order
)
})
})
}
/// Display the final page with user confirmation.
fn paint_final_page(&mut self) {
let final_text = self.get_final_text();
text_multiline(
self.area.split_top(INFO_TOP_OFFSET).1,
&self.get_final_text(),
final_text.as_str().into(),
Font::NORMAL,
theme::FG,
theme::BG,
@ -184,6 +189,6 @@ where
}
content
};
t.string("screen_content", &content);
t.string("screen_content", content.as_str().into());
}
}

@ -1,5 +1,5 @@
use crate::{
strutil::StringType,
strutil::{StringType, TString},
ui::{
component::{Child, Component, Event, EventCtx},
geometry::{Insets, Rect},
@ -14,20 +14,20 @@ pub enum CancelInfoConfirmMsg {
Confirmed,
}
pub struct ShowMore<T, U>
where
U: StringType,
{
pub struct ShowMore<T> {
content: Child<T>,
buttons: Child<ButtonController<U>>,
buttons: Child<ButtonController>,
}
impl<T, U> ShowMore<T, U>
impl<T> ShowMore<T>
where
T: Component,
U: StringType + Clone,
{
pub fn new(content: T, cancel_button: Option<U>, button: U) -> Self {
pub fn new(
content: T,
cancel_button: Option<TString<'static>>,
button: TString<'static>,
) -> Self {
let btn_layout = if let Some(cancel_text) = cancel_button {
ButtonLayout::text_armed_info(cancel_text, button)
} else {
@ -40,10 +40,9 @@ where
}
}
impl<T, U> Component for ShowMore<T, U>
impl<T> Component for ShowMore<T>
where
T: Component,
U: StringType + Clone,
{
type Msg = CancelInfoConfirmMsg;
@ -83,10 +82,9 @@ where
// DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for ShowMore<T, U>
impl<T> crate::trace::Trace for ShowMore<T>
where
T: crate::trace::Trace + Component,
U: StringType + Clone,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("ShowMore");

@ -127,6 +127,6 @@ where
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Title");
t.string("text", self.title.as_ref());
t.string("text", self.title.as_ref().into());
}
}

@ -58,6 +58,6 @@ impl Component for WelcomeScreen {
impl crate::trace::Trace for WelcomeScreen {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("WelcomeScreen");
t.string("model_name", "Trezor Safe 3");
t.string("model_name", "Trezor Safe 3".into());
}
}

@ -16,7 +16,8 @@ use crate::{
qstr::Qstr,
util,
},
strutil::StringType,
strutil::{StringType, TString},
translations::TR,
trezorhal::model,
ui::{
component::{
@ -39,7 +40,6 @@ use crate::{
util::{upy_disable_animation, ConfirmBlob},
},
model_tr::component::check_homescreen_format,
translations::tr,
},
};
@ -63,10 +63,9 @@ impl From<CancelConfirmMsg> for Obj {
}
}
impl<T, U> ComponentMsgObj for ShowMore<T, U>
impl<T> ComponentMsgObj for ShowMore<T>
where
T: Component,
U: StringType + Clone,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
@ -86,10 +85,9 @@ where
}
}
impl<T, U> ComponentMsgObj for ButtonPage<T, U>
impl<T> ComponentMsgObj for ButtonPage<T>
where
T: Component + Paginate,
U: StringType + Clone,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
@ -141,10 +139,7 @@ where
}
}
impl<T> ComponentMsgObj for AddressDetails<T>
where
T: StringType + Clone,
{
impl ComponentMsgObj for AddressDetails {
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
Ok(CANCELLED.as_obj())
}
@ -159,19 +154,13 @@ where
}
}
impl<T> ComponentMsgObj for NumberInput<T>
where
T: StringType + Clone,
{
impl ComponentMsgObj for NumberInput {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
msg.try_into()
}
}
impl<T> ComponentMsgObj for SimpleChoice<T>
where
T: StringType + Clone,
{
impl ComponentMsgObj for SimpleChoice {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
if self.return_index {
msg.try_into()
@ -182,19 +171,13 @@ where
}
}
impl<T> ComponentMsgObj for WordlistEntry<T>
where
T: StringType + Clone,
{
impl ComponentMsgObj for WordlistEntry {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
msg.try_into()
}
}
impl<T> ComponentMsgObj for PassphraseEntry<T>
where
T: StringType + Clone,
{
impl ComponentMsgObj for PassphraseEntry {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
CancelConfirmMsg::Confirmed => self.passphrase().try_into(),
@ -263,9 +246,8 @@ where
}
}
impl<T, U> ComponentMsgObj for super::component::bl_confirm::Confirm<T, U>
impl<U> ComponentMsgObj for super::component::bl_confirm::Confirm<U>
where
T: StringType + Clone,
U: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
@ -283,8 +265,8 @@ where
fn content_in_button_page<T: Component + Paginate + MaybeTrace + 'static>(
title: StrBuffer,
content: T,
verb: StrBuffer,
verb_cancel: Option<StrBuffer>,
verb: TString<'static>,
verb_cancel: Option<TString<'static>>,
hold: bool,
) -> Result<Obj, Error> {
// Left button - icon, text or nothing.
@ -320,8 +302,9 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
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: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, tr("buttons__confirm").into())?;
let verb_cancel: Option<StrBuffer> = kwargs
let verb: TString<'static> =
kwargs.get_or(Qstr::MP_QSTR_verb, TR::buttons__confirm.into())?;
let verb_cancel: Option<TString<'static>> = kwargs
.get(Qstr::MP_QSTR_verb_cancel)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
@ -356,8 +339,9 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map
let description: Option<StrBuffer> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let extra: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, tr("buttons__confirm").into())?;
let verb_cancel: Option<StrBuffer> = kwargs
let verb: TString<'static> =
kwargs.get_or(Qstr::MP_QSTR_verb, TR::buttons__confirm.into())?;
let verb_cancel: Option<TString<'static>> = kwargs
.get(Qstr::MP_QSTR_verb_cancel)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
@ -426,7 +410,7 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
content_in_button_page(
title,
paragraphs.into_paragraphs(),
tr("buttons__confirm").into(),
TR::buttons__confirm.into(),
None,
hold,
)
@ -455,14 +439,14 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL)
.text_normal(tr("reset__by_continuing").into())
.text_normal(TR::reset__by_continuing.try_into()?)
.next_page()
.text_normal(tr("reset__more_info_at").into())
.text_normal(TR::reset__more_info_at.try_into()?)
.newline()
.text_bold(tr("reset__tos_link").into());
.text_bold(TR::reset__tos_link.try_into()?);
let formatted = FormattedText::new(ops).vertically_centered();
content_in_button_page(title, formatted, button, Some("".into()), false)
@ -472,26 +456,33 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], _kwargs: &Map| {
// cached allocated translations that get_page can reuse
let tr_title_success: StrBuffer = TR::words__title_success.try_into()?;
let tr_new_wallet_created: StrBuffer = TR::backup__new_wallet_created.try_into()?;
let tr_it_should_be_backed_up_now: StrBuffer =
TR::backup__it_should_be_backed_up_now.try_into()?;
let tr_title_backup_wallet: StrBuffer = TR::backup__title_backup_wallet.try_into()?;
let tr_recover_anytime: StrBuffer = TR::backup__recover_anytime.try_into()?;
let get_page = move |page_index| match page_index {
0 => {
let btn_layout = ButtonLayout::text_none_arrow_wide(tr("buttons__skip").into());
let btn_layout = ButtonLayout::text_none_arrow_wide(TR::buttons__skip.into());
let btn_actions = ButtonActions::cancel_none_next();
// TODO: add the second newline conditionally when it fits
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
.text_normal(tr("backup__new_wallet_created").into())
.text_normal(tr_new_wallet_created)
.newline()
.text_normal(tr("backup__it_should_be_backed_up_now").into());
.text_normal(tr_it_should_be_backed_up_now);
let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted)
.with_title(tr("words__title_success").into())
Page::new(btn_layout, btn_actions, formatted).with_title(tr_title_success)
}
1 => {
let btn_layout = ButtonLayout::up_arrow_none_text(tr("buttons__back_up").into());
let btn_layout = ButtonLayout::up_arrow_none_text(TR::buttons__back_up.into());
let btn_actions = ButtonActions::prev_none_confirm();
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
.text_normal(tr("backup__recover_anytime").into());
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(tr_recover_anytime);
let formatted = FormattedText::new(ops).vertically_centered();
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
.with_title(tr("backup__title_backup_wallet").into())
.with_title(tr_title_backup_wallet)
}
_ => unreachable!(),
};
@ -531,7 +522,7 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let value: StrBuffer = kwargs.get(Qstr::MP_QSTR_value)?.try_into()?;
let verb: Option<StrBuffer> = kwargs
let verb: Option<TString<'static>> = kwargs
.get(Qstr::MP_QSTR_verb)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
@ -545,7 +536,7 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma
content_in_button_page(
title,
paragraphs,
verb.unwrap_or_else(|| tr("buttons__confirm").into()),
verb.unwrap_or(TR::buttons__confirm.into()),
Some("".into()),
hold,
)
@ -559,16 +550,22 @@ extern "C" fn new_confirm_joint_total(n_args: usize, args: *const Obj, kwargs: *
let total_amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_amount)?.try_into()?;
let paragraphs = Paragraphs::new([
Paragraph::new(&theme::TEXT_BOLD, tr("joint__you_are_contributing").into()),
Paragraph::new(
&theme::TEXT_BOLD,
TR::joint__you_are_contributing.try_into()?,
),
Paragraph::new(&theme::TEXT_MONO, spending_amount),
Paragraph::new(&theme::TEXT_BOLD, tr("joint__to_the_total_amount").into()),
Paragraph::new(
&theme::TEXT_BOLD,
TR::joint__to_the_total_amount.try_into()?,
),
Paragraph::new(&theme::TEXT_MONO, total_amount),
]);
content_in_button_page(
tr("joint__title").into(),
TR::joint__title.try_into()?,
paragraphs,
tr("buttons__hold_to_confirm").into(),
TR::buttons__hold_to_confirm.into(),
Some("".into()),
true,
)
@ -583,22 +580,22 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs:
let amount_new: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_new)?.try_into()?;
let description = if sign < 0 {
tr("modify_amount__decrease_amount")
TR::modify_amount__decrease_amount
} else {
tr("modify_amount__increase_amount")
TR::modify_amount__increase_amount
};
let paragraphs = Paragraphs::new([
Paragraph::new(&theme::TEXT_NORMAL, description.into()),
Paragraph::new(&theme::TEXT_NORMAL, description.try_into()?),
Paragraph::new(&theme::TEXT_MONO, amount_change).break_after(),
Paragraph::new(&theme::TEXT_BOLD, tr("modify_amount__new_amount").into()),
Paragraph::new(&theme::TEXT_BOLD, TR::modify_amount__new_amount.try_into()?),
Paragraph::new(&theme::TEXT_MONO, amount_new),
]);
content_in_button_page(
tr("modify_amount__title").into(),
TR::modify_amount__title.try_into()?,
paragraphs,
tr("buttons__confirm").into(),
TR::buttons__confirm.into(),
Some("".into()),
false,
)
@ -616,7 +613,7 @@ extern "C" fn new_confirm_output_address(n_args: usize, args: *const Obj, kwargs
let get_page = move |page_index| {
assert!(page_index == 0);
// RECIPIENT + address
let btn_layout = ButtonLayout::cancel_none_text(tr("buttons__continue").into());
let btn_layout = ButtonLayout::cancel_none_text(TR::buttons__continue.into());
let btn_actions = ButtonActions::cancel_none_confirm();
// Not putting hyphens in the address.
// Potentially adding address label in different font.
@ -654,7 +651,7 @@ extern "C" fn new_confirm_output_amount(n_args: usize, args: *const Obj, kwargs:
let get_page = move |page_index| {
assert!(page_index == 0);
// AMOUNT + amount
let btn_layout = ButtonLayout::up_arrow_none_text(tr("buttons__confirm").into());
let btn_layout = ButtonLayout::up_arrow_none_text(TR::buttons__confirm.into());
let btn_actions = ButtonActions::cancel_none_confirm();
let ops = OpTextLayout::new(theme::TEXT_MONO).text_mono(amount.clone());
let formatted = FormattedText::new(ops).vertically_centered();
@ -680,11 +677,17 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
let total_label: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_label)?.try_into()?;
let fee_label: StrBuffer = kwargs.get(Qstr::MP_QSTR_fee_label)?.try_into()?;
// cached allocated translated strings that get_page can reuse
let tr_title_fee = TR::confirm_total__title_fee.try_into()?;
let tr_fee_rate = TR::confirm_total__fee_rate.try_into()?;
let tr_title_sending_from = TR::confirm_total__title_sending_from.try_into()?;
let tr_account = TR::words__account_colon.try_into()?;
let get_page = move |page_index| {
match page_index {
0 => {
// Total amount + fee
let btn_layout = ButtonLayout::cancel_armed_info(tr("buttons__confirm").into());
let btn_layout = ButtonLayout::cancel_armed_info(TR::buttons__confirm.into());
let btn_actions = ButtonActions::cancel_confirm_next();
let ops = OpTextLayout::new(theme::TEXT_MONO)
@ -708,11 +711,11 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
let fee_rate_amount = fee_rate_amount.clone().unwrap_or_default();
let ops = OpTextLayout::new(theme::TEXT_MONO)
.text_bold(tr("confirm_total__title_fee").into())
.text_bold(tr_title_fee)
.newline()
.newline()
.newline_half()
.text_bold(tr("confirm_total__fee_rate").into())
.text_bold(tr_fee_rate)
.newline()
.text_mono(fee_rate_amount);
@ -729,11 +732,11 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
// TODO: include wallet info when available
let ops = OpTextLayout::new(theme::TEXT_MONO)
.text_bold(tr("confirm_total__title_sending_from").into())
.text_bold(tr_title_sending_from)
.newline()
.newline()
.newline_half()
.text_bold(tr("words__account_colon").into())
.text_bold(tr_account)
.newline()
.text_mono(account_label);
@ -760,14 +763,17 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m
let cancel_cross: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_cross, false)?;
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
// cached allocated translated strings that get_page can reuse
let tr_title_fee = TR::confirm_total__title_fee.try_into()?;
let get_page = move |page_index| {
match page_index {
0 => {
// Amount + fee
let btn_layout = if cancel_cross {
ButtonLayout::cancel_armed_info(tr("buttons__confirm").into())
ButtonLayout::cancel_armed_info(TR::buttons__confirm.into())
} else {
ButtonLayout::up_arrow_armed_info(tr("buttons__confirm").into())
ButtonLayout::up_arrow_armed_info(TR::buttons__confirm.into())
};
let btn_actions = ButtonActions::cancel_confirm_next();
@ -803,7 +809,7 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m
let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted)
.with_title(tr("confirm_total__title_fee").into())
.with_title(tr_title_fee)
.with_slim_arrows()
}
_ => unreachable!(),
@ -821,7 +827,8 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_data)?.try_into()?;
let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, tr("buttons__confirm").into())?;
let verb: TString<'static> =
kwargs.get_or(Qstr::MP_QSTR_verb, TR::buttons__confirm.into())?;
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
let get_page = move |page_index| {
@ -850,9 +857,9 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
/// General pattern of most tutorial screens.
/// (title, text, btn_layout, btn_actions, text_y_offset)
fn tutorial_screen(
title: &'static str,
text: &'static str,
btn_layout: ButtonLayout<StrBuffer>,
title: StrBuffer,
text: StrBuffer,
btn_layout: ButtonLayout,
btn_actions: ButtonActions,
) -> Page<StrBuffer> {
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL).text_normal(text.into());
@ -864,7 +871,23 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
let block = |_args: &[Obj], _kwargs: &Map| {
const PAGE_COUNT: usize = 7;
let get_page = |page_index| {
// cached allocated translated strings that get_page can reuse
let tr_title_hello: StrBuffer = TR::tutorial__title_hello.try_into()?;
let tr_welcome_press_right: StrBuffer = TR::tutorial__welcome_press_right.try_into()?;
let tr_use_trezor: StrBuffer = TR::tutorial__use_trezor.try_into()?;
let tr_hold_to_confirm: StrBuffer = TR::buttons__hold_to_confirm.try_into()?;
let tr_press_and_hold: StrBuffer = TR::tutorial__press_and_hold.try_into()?;
let tr_title_screen_scroll: StrBuffer = TR::tutorial__title_screen_scroll.try_into()?;
let tr_scroll_down: StrBuffer = TR::tutorial__scroll_down.try_into()?;
let tr_confirm: StrBuffer = TR::buttons__confirm.try_into()?;
let tr_middle_click: StrBuffer = TR::tutorial__middle_click.try_into()?;
let tr_title_tutorial_complete: StrBuffer =
TR::tutorial__title_tutorial_complete.try_into()?;
let tr_ready_to_use: StrBuffer = TR::tutorial__ready_to_use.try_into()?;
let tr_title_skip: StrBuffer = TR::tutorial__title_skip.try_into()?;
let tr_sure_you_want_skip: StrBuffer = TR::tutorial__sure_you_want_skip.try_into()?;
let get_page = move |page_index| {
// Lazy-loaded list of screens to show, with custom content,
// buttons and actions triggered by these buttons.
// Cancelling the first screen will point to the last one,
@ -873,48 +896,48 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
match page_index {
// title, text, btn_layout, btn_actions
0 => tutorial_screen(
tr("tutorial__title_hello"),
tr("tutorial__welcome_press_right"),
tr_title_hello,
tr_welcome_press_right,
ButtonLayout::cancel_none_arrow(),
ButtonActions::last_none_next(),
),
1 => tutorial_screen(
"",
tr("tutorial__use_trezor"),
"".into(),
tr_use_trezor,
ButtonLayout::arrow_none_arrow(),
ButtonActions::prev_none_next(),
),
2 => tutorial_screen(
tr("buttons__hold_to_confirm"),
tr("tutorial__press_and_hold"),
ButtonLayout::arrow_none_htc(tr("buttons__hold_to_confirm").into()),
tr_hold_to_confirm,
tr_press_and_hold,
ButtonLayout::arrow_none_htc(TR::buttons__hold_to_confirm.into()),
ButtonActions::prev_none_next(),
),
3 => tutorial_screen(
tr("tutorial__title_screen_scroll"),
tr("tutorial__scroll_down"),
ButtonLayout::arrow_none_text(tr("buttons__continue").into()),
tr_title_screen_scroll,
tr_scroll_down,
ButtonLayout::arrow_none_text(TR::buttons__continue.into()),
ButtonActions::prev_none_next(),
),
4 => tutorial_screen(
tr("buttons__confirm"),
tr("tutorial__middle_click"),
ButtonLayout::none_armed_none(tr("buttons__confirm").into()),
tr_confirm,
tr_middle_click,
ButtonLayout::none_armed_none(TR::buttons__confirm.into()),
ButtonActions::none_next_none(),
),
5 => tutorial_screen(
tr("tutorial__title_tutorial_complete"),
tr("tutorial__ready_to_use"),
tr_title_tutorial_complete,
tr_ready_to_use,
ButtonLayout::text_none_text(
tr("buttons__again").into(),
tr("buttons__continue").into(),
TR::buttons__again.into(),
TR::buttons__continue.into(),
),
ButtonActions::beginning_none_confirm(),
),
6 => tutorial_screen(
tr("tutorial__title_skip"),
tr("tutorial__sure_you_want_skip"),
ButtonLayout::arrow_none_text(tr("buttons__skip").into()),
tr_title_skip,
tr_sure_you_want_skip,
ButtonLayout::arrow_none_text(TR::buttons__skip.into()),
ButtonActions::beginning_none_cancel(),
),
_ => unreachable!(),
@ -946,33 +969,37 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
.try_into_option()?;
let (description, change) = match sign {
s if s < 0 => (tr("modify_fee__decrease_fee"), user_fee_change),
s if s > 0 => (tr("modify_fee__increase_fee"), user_fee_change),
_ => (tr("modify_fee__no_change"), StrBuffer::empty()),
s if s < 0 => (TR::modify_fee__decrease_fee, user_fee_change),
s if s > 0 => (TR::modify_fee__increase_fee, user_fee_change),
_ => (TR::modify_fee__no_change, StrBuffer::empty()),
};
let mut paragraphs_vec = ParagraphVecShort::new();
paragraphs_vec
.add(Paragraph::new(&theme::TEXT_BOLD, description.into()))
.add(Paragraph::new(&theme::TEXT_BOLD, description.try_into()?))
.add(Paragraph::new(&theme::TEXT_MONO, change))
.add(
Paragraph::new(&theme::TEXT_BOLD, tr("modify_fee__transaction_fee").into())
.no_break(),
Paragraph::new(
&theme::TEXT_BOLD,
TR::modify_fee__transaction_fee.try_into()?,
)
.no_break(),
)
.add(Paragraph::new(&theme::TEXT_MONO, total_fee_new));
if let Some(fee_rate_amount) = fee_rate_amount {
paragraphs_vec
.add(
Paragraph::new(&theme::TEXT_BOLD, tr("modify_fee__fee_rate").into()).no_break(),
Paragraph::new(&theme::TEXT_BOLD, TR::modify_fee__fee_rate.try_into()?)
.no_break(),
)
.add(Paragraph::new(&theme::TEXT_MONO, fee_rate_amount));
}
content_in_button_page(
tr("modify_fee__title").into(),
TR::modify_fee__title.try_into()?,
paragraphs_vec.into_paragraphs(),
tr("buttons__confirm").into(),
TR::buttons__confirm.into(),
Some("".into()),
false,
)
@ -983,7 +1010,7 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
extern "C" fn new_multiple_pages_texts(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let verb: StrBuffer = kwargs.get(Qstr::MP_QSTR_verb)?.try_into()?;
let verb: TString<'static> = kwargs.get(Qstr::MP_QSTR_verb)?.try_into()?;
let items: Gc<List> = kwargs.get(Qstr::MP_QSTR_items)?.try_into()?;
// Cache the page count so that we can move `items` into the closure.
@ -1054,25 +1081,25 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
let (btn_layout, btn_actions) = if page_count == 1 {
// There is only one page
(
ButtonLayout::cancel_none_text(tr("buttons__confirm").into()),
ButtonLayout::cancel_none_text(TR::buttons__confirm.into()),
ButtonActions::cancel_none_confirm(),
)
} else if page_index == 0 {
// First page
(
ButtonLayout::cancel_armed_arrow(tr("buttons__select").into()),
ButtonLayout::cancel_armed_arrow(TR::buttons__select.into()),
ButtonActions::cancel_confirm_next(),
)
} else if page_index == page_count - 1 {
// Last page
(
ButtonLayout::arrow_armed_none(tr("buttons__select").into()),
ButtonLayout::arrow_armed_none(TR::buttons__select.into()),
ButtonActions::prev_confirm_none(),
)
} else {
// Page in the middle
(
ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()),
ButtonLayout::arrow_armed_arrow(TR::buttons__select.into()),
ButtonActions::prev_confirm_next(),
)
};
@ -1101,7 +1128,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let warning: StrBuffer = kwargs.get(Qstr::MP_QSTR_warning)?.try_into()?;
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
@ -1156,7 +1183,7 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -
extern "C" fn new_show_passphrase() -> Obj {
let block = move || {
let text: StrBuffer = tr("passphrase__please_enter").into();
let text: StrBuffer = TR::passphrase__please_enter.try_into()?;
let paragraph = Paragraph::new(&theme::TEXT_NORMAL, text).centered();
let content = Paragraphs::new([paragraph]);
let obj = LayoutObj::new(content)?;
@ -1168,18 +1195,23 @@ extern "C" fn new_show_passphrase() -> Obj {
extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
// cached allocated translated strings that get_page can reuse
let tr_contact_support_at = TR::addr_mismatch__contact_support_at.try_into()?;
let tr_support_url = TR::addr_mismatch__support_url.try_into()?;
let get_page = move |page_index| {
assert!(page_index == 0);
let btn_layout = ButtonLayout::arrow_none_text(tr("buttons__quit").into());
let btn_layout = ButtonLayout::arrow_none_text(TR::buttons__quit.into());
let btn_actions = ButtonActions::cancel_none_confirm();
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL)
.text_bold(title.clone())
.newline()
.newline_half()
.text_normal(tr("addr_mismatch__contact_support_at").into())
.text_normal(tr_contact_support_at)
.newline()
.text_bold(tr("addr_mismatch__support_url").into());
.text_bold(tr_support_url);
let formatted = FormattedText::new(ops);
Page::new(btn_layout, btn_actions, formatted)
};
@ -1194,8 +1226,8 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma
extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let verb_cancel: Option<StrBuffer> = kwargs
let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let verb_cancel: Option<TString<'static>> = kwargs
.get(Qstr::MP_QSTR_verb_cancel)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
@ -1215,7 +1247,7 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
let obj = LayoutObj::new(Frame::new(
title,
ShowMore::<Paragraphs<ParagraphVecShort<StrBuffer>>, StrBuffer>::new(
ShowMore::<Paragraphs<ParagraphVecShort<StrBuffer>>>::new(
paragraphs.into_paragraphs(),
verb_cancel,
button,
@ -1229,7 +1261,7 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
let mut paragraphs = ParagraphVecLong::new();
@ -1259,19 +1291,19 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
// Decreasing bottom padding between paragraphs to fit one screen
let paragraphs = Paragraphs::new([
Paragraph::new(&theme::TEXT_BOLD, tr("coinjoin__max_rounds").into())
Paragraph::new(&theme::TEXT_BOLD, TR::coinjoin__max_rounds.try_into()?)
.with_bottom_padding(2),
Paragraph::new(&theme::TEXT_MONO, max_rounds),
Paragraph::new(&theme::TEXT_BOLD, tr("coinjoin__max_mining_fee").into())
Paragraph::new(&theme::TEXT_BOLD, TR::coinjoin__max_mining_fee.try_into()?)
.with_bottom_padding(2)
.no_break(),
Paragraph::new(&theme::TEXT_MONO, max_feerate).with_bottom_padding(2),
]);
content_in_button_page(
tr("coinjoin__title").into(),
TR::coinjoin__title.try_into()?,
paragraphs,
tr("buttons__hold_to_confirm").into(),
TR::buttons__hold_to_confirm.into(),
None,
true,
)
@ -1295,9 +1327,7 @@ extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *m
let block = |_args: &[Obj], kwargs: &Map| {
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let obj = LayoutObj::new(
Frame::new(prompt, PassphraseEntry::<StrBuffer>::new()).with_title_centered(),
)?;
let obj = LayoutObj::new(Frame::new(prompt, PassphraseEntry::new()).with_title_centered())?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1308,8 +1338,7 @@ extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Ma
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let obj = LayoutObj::new(
Frame::new(prompt, WordlistEntry::<StrBuffer>::new(WordlistType::Bip39))
.with_title_centered(),
Frame::new(prompt, WordlistEntry::new(WordlistType::Bip39)).with_title_centered(),
)?;
Ok(obj.into())
};
@ -1321,11 +1350,7 @@ extern "C" fn new_request_slip39(n_args: usize, args: *const Obj, kwargs: *mut M
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let obj = LayoutObj::new(
Frame::new(
prompt,
WordlistEntry::<StrBuffer>::new(WordlistType::Slip39),
)
.with_title_centered(),
Frame::new(prompt, WordlistEntry::new(WordlistType::Slip39)).with_title_centered(),
)?;
Ok(obj.into())
};
@ -1338,7 +1363,7 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
// There are only 3 words, but SimpleChoice requires 5 elements
let words: Vec<StrBuffer, 5> = iter_into_vec(words_iterable)?;
let words: Vec<TString<'static>, 5> = util::iter_into_vec(words_iterable)?;
// Returning the index of the selected word, not the word itself
let obj = LayoutObj::new(
@ -1358,13 +1383,11 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], kwargs: &Map| {
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?;
let share_words: Vec<StrBuffer, 33> = iter_into_vec(share_words_obj)?;
let share_words: Vec<StrBuffer, 33> = util::iter_into_vec(share_words_obj)?;
let cancel_btn = Some(ButtonDetails::up_arrow_icon());
let confirm_btn = Some(
ButtonDetails::<StrBuffer>::text(tr("buttons__hold_to_confirm").into())
.with_default_duration(),
);
let confirm_btn =
Some(ButtonDetails::text(TR::buttons__hold_to_confirm.into()).with_default_duration());
let obj = LayoutObj::new(
ButtonPage::new(ShareWords::new(share_words), theme::BG)
@ -1384,11 +1407,7 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M
let count: u32 = kwargs.get(Qstr::MP_QSTR_count)?.try_into()?;
let obj = LayoutObj::new(
Frame::new(
title,
NumberInput::<StrBuffer>::new(min_count, max_count, count),
)
.with_title_centered(),
Frame::new(title, NumberInput::new(min_count, max_count, count)).with_title_centered(),
)?;
Ok(obj.into())
};
@ -1397,7 +1416,7 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M
extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let active: usize = kwargs.get(Qstr::MP_QSTR_active)?.try_into()?;
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
@ -1438,7 +1457,7 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?;
let show_info: bool = kwargs.get(Qstr::MP_QSTR_show_info)?.try_into()?;
@ -1448,22 +1467,22 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
paragraphs
.add(Paragraph::new(
&theme::TEXT_NORMAL,
tr("recovery__only_first_n_letters").into(),
TR::recovery__only_first_n_letters.try_into()?,
))
.add(Paragraph::new(
&theme::TEXT_NORMAL,
tr("recovery__cursor_will_change").into(),
TR::recovery__cursor_will_change.try_into()?,
));
}
let title = if dry_run {
tr("recovery__title_dry_run")
TR::recovery__title_dry_run
} else {
tr("recovery__title")
TR::recovery__title
};
content_in_button_page(
title.into(),
title.try_into()?,
paragraphs.into_paragraphs(),
button,
Some("".into()),
@ -1475,9 +1494,9 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], _kwargs: &Map| {
let title: StrBuffer = tr("word_count__title").into();
let title: StrBuffer = TR::word_count__title.try_into()?;
let choices: Vec<StrBuffer, 5> = ["12", "18", "20", "24", "33"]
let choices: Vec<TString<'static>, 5> = ["12", "18", "20", "24", "33"]
.map(|num| num.into())
.into_iter()
.collect();
@ -1511,7 +1530,7 @@ extern "C" fn new_show_group_share_success(
content_in_button_page(
"".into(),
paragraphs,
tr("buttons__continue").into(),
TR::buttons__continue.into(),
None,
false,
)
@ -1593,7 +1612,7 @@ extern "C" fn new_show_lockscreen(n_args: usize, args: *const Obj, kwargs: *mut
let coinjoin_authorized: bool = kwargs.get_or(Qstr::MP_QSTR_coinjoin_authorized, false)?;
let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?;
let obj = LayoutObj::new(Lockscreen::new(label, bootscreen, coinjoin_authorized))?;
let obj = LayoutObj::new(Lockscreen::new(label, bootscreen, coinjoin_authorized)?)?;
if skip_first_paint {
obj.skip_first_paint();
}
@ -1612,7 +1631,7 @@ extern "C" fn new_confirm_firmware_update(
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?;
let title = tr("firmware_update__title");
let title = TR::firmware_update__title.try_into()?;
let message = Label::left_aligned(description, theme::TEXT_NORMAL).vertically_centered();
let fingerprint = Label::left_aligned(
fingerprint,
@ -1621,18 +1640,8 @@ extern "C" fn new_confirm_firmware_update(
.vertically_centered();
let obj = LayoutObj::new(
Confirm::new(
theme::BG,
title,
message,
None,
tr("buttons__install").into(),
false,
)
.with_info_screen(
StrBuffer::from(tr("firmware_update__title_fingerprint")),
fingerprint,
),
Confirm::new(theme::BG, title, message, None, TR::buttons__install, false)
.with_info_screen(TR::firmware_update__title_fingerprint, fingerprint),
)?;
Ok(obj.into())
};

@ -2,14 +2,15 @@ use heapless::Vec;
use crate::{
error::Error,
micropython::buffer::StrBuffer,
strutil::StringType,
translations::TR,
ui::{
component::{
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt},
Component, Event, EventCtx, Paginate, Qr,
},
geometry::Rect,
translations::tr,
},
};
@ -19,7 +20,7 @@ const MAX_XPUBS: usize = 16;
pub struct AddressDetails<T> {
qr_code: Frame<Qr, T>,
details: Frame<Paragraphs<ParagraphVecShort<T>>, T>,
details: Frame<Paragraphs<ParagraphVecShort<StrBuffer>>, T>,
xpub_view: Frame<Paragraphs<Paragraph<T>>, T>,
xpubs: Vec<(T, T), MAX_XPUBS>,
xpub_page_count: Vec<u8, MAX_XPUBS>,
@ -35,8 +36,8 @@ where
qr_address: T,
case_sensitive: bool,
details_title: T,
account: Option<T>,
path: Option<T>,
account: Option<StrBuffer>,
path: Option<StrBuffer>,
) -> Result<Self, Error>
where
T: From<&'static str>,
@ -45,14 +46,14 @@ where
if let Some(a) = account {
para.add(Paragraph::new(
&theme::TEXT_NORMAL,
tr("words__account_colon").into(),
TR::words__account_colon.try_into()?,
));
para.add(Paragraph::new(&theme::TEXT_MONO, a));
}
if let Some(p) = path {
para.add(Paragraph::new(
&theme::TEXT_NORMAL,
tr("address_details__derivation_path").into(),
TR::address_details__derivation_path.try_into()?,
));
para.add(Paragraph::new(&theme::TEXT_MONO, p));
}

@ -50,8 +50,8 @@ pub struct Confirm<T> {
title: ConfirmTitle<T>,
message: Child<Label<T>>,
alert: Option<Child<Label<T>>>,
left_button: Child<Button<&'static str>>,
right_button: Child<Button<&'static str>>,
left_button: Child<Button<T>>,
right_button: Child<Button<T>>,
info: Option<ConfirmInfo<T>>,
show_info: bool,
}
@ -62,8 +62,8 @@ where
{
pub fn new(
bg_color: Color,
left_button: Button<&'static str>,
right_button: Button<&'static str>,
left_button: Button<T>,
right_button: Button<T>,
title: ConfirmTitle<T>,
message: Label<T>,
) -> Self {

@ -329,10 +329,10 @@ where
t.component("Button");
match &self.content {
ButtonContent::Empty => {}
ButtonContent::Text(text) => t.string("text", text.as_ref()),
ButtonContent::Text(text) => t.string("text", text.as_ref().into()),
ButtonContent::Icon(_) => t.bool("icon", true),
ButtonContent::IconAndText(content) => {
t.string("text", content.text);
t.string("text", content.text.into());
t.bool("icon", true);
}
ButtonContent::IconBlend(_, _, _) => t.bool("icon", true),

@ -1,7 +1,10 @@
use core::mem;
use crate::{
error::Error,
maybe_trace::MaybeTrace,
micropython::buffer::StrBuffer,
translations::TR,
ui::{
component::{
base::Never, painter, Child, Component, ComponentExt, Empty, Event, EventCtx, Label,
@ -9,7 +12,6 @@ use crate::{
},
display::loader::{loader_circular_uncompress, LoaderDimensions},
geometry::{Insets, Rect},
translations::tr,
util::animation_disabled,
},
};
@ -26,7 +28,7 @@ const LOADER_SPEED: u16 = 5;
pub struct CoinJoinProgress<T, U> {
value: u16,
indeterminate: bool,
content: Child<Frame<Split<Empty, U>, &'static str>>,
content: Child<Frame<Split<Empty, U>, StrBuffer>>,
// Label is not a child since circular loader paints large black rectangle which overlaps it.
// To work around this, draw label every time loader is drawn.
label: Label<T>,
@ -39,13 +41,16 @@ where
pub fn new(
text: T,
indeterminate: bool,
) -> CoinJoinProgress<T, impl Component<Msg = Never> + MaybeTrace>
) -> Result<CoinJoinProgress<T, impl Component<Msg = Never> + MaybeTrace>, Error>
where
T: AsRef<str>,
{
let style = theme::label_coinjoin_progress();
let label =
Label::centered(tr("coinjoin__title_do_not_disconnect"), style).vertically_centered();
let label = Label::centered(
TryInto::<StrBuffer>::try_into(TR::coinjoin__title_do_not_disconnect)?,
style,
)
.vertically_centered();
let bg = painter::rect_painter(style.background_color, theme::BG);
let inner = (bg, label);
CoinJoinProgress::with_background(text, inner, indeterminate)
@ -57,18 +62,18 @@ where
T: AsRef<str>,
U: Component<Msg = Never>,
{
pub fn with_background(text: T, inner: U, indeterminate: bool) -> Self {
Self {
pub fn with_background(text: T, inner: U, indeterminate: bool) -> Result<Self, Error> {
Ok(Self {
value: 0,
indeterminate,
content: Frame::centered(
theme::label_title(),
tr("coinjoin__title_progress"),
TR::coinjoin__title_progress.try_into()?,
Split::bottom(RECTANGLE_HEIGHT, 0, Empty, inner),
)
.into_child(),
label: Label::centered(text, theme::TEXT_NORMAL),
}
})
}
}

@ -1,7 +1,9 @@
mod render;
use crate::{
strutil::TString,
time::{Duration, Instant},
translations::TR,
trezorhal::usb::usb_configured,
ui::{
component::{Component, Event, EventCtx, Pad, TimerToken},
@ -10,7 +12,6 @@ use crate::{
geometry::{Offset, Point, Rect},
layout::util::get_user_custom_image,
model_tt::{constant, theme::IMAGE_HOMESCREEN},
translations::tr,
},
};
@ -45,9 +46,9 @@ const LOADER_OFFSET: Offset = Offset::y(-10);
const LOADER_DELAY: Duration = Duration::from_millis(500);
const LOADER_DURATION: Duration = Duration::from_millis(2000);
pub struct Homescreen<T> {
label: T,
notification: Option<(T, u8)>,
pub struct Homescreen {
label: TString<'static>,
notification: Option<(TString<'static>, u8)>,
hold_to_lock: bool,
loader: Loader,
pad: Pad,
@ -59,11 +60,12 @@ pub enum HomescreenMsg {
Dismissed,
}
impl<T> Homescreen<T>
where
T: AsRef<str>,
{
pub fn new(label: T, notification: Option<(T, u8)>, hold_to_lock: bool) -> Self {
impl Homescreen {
pub fn new(
label: TString<'static>,
notification: Option<(TString<'static>, u8)>,
hold_to_lock: bool,
) -> Self {
Self {
label,
notification,
@ -88,14 +90,14 @@ where
if !usb_configured() {
let (color, icon) = Self::level_to_style(0);
Some(HomescreenNotification {
text: tr("homescreen__title_no_usb_connection"),
text: TR::homescreen__title_no_usb_connection.into(),
icon,
color,
})
} else if let Some((notification, level)) = &self.notification {
let (color, icon) = Self::level_to_style(*level);
} else if let Some((notification, level)) = self.notification {
let (color, icon) = Self::level_to_style(level);
Some(HomescreenNotification {
text: notification.as_ref(),
text: notification,
icon,
color,
})
@ -105,13 +107,15 @@ where
}
fn paint_loader(&mut self) {
display::text_center(
TOP_CENTER + Offset::y(HOLD_Y),
tr("homescreen__title_hold_to_lock"),
Font::BOLD,
theme::FG,
theme::BG,
);
TR::homescreen__title_hold_to_lock.map_translated(|t| {
display::text_center(
TOP_CENTER + Offset::y(HOLD_Y),
t,
Font::BOLD,
theme::FG,
theme::BG,
)
});
self.loader.paint()
}
@ -171,10 +175,7 @@ where
}
}
impl<T> Component for Homescreen<T>
where
T: AsRef<str>,
{
impl Component for Homescreen {
type Msg = HomescreenMsg;
fn place(&mut self, bounds: Rect) -> Rect {
@ -201,7 +202,7 @@ where
label_style.text_color = theme::FG;
let text = HomescreenText {
text: self.label.as_ref(),
text: self.label,
style: label_style,
offset: Offset::y(LABEL_Y),
icon: None,
@ -261,21 +262,21 @@ where
}
#[cfg(feature = "ui_debug")]
impl<T: AsRef<str>> crate::trace::Trace for Homescreen<T> {
impl crate::trace::Trace for Homescreen {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Homescreen");
t.string("label", self.label.as_ref());
t.string("label", self.label);
}
}
pub struct Lockscreen<T> {
label: T,
pub struct Lockscreen {
label: TString<'static>,
bootscreen: bool,
coinjoin_authorized: bool,
}
impl<T> Lockscreen<T> {
pub fn new(label: T, bootscreen: bool, coinjoin_authorized: bool) -> Self {
impl Lockscreen {
pub fn new(label: TString<'static>, bootscreen: bool, coinjoin_authorized: bool) -> Self {
Lockscreen {
label,
bootscreen,
@ -284,10 +285,7 @@ impl<T> Lockscreen<T> {
}
}
impl<T> Component for Lockscreen<T>
where
T: AsRef<str>,
{
impl Component for Lockscreen {
type Msg = HomescreenMsg;
fn place(&mut self, bounds: Rect) -> Rect {
@ -304,14 +302,11 @@ where
fn paint(&mut self) {
let (locked, tap) = if self.bootscreen {
(
tr("lockscreen__title_not_connected"),
tr("lockscreen__tap_to_connect"),
TR::lockscreen__title_not_connected,
TR::lockscreen__tap_to_connect,
)
} else {
(
tr("lockscreen__title_locked"),
tr("lockscreen__tap_to_unlock"),
)
(TR::lockscreen__title_locked, TR::lockscreen__tap_to_unlock)
};
let mut label_style = theme::TEXT_DEMIBOLD;
@ -319,25 +314,25 @@ where
let mut texts: &[HomescreenText] = &[
HomescreenText {
text: "",
text: "".into(),
style: theme::TEXT_NORMAL,
offset: Offset::new(2, COINJOIN_Y),
icon: Some(theme::ICON_COINJOIN),
},
HomescreenText {
text: locked,
text: locked.into(),
style: theme::TEXT_BOLD,
offset: Offset::y(LOCKED_Y),
icon: Some(theme::ICON_LOCK),
},
HomescreenText {
text: tap,
text: tap.into(),
style: theme::TEXT_NORMAL,
offset: Offset::y(TAP_Y),
icon: None,
},
HomescreenText {
text: self.label.as_ref(),
text: self.label,
style: label_style,
offset: Offset::y(LABEL_Y),
icon: None,
@ -406,7 +401,7 @@ fn is_image_toif(buffer: &[u8]) -> bool {
}
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Lockscreen<T> {
impl crate::trace::Trace for Lockscreen {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Lockscreen");
}

@ -1,4 +1,5 @@
use crate::{
strutil::TString,
trezorhal::{
buffers::{
BufferBlurring, BufferBlurringTotals, BufferJpeg, BufferLine16bpp, BufferLine4bpp,
@ -24,15 +25,15 @@ use crate::{
#[derive(Clone, Copy)]
pub struct HomescreenText<'a> {
pub text: &'a str,
pub text: TString<'a>,
pub style: TextStyle,
pub offset: Offset,
pub icon: Option<Icon>,
}
#[derive(Clone, Copy)]
pub struct HomescreenNotification<'a> {
pub text: &'a str,
pub struct HomescreenNotification {
pub text: TString<'static>,
pub icon: Icon,
pub color: Color,
}
@ -216,7 +217,9 @@ fn homescreen_position_text(
buffer: &mut BufferText,
icon_buffer: &mut [u8],
) -> HomescreenTextInfo {
let text_width = display::text_width(text.text, text.style.text_font.into());
let text_width = text
.text
.map(|t| display::text_width(t, text.style.text_font.into()));
let font_max_height = display::text_max_height(text.style.text_font.into());
let font_baseline = display::text_baseline(text.style.text_font.into());
let text_width_clamped = text_width.clamp(0, screen().width());
@ -253,7 +256,8 @@ fn homescreen_position_text(
None
};
display::text_into_buffer(text.text, text.style.text_font.into(), buffer, 0);
text.text
.map(|t| display::text_into_buffer(t, text.style.text_font.into(), buffer, 0));
HomescreenTextInfo {
text_area,

@ -385,6 +385,6 @@ impl Component for Input {
impl crate::trace::Trace for PassphraseKeyboard {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("PassphraseKeyboard");
t.string("passphrase", self.passphrase());
t.string("passphrase", self.passphrase().into());
}
}

@ -475,8 +475,8 @@ where
unwrap!(digits_order.push_str(text));
}
}
t.string("digits_order", &digits_order);
t.string("pin", self.textbox.inner().pin());
t.string("digits_order", digits_order.as_str().into());
t.string("pin", self.textbox.inner().pin().into());
t.bool("display_digits", self.textbox.inner().display_digits);
}
}

@ -1,5 +1,8 @@
use crate::{
error::Error,
micropython::buffer::StrBuffer,
strutil::{self, StringType},
translations::TR,
ui::{
component::{
base::ComponentExt,
@ -9,7 +12,6 @@ use crate::{
},
display::{self, Font},
geometry::{Grid, Insets, Offset, Rect},
translations::tr,
},
};
@ -29,8 +31,8 @@ where
input: Child<NumberInput>,
paragraphs: Child<Paragraphs<Paragraph<T>>>,
paragraphs_pad: Pad,
info_button: Child<Button<&'static str>>,
confirm_button: Child<Button<&'static str>>,
info_button: Child<Button<StrBuffer>>,
confirm_button: Child<Button<StrBuffer>>,
}
impl<T, F> NumberInputDialog<T, F>
@ -38,19 +40,19 @@ where
F: Fn(u32) -> T,
T: StringType,
{
pub fn new(min: u32, max: u32, init_value: u32, description_func: F) -> Self {
pub fn new(min: u32, max: u32, init_value: u32, description_func: F) -> Result<Self, Error> {
let text = description_func(init_value);
Self {
Ok(Self {
area: Rect::zero(),
description_func,
input: NumberInput::new(min, max, init_value).into_child(),
paragraphs: Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, text)).into_child(),
paragraphs_pad: Pad::with_background(theme::BG),
info_button: Button::with_text(tr("buttons__info")).into_child(),
confirm_button: Button::with_text(tr("buttons__continue"))
info_button: Button::with_text(TR::buttons__info.try_into()?).into_child(),
confirm_button: Button::with_text(TR::buttons__continue.try_into()?)
.styled(theme::button_confirm())
.into_child(),
}
})
}
fn update_text(&mut self, ctx: &mut EventCtx, value: u32) {

@ -1,13 +1,11 @@
use crate::{
time::Instant,
ui::{
error::Error, micropython::buffer::StrBuffer, time::Instant, translations::TR, ui::{
component::{paginated::PageMsg, Component, ComponentExt, Event, EventCtx, Pad, Paginate},
constant,
display::{self, Color},
geometry::{Insets, Rect},
translations::tr,
util::animation_disabled,
},
}
};
use super::{
@ -41,6 +39,19 @@ pub struct ButtonPage<T, U> {
fade: Option<u16>,
}
impl<T> ButtonPage<T, StrBuffer>
where
T: Paginate,
T: Component,
{
pub fn with_hold(mut self) -> Result<Self, Error> {
self.button_confirm = Button::with_text(TR::buttons__hold_to_confirm.try_into()?)
.styled(theme::button_confirm());
self.loader = Some(Loader::new());
Ok(self)
}
}
impl<T, U> ButtonPage<T, U>
where
T: Paginate,
@ -88,13 +99,6 @@ where
self
}
pub fn with_hold(mut self) -> Self {
self.button_confirm = Button::with_text(tr("buttons__hold_to_confirm").into())
.styled(theme::button_confirm());
self.loader = Some(Loader::new());
self
}
pub fn with_back_button(mut self) -> Self {
self.cancel_from_any_page = true;
self.button_prev = Button::with_icon(theme::ICON_BACK).initially_enabled(false);

@ -74,6 +74,6 @@ impl crate::trace::Trace for WelcomeScreen {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("WelcomeScreen");
#[cfg(not(feature = "bootloader"))]
t.string("model", model::FULL_NAME);
t.string("model", model::FULL_NAME.into());
}
}

@ -13,7 +13,8 @@ use crate::{
qstr::Qstr,
util,
},
strutil::StringType,
strutil::{StringType, TString},
translations::TR,
trezorhal::model,
ui::{
component::{
@ -40,7 +41,6 @@ use crate::{
util::{upy_disable_animation, ConfirmBlob, PropsList},
},
model_tt::component::check_homescreen_format,
translations::tr,
},
};
@ -280,10 +280,7 @@ where
}
}
impl<T> ComponentMsgObj for Homescreen<T>
where
T: AsRef<str>,
{
impl ComponentMsgObj for Homescreen {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()),
@ -291,10 +288,7 @@ where
}
}
impl<T> ComponentMsgObj for Lockscreen<T>
where
T: AsRef<str>,
{
impl ComponentMsgObj for Lockscreen {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()),
@ -408,7 +402,7 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
};
let mut page = if hold {
ButtonPage::new(paragraphs, theme::BG).with_hold()
ButtonPage::new(paragraphs, theme::BG).with_hold()?
} else {
ButtonPage::new(paragraphs, theme::BG).with_cancel_confirm(verb_cancel, verb)
};
@ -533,7 +527,7 @@ impl ConfirmBlobParams {
page = page.with_cancel_confirm(self.verb_cancel, Some(verb))
}
if self.hold {
page = page.with_hold()
page = page.with_hold()?
}
let mut frame = Frame::left_aligned(theme::label_title(), self.title, page);
if let Some(subtitle) = self.subtitle {
@ -578,7 +572,8 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let description: Option<StrBuffer> =
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, tr("buttons__confirm").into())?;
let verb: StrBuffer =
kwargs.get_or(Qstr::MP_QSTR_verb, TR::buttons__confirm.try_into()?)?;
let extra: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
let data: Obj = kwargs.get(Qstr::MP_QSTR_data)?;
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
@ -628,10 +623,10 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
&theme::TEXT_MONO,
)?;
let page: ButtonPage<_, StrBuffer> = if hold {
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?
} else {
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
.with_cancel_confirm(None, Some(tr("buttons__confirm").into()))
.with_cancel_confirm(None, Some(TR::buttons__confirm.try_into()?))
};
let obj = LayoutObj::new(Frame::left_aligned(theme::label_title(), title, page))?;
Ok(obj.into())
@ -664,7 +659,8 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
_ => return Err(value_error!("Invalid image.")),
};
let buttons = Button::cancel_confirm_text(None, Some(tr("buttons__change")));
let tr_change: StrBuffer = TR::buttons__change.try_into()?;
let buttons = Button::cancel_confirm_text(None, Some(tr_change));
let obj = LayoutObj::new(Frame::centered(
theme::label_title(),
title,
@ -681,21 +677,13 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let paragraphs = Paragraphs::new([
Paragraph::new(
&theme::TEXT_NORMAL,
StrBuffer::from(tr("reset__by_continuing")),
)
.with_bottom_padding(17), // simulating a carriage return
Paragraph::new(
&theme::TEXT_NORMAL,
StrBuffer::from(tr("reset__more_info_at")),
),
Paragraph::new(
&theme::TEXT_DEMIBOLD,
StrBuffer::from(tr("reset__tos_link")),
),
]);
let par_array: [Paragraph<StrBuffer>; 3] = [
Paragraph::new(&theme::TEXT_NORMAL, TR::reset__by_continuing.try_into()?)
.with_bottom_padding(17), // simulating a carriage return
Paragraph::new(&theme::TEXT_NORMAL, TR::reset__more_info_at.try_into()?),
Paragraph::new(&theme::TEXT_DEMIBOLD, TR::reset__tos_link.try_into()?),
];
let paragraphs = Paragraphs::new(par_array);
let buttons = Button::cancel_confirm(
Button::with_icon(theme::ICON_CANCEL),
Button::with_text(button).styled(theme::button_confirm()),
@ -822,7 +810,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value));
}
let mut page: ButtonPage<_, StrBuffer> =
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold();
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?;
if cancel_arrow {
page = page.with_cancel_arrow()
}
@ -846,23 +834,27 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs:
let amount_new: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_new)?.try_into()?;
let description = if sign < 0 {
tr("modify_amount__decrease_amount")
TR::modify_amount__decrease_amount.try_into()?
} else {
tr("modify_amount__increase_amount")
TR::modify_amount__increase_amount.try_into()?
};
let paragraphs = Paragraphs::new([
Paragraph::new(&theme::TEXT_NORMAL, description.into()),
Paragraph::new(&theme::TEXT_NORMAL, description),
Paragraph::new(&theme::TEXT_MONO, amount_change),
Paragraph::new(&theme::TEXT_NORMAL, tr("modify_amount__new_amount").into()),
Paragraph::new(
&theme::TEXT_NORMAL,
TR::modify_amount__new_amount.try_into()?,
),
Paragraph::new(&theme::TEXT_MONO, amount_new),
]);
let tr_title: StrBuffer = TR::modify_amount__title.try_into()?;
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
tr("modify_amount__title"),
tr_title,
ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG)
.with_cancel_confirm(Some("^".into()), Some(tr("buttons__continue").into())),
.with_cancel_confirm(Some("^".into()), Some(TR::buttons__continue.try_into()?)),
))?;
Ok(obj.into())
};
@ -878,26 +870,26 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
let (description, change, total_label) = match sign {
s if s < 0 => (
tr("modify_fee__decrease_fee"),
TR::modify_fee__decrease_fee.try_into()?,
user_fee_change,
tr("modify_fee__new_transaction_fee"),
TR::modify_fee__new_transaction_fee.try_into()?,
),
s if s > 0 => (
tr("modify_fee__increase_fee"),
TR::modify_fee__increase_fee.try_into()?,
user_fee_change,
tr("modify_fee__new_transaction_fee"),
TR::modify_fee__new_transaction_fee.try_into()?,
),
_ => (
tr("modify_fee__no_change"),
TR::modify_fee__no_change.try_into()?,
StrBuffer::empty(),
tr("modify_fee__transaction_fee"),
TR::modify_fee__transaction_fee.try_into()?,
),
};
let paragraphs = Paragraphs::new([
Paragraph::new(&theme::TEXT_NORMAL, description.into()),
Paragraph::new(&theme::TEXT_NORMAL, description),
Paragraph::new(&theme::TEXT_MONO, change),
Paragraph::new(&theme::TEXT_NORMAL, total_label.into()),
Paragraph::new(&theme::TEXT_NORMAL, total_label),
Paragraph::new(&theme::TEXT_MONO, total_fee_new),
]);
@ -906,7 +898,7 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
theme::label_title(),
title,
ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG)
.with_hold()
.with_hold()?
.with_swipe_left(),
)
.with_info_button(),
@ -924,7 +916,8 @@ fn new_show_modal(
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let value: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_value, StrBuffer::empty())?;
let description: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_description, StrBuffer::empty())?;
let button: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_button, tr("buttons__continue").into())?;
let button: StrBuffer =
kwargs.get_or(Qstr::MP_QSTR_button, TR::buttons__continue.try_into()?)?;
let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?;
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
@ -1018,7 +1011,8 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
let controls = Button::cancel_confirm(
Button::with_icon(theme::ICON_CANCEL),
Button::with_text(tr("buttons__confirm")).styled(theme::button_confirm()),
Button::<StrBuffer>::with_text(TR::buttons__confirm.try_into()?)
.styled(theme::button_confirm()),
true,
);
@ -1075,9 +1069,9 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -
extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let description: StrBuffer = tr("addr_mismatch__contact_support_at").into();
let url: StrBuffer = tr("addr_mismatch__support_url").into();
let button = tr("buttons__quit");
let description: StrBuffer = TR::addr_mismatch__contact_support_at.try_into()?;
let url: StrBuffer = TR::addr_mismatch__support_url.try_into()?;
let button: StrBuffer = TR::buttons__quit.try_into()?;
let icon = BlendedImage::new(
theme::IMAGE_BG_OCTAGON,
@ -1225,16 +1219,20 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
let max_feerate: StrBuffer = kwargs.get(Qstr::MP_QSTR_max_feerate)?.try_into()?;
let paragraphs = Paragraphs::new([
Paragraph::new(&theme::TEXT_NORMAL, tr("coinjoin__max_rounds").into()),
Paragraph::new(&theme::TEXT_NORMAL, TR::coinjoin__max_rounds.try_into()?),
Paragraph::new(&theme::TEXT_MONO, max_rounds),
Paragraph::new(&theme::TEXT_NORMAL, tr("coinjoin__max_mining_fee").into()),
Paragraph::new(
&theme::TEXT_NORMAL,
TR::coinjoin__max_mining_fee.try_into()?,
),
Paragraph::new(&theme::TEXT_MONO, max_feerate),
]);
let tr_title: StrBuffer = TR::coinjoin__title.try_into()?;
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
tr("coinjoin__title"),
ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG).with_hold(),
tr_title,
ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG).with_hold()?,
))?;
Ok(obj.into())
};
@ -1248,7 +1246,7 @@ extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map)
let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?;
let warning: bool = kwargs.get_or(Qstr::MP_QSTR_wrong_pin, false)?;
let warning = if warning {
Some(tr("pin__wrong_pin").into())
Some(TR::pin__wrong_pin.try_into()?)
} else {
None
};
@ -1319,7 +1317,7 @@ extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut
theme::label_title(),
title,
ButtonPage::<_, StrBuffer>::new(paragraphs.into_paragraphs(), theme::BG)
.with_hold()
.with_hold()?
.without_cancel(),
))?;
Ok(obj.into())
@ -1348,7 +1346,7 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
NumberInputDialog::new(min_count, max_count, count, callback),
NumberInputDialog::new(min_count, max_count, count, callback)?,
))?;
Ok(obj.into())
};
@ -1412,10 +1410,10 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
])
.with_spacing(theme::RECOVERY_SPACING);
let notification = if dry_run {
tr("recovery__title_dry_run")
let notification: StrBuffer = if dry_run {
TR::recovery__title_dry_run.try_into()?
} else {
tr("recovery__title")
TR::recovery__title.try_into()?
};
let obj = if info_button {
@ -1424,7 +1422,10 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
notification,
Dialog::new(
paragraphs,
Button::cancel_info_confirm(tr("buttons__continue"), tr("buttons__more_info")),
Button::<StrBuffer>::cancel_info_confirm(
TR::buttons__continue.try_into()?,
TR::buttons__more_info.try_into()?,
),
),
))?
} else {
@ -1442,15 +1443,15 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?;
let title = if dry_run {
tr("recovery__title_dry_run")
let title: StrBuffer = if dry_run {
TR::recovery__title_dry_run.try_into()?
} else {
tr("recovery__title")
TR::recovery__title.try_into()?
};
let paragraphs = Paragraphs::new(Paragraph::new(
let paragraphs = Paragraphs::new(Paragraph::<StrBuffer>::new(
&theme::TEXT_DEMIBOLD,
StrBuffer::from(tr("recovery__select_num_of_words")),
TR::recovery__select_num_of_words.try_into()?,
));
let obj = LayoutObj::new(Frame::left_aligned(
@ -1474,9 +1475,11 @@ extern "C" fn new_show_group_share_success(
let obj = LayoutObj::new(IconDialog::new_shares(
lines,
theme::button_bar(Button::with_text(tr("buttons__continue")).map(|msg| {
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
})),
theme::button_bar(
Button::<StrBuffer>::with_text(TR::buttons__continue.try_into()?).map(|msg| {
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
}),
),
))?;
Ok(obj.into())
};
@ -1495,11 +1498,12 @@ extern "C" fn new_show_remaining_shares(n_args: usize, args: *const Obj, kwargs:
.add(Paragraph::new(&theme::TEXT_NORMAL, description).break_after());
}
let tr_title: StrBuffer = TR::recovery__title_remaining_shares.try_into()?;
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
tr("recovery__title_remaining_shares"),
tr_title,
ButtonPage::<_, StrBuffer>::new(paragraphs.into_paragraphs(), theme::BG)
.with_cancel_confirm(None, Some(tr("buttons__continue").into()))
.with_cancel_confirm(None, Some(TR::buttons__continue.try_into()?))
.with_confirm_style(theme::button_default())
.without_cancel(),
))?;
@ -1537,7 +1541,7 @@ extern "C" fn new_show_progress_coinjoin(n_args: usize, args: *const Obj, kwargs
// The second type parameter is actually not used in `new()` but we need to
// provide it.
let progress = CoinJoinProgress::<_, Never>::new(title, indeterminate);
let progress = CoinJoinProgress::<_, Never>::new(title, indeterminate)?;
let obj = if time_ms > 0 && indeterminate {
let timeout = Timeout::new(time_ms);
LayoutObj::new((timeout, progress.map(|_msg| None)))?
@ -1554,11 +1558,11 @@ extern "C" fn new_show_progress_coinjoin(n_args: usize, args: *const Obj, kwargs
extern "C" fn new_show_homescreen(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let label: StrBuffer = kwargs
let label: TString<'static> = kwargs
.get(Qstr::MP_QSTR_label)?
.try_into_option()?
.unwrap_or_else(|| model::FULL_NAME.into());
let notification: Option<StrBuffer> =
let notification: Option<TString<'static>> =
kwargs.get(Qstr::MP_QSTR_notification)?.try_into_option()?;
let notification_level: u8 = kwargs.get_or(Qstr::MP_QSTR_notification_level, 0)?;
let hold: bool = kwargs.get(Qstr::MP_QSTR_hold)?.try_into()?;
@ -1576,7 +1580,7 @@ extern "C" fn new_show_homescreen(n_args: usize, args: *const Obj, kwargs: *mut
extern "C" fn new_show_lockscreen(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let label: StrBuffer = kwargs
let label: TString<'static> = kwargs
.get(Qstr::MP_QSTR_label)?
.try_into_option()?
.unwrap_or_else(|| model::FULL_NAME.into());
@ -1614,16 +1618,18 @@ extern "C" fn new_confirm_firmware_update(
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?;
let title_str = StrBuffer::from(tr("firmware_update__title"));
let title_str = TR::firmware_update__title.try_into()?;
let title = Label::left_aligned(title_str, theme::TEXT_BOLD).vertically_centered();
let msg = Label::left_aligned(description, theme::TEXT_NORMAL);
let left = Button::with_text(tr("buttons__cancel")).styled(theme::button_default());
let right = Button::with_text(tr("buttons__install")).styled(theme::button_confirm());
let left =
Button::with_text(TR::buttons__cancel.try_into()?).styled(theme::button_default());
let right =
Button::with_text(TR::buttons__install.try_into()?).styled(theme::button_confirm());
let obj = LayoutObj::new(
Confirm::new(theme::BG, left, right, ConfirmTitle::Text(title), msg).with_info(
tr("firmware_update__title_fingerprint").into(),
TR::firmware_update__title_fingerprint.try_into()?,
fingerprint,
theme::button_moreinfo(),
),

@ -1,5 +1,5 @@
use crate::{
strutil::ShortString,
strutil::{ShortString, TString},
ui::{
component::text::TextStyle,
display,
@ -102,29 +102,31 @@ pub fn icon_text_center(
baseline: Point,
icon: Icon,
space: i16,
text: &str,
text: TString<'_>,
style: TextStyle,
text_offset: Offset,
) {
let icon_width = icon.toif.width();
let text_width = style.text_font.text_width(text);
let text_height = style.text_font.text_height();
let text_center = baseline + Offset::new((icon_width + space) / 2, text_height / 2);
let icon_center = baseline - Offset::x((text_width + space) / 2);
display::text_center(
text_center + text_offset,
text,
style.text_font,
style.text_color,
style.background_color,
);
icon.draw(
icon_center,
Alignment2D::CENTER,
style.text_color,
style.background_color,
);
text.map(|t| {
let icon_width = icon.toif.width();
let text_width = style.text_font.text_width(t);
let text_height = style.text_font.text_height();
let text_center = baseline + Offset::new((icon_width + space) / 2, text_height / 2);
let icon_center = baseline - Offset::x((text_width + space) / 2);
display::text_center(
text_center + text_offset,
t,
style.text_font,
style.text_color,
style.background_color,
);
icon.draw(
icon_center,
Alignment2D::CENTER,
style.text_color,
style.background_color,
);
});
}
/// Convert char to a ShortString.

@ -25,7 +25,7 @@ CORE_ROOT = HERE.parent.parent
FONTS_DIR = HERE / "fonts"
OUT_DIR = HERE / ".." / ".." / "embed" / "lib" / "fonts"
C_FONTS_DEST = CORE_ROOT / "embed" / "lib" / "fonts"
JSON_FONTS_DEST = CORE_ROOT / "embed" / "rust" / "src" / "ui" / "translations" / "fonts"
JSON_FONTS_DEST = CORE_ROOT / "translations" / "fonts"
MIN_GLYPH = ord(" ")
MAX_GLYPH = ord("~")

@ -1,13 +1,13 @@
{
"font": {
"Safe 3": {
"T2B1": {
"1_FONT_NORMAL": "font_pixeloperator_regular_8_cs.json",
"2_FONT_BOLD": "font_pixeloperator_bold_8_cs.json",
"3_FONT_MONO": "font_pixeloperatormono_regular_8_cs.json",
"4_FONT_BIG": "font_unifont_regular_16_cs.json",
"5_FONT_DEMIBOLD": "font_unifont_bold_16_cs.json"
},
"T": {
"T2T1": {
"1_FONT_NORMAL": "font_tthoves_regular_21_cs.json",
"2_FONT_BOLD": "font_tthoves_bold_17_cs.json",
"3_FONT_MONO": "font_robotomono_medium_20_cs.json",
@ -18,7 +18,7 @@
"header": {
"change_language_prompt": "Zmenit jazyk na cestinu?",
"change_language_title": "ZMENA JAZYKA",
"language": "cs",
"language": "csCZ",
"version": "2.6.5"
},
"translations": {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save