feat: software keyboard (without language switch)

This commit is contained in:
ardocrat
2025-05-23 19:20:42 +03:00
parent fb159c17a0
commit d11e90226b
32 changed files with 575 additions and 349 deletions
+16 -3
View File
@@ -21,7 +21,7 @@ use crate::AppConfig;
use crate::gui::Colors;
use crate::gui::icons::{ARROWS_IN, ARROWS_OUT, CARET_DOWN, MOON, SUN, X};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{Content, Modal, TitlePanel, View};
use crate::gui::views::{Content, KeyboardContent, Modal, TitlePanel, View};
use crate::wallet::ExternalConnection;
lazy_static! {
@@ -33,8 +33,12 @@ lazy_static! {
pub struct App<Platform> {
/// Handles platform-specific functionality.
pub platform: Platform,
/// Main content.
content: Content,
/// Keyboard content.
keyboard_content: KeyboardContent,
/// Last window resize direction.
resize_direction: Option<ResizeDirection>,
/// Flag to check if it's first draw.
@@ -46,6 +50,7 @@ impl<Platform: PlatformCallbacks> App<Platform> {
Self {
platform,
content: Content::default(),
keyboard_content: KeyboardContent::default(),
resize_direction: None,
first_draw: true
}
@@ -73,7 +78,12 @@ impl<Platform: PlatformCallbacks> App<Platform> {
// Handle Esc keyboard key event and platform Back button key event.
let back_pressed = BACK_BUTTON_PRESSED.load(Ordering::Relaxed);
if back_pressed || ctx.input_mut(|i| i.consume_key(Modifiers::NONE, egui::Key::Escape)) {
self.content.on_back(&self.platform);
// Hide keyboard or pass event to content.
if KeyboardContent::showing() {
KeyboardContent::hide();
} else {
self.content.on_back(&self.platform);
}
if back_pressed {
BACK_BUTTON_PRESSED.store(false, Ordering::Relaxed);
}
@@ -128,10 +138,13 @@ impl<Platform: PlatformCallbacks> App<Platform> {
self.content.ui(ui, &self.platform);
}
// Show soft keyboard content.
self.keyboard_content.ui(ctx);
// Provide incoming data to wallets.
if let Some(data) = crate::consume_incoming_data() {
if !data.is_empty() {
self.content.wallets.on_data(ui, Some(data), &self.platform);
self.content.wallets.on_data(ui, Some(data));
}
}
});
-14
View File
@@ -70,20 +70,6 @@ impl PlatformCallbacks for Android {
let _ = self.call_java_method("exit", "()V", &[]);
}
fn show_keyboard(&self) {
// Disable NDK soft input show call before fix for egui.
// self.android_app.show_soft_input(false);
let _ = self.call_java_method("showKeyboard", "()V", &[]);
}
fn hide_keyboard(&self) {
// Disable NDK soft input hide call before fix for egui.
// self.android_app.hide_soft_input(false);
let _ = self.call_java_method("hideKeyboard", "()V", &[]);
}
fn copy_string_to_buffer(&self, data: String) {
let vm = unsafe { jni::JavaVM::from_raw(self.android_app.vm_as_ptr() as _) }.unwrap();
let env = vm.attach_current_thread().unwrap();
-4
View File
@@ -180,10 +180,6 @@ impl PlatformCallbacks for Desktop {
}
}
fn show_keyboard(&self) {}
fn hide_keyboard(&self) {}
fn copy_string_to_buffer(&self, data: String) {
let mut clipboard = arboard::Clipboard::new().unwrap();
clipboard.set_text(data).unwrap();
-2
View File
@@ -24,8 +24,6 @@ pub mod platform;
pub trait PlatformCallbacks {
fn set_context(&mut self, ctx: &egui::Context);
fn exit(&self);
fn show_keyboard(&self);
fn hide_keyboard(&self);
fn copy_string_to_buffer(&self, data: String);
fn get_string_from_buffer(&self) -> String;
fn start_camera(&self);
+358
View File
@@ -0,0 +1,358 @@
// Copyright 2025 The Grim Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use eframe::emath::Align;
use eframe::epaint::{Margin, Shadow};
use egui::{Align2, Button, Color32, CursorIcon, Layout, Rect, Response, RichText, Stroke, Vec2, Widget};
use parking_lot::RwLock;
use lazy_static::lazy_static;
use crate::gui::Colors;
use crate::gui::icons::{ARROW_FAT_UP, BACKSPACE, KEY_RETURN, TRANSLATE};
use crate::gui::views::View;
lazy_static! {
/// Last input from software keyboard.
static ref LAST_INPUT: Arc<RwLock<Option<KeyboardInput>>> = Arc::new(RwLock::new(None));
/// Flag to show software keyboard.
static ref SHOW_KEYBOARD: AtomicBool = AtomicBool::new(false);
/// Flag to show English layout at keyboard.
static ref ENG_KEYBOARD: AtomicBool = AtomicBool::new(false);
}
/// Software keyboard input.
#[derive(Clone)]
pub enum KeyboardInput {
TEXT(String), CLEAR, ENTER
}
/// Software keyboard content.
pub struct KeyboardContent {
/// Flag to check if uppercase text entering is enabled.
uppercase: bool,
/// Flag to show symbols layout.
symbols: bool,
}
impl Default for KeyboardContent {
fn default() -> Self {
Self {
uppercase: false,
symbols: false,
}
}
}
impl KeyboardContent {
/// Maximum keyboard content width.
const MAX_WIDTH: f32 = 600.0;
/// Buttons content margin.
const MARGIN: f32 = 5.0;
/// Draw keyboard content.
pub fn ui(&mut self, ctx: &egui::Context) {
if !KeyboardContent::showing() {
return;
}
let width = ctx.screen_rect().width();
let layer_id = egui::Window::new("soft_keyboard")
.title_bar(false)
.resizable(false)
.collapsible(false)
.min_width(width)
.default_width(width)
.anchor(Align2::CENTER_BOTTOM, Vec2::new(0.0, 0.0))
.frame(egui::Frame {
shadow: Shadow {
offset: Default::default(),
blur: 30.0,
spread: 3.0,
color: egui::Color32::from_black_alpha(32),
},
inner_margin: Margin {
left: View::get_left_inset() + Self::MARGIN,
right: View::get_right_inset() + Self::MARGIN,
top: Self::MARGIN,
bottom: View::get_bottom_inset() + Self::MARGIN,
},
fill: Colors::fill(),
..Default::default()
})
.show(ctx, |ui| {
ui.set_min_width(width);
// Calculate content width.
let side_insets = View::get_left_inset() + View::get_right_inset();
let available_width = width - (side_insets + Self::MARGIN * 2.0);
let w = f32::min(available_width, Self::MAX_WIDTH);
View::max_width_ui(ui, w, |ui| {
// Setup spacing between buttons.
ui.style_mut().spacing.item_spacing = egui::vec2(Self::MARGIN, 0.0);
// Setup vertical padding inside buttons.
ui.style_mut().spacing.button_padding = egui::vec2(Self::MARGIN, 8.0);
let button_rect = if self.symbols {
self.symbols_ui(ui)
} else {
self.text_ui(ui)
};
ui.add_space(View::TAB_ITEMS_PADDING);
// Draw bottom keyboard buttons.
let bottom_size = {
let mut r = button_rect.clone();
r.set_width(ui.available_width());
r.size()
};
let button_width = button_rect.width();
ui.allocate_ui_with_layout(bottom_size, Layout::right_to_left(Align::Max), |ui| {
ui.horizontal_centered(|ui| {
ui.set_max_width(button_width * 2.0 + Self::MARGIN);
self.button_ui(KEY_RETURN,
Colors::white_or_black(false),
Some(Colors::green()),
ui,
|_| {
Self::on_input(KeyboardInput::ENTER);
});
});
ui.horizontal_centered(|ui| {
ui.set_max_width(button_width * 5.0 + 4.0 * Self::MARGIN);
self.button_ui(" ", Colors::inactive_text(), None, ui, |l| {
Self::on_input(KeyboardInput::TEXT(l));
});
});
ui.horizontal_centered(|ui| {
ui.set_max_width(button_width);
self.button_ui(TRANSLATE,
Colors::text_button(),
Some(Colors::fill_lite()),
ui,
|_| {
let eng_keyboard = ENG_KEYBOARD.load(Ordering::Relaxed);
ENG_KEYBOARD.store(!eng_keyboard, Ordering::Relaxed);
});
});
ui.horizontal_centered(|ui| {
let label = if self.symbols {
"ABC"
} else {
"?/ツ"
};
let mut symbols = self.symbols;
self.button_ui(label, Colors::text_button(), Some(Colors::fill_lite()), ui, |_| {
symbols = !self.symbols;
});
self.symbols = symbols;
});
});
});
}).unwrap().response.layer_id;
// Always show keyboard above others windows.
ctx.move_to_top(layer_id);
}
/// Draw text content returning button [`Rect`].
fn text_ui(&mut self, ui: &mut egui::Ui) -> Rect {
let mut button_rect = ui.available_rect_before_wrap();
let tl_0: Vec<&str> = vec!["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"];
ui.columns(tl_0.len(), |columns| {
for (index, s) in tl_0.iter().enumerate() {
button_rect = self.input_button_ui(s, &mut columns[index]);
}
});
ui.add_space(View::TAB_ITEMS_PADDING);
let tl_1: Vec<&str> = vec!["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"];
ui.columns(tl_1.len(), |columns| {
for (index, s) in tl_1.iter().enumerate() {
self.input_button_ui(s, &mut columns[index]);
}
});
ui.add_space(View::TAB_ITEMS_PADDING);
let tl_2: Vec<&str> = vec!["a", "s", "d", "f", "g", "h", "j", "k", "l", ":"];
ui.columns(tl_2.len(), |columns| {
for (index, s) in tl_2.iter().enumerate() {
self.input_button_ui(s, &mut columns[index]);
}
});
ui.add_space(View::TAB_ITEMS_PADDING);
let tl_3: Vec<&str> = vec![ARROW_FAT_UP, "z", "x", "c", "v", "b", "n", "m", ".", BACKSPACE];
ui.columns(tl_3.len(), |columns| {
for (index, s) in tl_3.iter().enumerate() {
if index == 0 {
// Check for shift input.
let color = if self.uppercase {
Colors::yellow_dark()
} else {
Colors::inactive_text()
};
let mut uppercase = self.uppercase;
self.button_ui(s, color, Some(Colors::fill_lite()), &mut columns[index], |_| {
uppercase = !uppercase;
});
self.uppercase = uppercase;
} else if index == tl_3.len() - 1 {
// Check for backspace input.
self.button_ui(s,
Colors::red(),
Some(Colors::fill_lite()),
&mut columns[index],
|_| {
Self::on_input(KeyboardInput::CLEAR);
});
} else {
self.input_button_ui(s, &mut columns[index]);
}
}
});
button_rect
}
/// Draw symbols content returning button [`Rect`].
fn symbols_ui(&mut self, ui: &mut egui::Ui) -> Rect {
let mut button_rect = ui.available_rect_before_wrap();
let tl_0: Vec<&str> = vec!["[", "]", "{", "}", "#", "%", "^", "*", "+", "="];
ui.columns(tl_0.len(), |columns| {
for (index, s) in tl_0.iter().enumerate() {
button_rect = self.input_button_ui(s, &mut columns[index]);
}
});
ui.add_space(View::TAB_ITEMS_PADDING);
let tl_1: Vec<&str> = vec!["_", "\\", "|", "~", "<", ">", "`", "", "π", ""];
ui.columns(tl_1.len(), |columns| {
for (index, s) in tl_1.iter().enumerate() {
self.input_button_ui(s, &mut columns[index]);
}
});
ui.add_space(View::TAB_ITEMS_PADDING);
let tl_2: Vec<&str> = vec!["-", "/", ":", ";", "(", ")", "$", "&", "@", "\""];
ui.columns(tl_2.len(), |columns| {
for (index, s) in tl_2.iter().enumerate() {
self.input_button_ui(s, &mut columns[index]);
}
});
ui.add_space(View::TAB_ITEMS_PADDING);
let tl_3: Vec<&str> = vec![".", ",", "?", "!", "", "£", "¥", "¢", "", BACKSPACE];
ui.columns(tl_3.len(), |columns| {
for (index, s) in tl_3.iter().enumerate() {
if index == tl_3.len() - 1 {
// Check for backspace input.
self.button_ui(s,
Colors::red(),
Some(Colors::fill_lite()),
&mut columns[index], |_| {
Self::on_input(KeyboardInput::CLEAR);
});
} else {
self.input_button_ui(s, &mut columns[index]);
}
}
});
button_rect
}
/// Draw keyboard button.
fn button_ui(&self,
s: &str,
color: Color32,
bg: Option<Color32>,
ui: &mut egui::Ui,
mut cb: impl FnMut(String)) -> Response {
ui.vertical_centered_justified(|ui| {
// Disable expansion on click/hover.
ui.style_mut().visuals.widgets.hovered.expansion = 0.0;
ui.style_mut().visuals.widgets.active.expansion = 0.0;
// Setup fill colors.
ui.visuals_mut().widgets.inactive.weak_bg_fill = Colors::white_or_black(false);
ui.visuals_mut().widgets.hovered.weak_bg_fill = Colors::fill_lite();
ui.visuals_mut().widgets.active.weak_bg_fill = Colors::fill();
// Setup stroke colors.
ui.visuals_mut().widgets.inactive.bg_stroke = View::item_stroke();
ui.visuals_mut().widgets.hovered.bg_stroke = View::hover_stroke();
ui.visuals_mut().widgets.active.bg_stroke = Stroke::NONE;
let label = if self.uppercase {
s.to_uppercase()
} else {
s.to_string()
};
let mut button = Button::new(RichText::new(label.clone()).size(17.0).color(color));
if let Some(bg) = bg {
button = button.fill(bg);
}
let resp = button.ui(ui).on_hover_cursor(CursorIcon::PointingHand);
if resp.clicked() {
(cb)(label);
}
}).response
}
/// Draw input button.
fn input_button_ui(&mut self, s: &str, ui: &mut egui::Ui) -> Rect {
let mut input = false;
let rect = self.button_ui(s, Colors::text_button(), None, ui, |l| {
input = true;
Self::on_input(KeyboardInput::TEXT(l));
}).rect;
if input {
self.uppercase = false;
}
rect
}
/// Save keyboard action to consume later.
fn on_input(action: KeyboardInput) {
let mut w_input = LAST_INPUT.write();
*w_input = Some(action);
}
/// Check last keyboard input action.
pub fn consume_action() -> Option<KeyboardInput> {
let empty = {
let r_input = LAST_INPUT.read();
r_input.is_none()
};
if !empty {
let mut w_input = LAST_INPUT.write();
let res = w_input.clone();
*w_input = None;
return res;
}
None
}
/// Check if keyboard is showing.
pub fn showing() -> bool {
SHOW_KEYBOARD.load(Ordering::Relaxed)
}
/// Show keyboard.
pub fn show() {
SHOW_KEYBOARD.store(true, Ordering::Relaxed);
}
/// Hide keyboard.
pub fn hide() {
SHOW_KEYBOARD.store(false, Ordering::Relaxed);
}
}
+4 -1
View File
@@ -43,4 +43,7 @@ mod pull_to_refresh;
pub use pull_to_refresh::*;
mod scan;
pub use scan::*;
pub use scan::*;
mod keyboard;
pub use keyboard::*;
+4 -2
View File
@@ -21,7 +21,7 @@ use egui::epaint::{RectShape, Shadow};
use egui::os::OperatingSystem;
use crate::gui::Colors;
use crate::gui::views::{Content, View};
use crate::gui::views::{Content, KeyboardContent, View};
use crate::gui::views::types::{ModalPosition, ModalState};
lazy_static! {
@@ -70,10 +70,12 @@ impl Modal {
w_state.modal.as_mut().unwrap().position = position;
}
/// Mark [`Modal`] closed.
/// Close [`Modal`] by clearing its state..
pub fn close(&self) {
let mut w_nav = MODAL_STATE.write();
w_nav.modal = None;
// Hide keyboard.
KeyboardContent::hide();
}
/// Setup possibility to close [`Modal`].
+3 -6
View File
@@ -96,7 +96,7 @@ impl ConnectionsContent {
// Show button to add new external node connection.
let add_node_text = format!("{} {}", PLUS_CIRCLE, t!("wallets.add_node"));
View::button(ui, add_node_text, Colors::white_or_black(false), || {
self.show_add_ext_conn_modal(None, cb);
self.show_add_ext_conn_modal(None);
});
ui.add_space(4.0);
@@ -114,7 +114,7 @@ impl ConnectionsContent {
ConnectionsConfig::remove_ext_conn(conn.id);
});
View::item_button(ui, Rounding::default(), PENCIL, None, || {
self.show_add_ext_conn_modal(Some(conn.clone()), cb);
self.show_add_ext_conn_modal(Some(conn.clone()));
});
});
});
@@ -234,15 +234,12 @@ impl ConnectionsContent {
}
/// Show [`Modal`] to add external connection.
pub fn show_add_ext_conn_modal(&mut self,
conn: Option<ExternalConnection>,
cb: &dyn PlatformCallbacks) {
pub fn show_add_ext_conn_modal(&mut self, conn: Option<ExternalConnection>) {
self.ext_conn_modal = ExternalConnectionModal::new(conn);
// Show modal.
Modal::new(ExternalConnectionModal::NETWORK_ID)
.position(ModalPosition::CenterTop)
.title(t!("wallets.add_node"))
.show();
cb.show_keyboard();
}
}
-2
View File
@@ -113,7 +113,6 @@ impl ExternalConnectionModal {
self.ext_node_url_edit = "".to_string();
self.ext_node_secret_edit = "".to_string();
self.ext_node_url_error = false;
cb.hide_keyboard();
modal.close();
});
});
@@ -146,7 +145,6 @@ impl ExternalConnectionModal {
self.ext_node_url_edit = "".to_string();
self.ext_node_secret_edit = "".to_string();
self.ext_node_url_error = false;
cb.hide_keyboard();
modal.close();
}
};
+8 -20
View File
@@ -96,28 +96,28 @@ impl DandelionSetup {
ui.vertical_centered(|ui| {
// Show epoch duration setup.
self.epoch_ui(ui, cb);
self.epoch_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show embargo expiration time setup.
self.embargo_ui(ui, cb);
self.embargo_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show aggregation period setup.
self.aggregation_ui(ui, cb);
self.aggregation_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show Stem phase probability setup.
self.stem_prob_ui(ui, cb);
self.stem_prob_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
@@ -133,7 +133,7 @@ impl DandelionSetup {
}
/// Draw epoch duration setup content.
fn epoch_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn epoch_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.epoch_duration"))
.size(16.0)
.color(Colors::gray())
@@ -149,7 +149,6 @@ impl DandelionSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -188,7 +187,6 @@ impl DandelionSetup {
let on_save = || {
if let Ok(epoch) = self.epoch_edit.parse::<u16>() {
NodeConfig::save_dandelion_epoch(epoch);
cb.hide_keyboard();
modal.close();
}
};
@@ -202,7 +200,6 @@ impl DandelionSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -215,7 +212,7 @@ impl DandelionSetup {
}
/// Draw embargo expiration time setup content.
fn embargo_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn embargo_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.embargo_timer"))
.size(16.0)
.color(Colors::gray())
@@ -230,7 +227,6 @@ impl DandelionSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -269,7 +265,6 @@ impl DandelionSetup {
let on_save = || {
if let Ok(embargo) = self.embargo_edit.parse::<u16>() {
NodeConfig::save_dandelion_embargo(embargo);
cb.hide_keyboard();
modal.close();
}
};
@@ -283,7 +278,6 @@ impl DandelionSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -296,7 +290,7 @@ impl DandelionSetup {
}
/// Draw aggregation period setup content.
fn aggregation_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn aggregation_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.aggregation_period"))
.size(16.0)
.color(Colors::gray())
@@ -312,7 +306,6 @@ impl DandelionSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -351,7 +344,6 @@ impl DandelionSetup {
let on_save = || {
if let Ok(embargo) = self.aggregation_edit.parse::<u16>() {
NodeConfig::save_dandelion_aggregation(embargo);
cb.hide_keyboard();
modal.close();
}
};
@@ -365,7 +357,6 @@ impl DandelionSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -378,7 +369,7 @@ impl DandelionSetup {
}
/// Draw stem phase probability setup content.
fn stem_prob_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn stem_prob_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.stem_probability"))
.size(16.0)
.color(Colors::gray())
@@ -394,7 +385,6 @@ impl DandelionSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -433,7 +423,6 @@ impl DandelionSetup {
let on_save = || {
if let Ok(prob) = self.stem_prob_edit.parse::<u8>() {
NodeConfig::save_stem_probability(prob);
cb.hide_keyboard();
modal.close();
}
};
@@ -447,7 +436,6 @@ impl DandelionSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
+7 -16
View File
@@ -185,12 +185,12 @@ impl NodeSetup {
NodeConfig::save_api_address(selected_ip, &api_port);
});
// Show API port setup.
self.api_port_setup_ui(ui, cb);
self.api_port_setup_ui(ui);
// Show API secret setup.
self.secret_ui(API_SECRET_MODAL, ui, cb);
self.secret_ui(API_SECRET_MODAL, ui);
ui.add_space(12.0);
// Show Foreign API secret setup.
self.secret_ui(FOREIGN_API_SECRET_MODAL, ui, cb);
self.secret_ui(FOREIGN_API_SECRET_MODAL, ui);
ui.add_space(6.0);
});
}
@@ -201,7 +201,7 @@ impl NodeSetup {
ui.vertical_centered(|ui| {
// Show FTL setup.
self.ftl_ui(ui, cb);
self.ftl_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
@@ -250,7 +250,7 @@ impl NodeSetup {
}
/// Draw API port setup content.
fn api_port_setup_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn api_port_setup_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.api_port")).size(16.0).color(Colors::gray()));
ui.add_space(6.0);
@@ -265,7 +265,6 @@ impl NodeSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
@@ -325,7 +324,6 @@ impl NodeSetup {
}
self.is_api_port_available = true;
cb.hide_keyboard();
modal.close();
}
};
@@ -338,7 +336,6 @@ impl NodeSetup {
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
cb.hide_keyboard();
modal.close();
});
});
@@ -351,7 +348,7 @@ impl NodeSetup {
}
/// Draw API secret token setup content.
fn secret_ui(&mut self, modal_id: &'static str, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn secret_ui(&mut self, modal_id: &'static str, ui: &mut egui::Ui) {
let secret_title = match modal_id {
API_SECRET_MODAL => t!("network_settings.api_secret"),
_ => t!("network_settings.foreign_api_secret")
@@ -381,7 +378,6 @@ impl NodeSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
}
@@ -428,7 +424,6 @@ impl NodeSetup {
NodeConfig::save_foreign_api_secret(&secret);
}
};
cb.hide_keyboard();
modal.close();
};
@@ -440,7 +435,6 @@ impl NodeSetup {
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
cb.hide_keyboard();
modal.close();
});
});
@@ -453,7 +447,7 @@ impl NodeSetup {
}
/// Draw FTL setup content.
fn ftl_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn ftl_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.ftl"))
.size(16.0)
.color(Colors::gray())
@@ -471,7 +465,6 @@ impl NodeSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
ui.label(RichText::new(t!("network_settings.ftl_description"))
@@ -514,7 +507,6 @@ impl NodeSetup {
let on_save = || {
if let Ok(ftl) = self.ftl_edit.parse::<u64>() {
NodeConfig::save_ftl(ftl);
cb.hide_keyboard();
modal.close();
}
};
@@ -528,7 +520,6 @@ impl NodeSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
+15 -30
View File
@@ -162,14 +162,14 @@ impl P2PSetup {
ui.vertical_centered(|ui| {
// Show p2p port setup.
self.port_ui(ui, cb);
self.port_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show seeding type setup.
self.seeding_type_ui(ui, cb);
self.seeding_type_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
@@ -180,7 +180,7 @@ impl P2PSetup {
.color(Colors::gray()));
ui.add_space(6.0);
// Show allowed peers setup.
self.peer_list_ui(ui, &PeerType::Allowed, cb);
self.peer_list_ui(ui, &PeerType::Allowed);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
@@ -191,7 +191,7 @@ impl P2PSetup {
.color(Colors::gray()));
ui.add_space(6.0);
// Show denied peers setup.
self.peer_list_ui(ui, &PeerType::Denied, cb);
self.peer_list_ui(ui, &PeerType::Denied);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
@@ -202,7 +202,7 @@ impl P2PSetup {
.color(Colors::gray()));
ui.add_space(6.0);
// Show preferred peers setup.
self.peer_list_ui(ui, &PeerType::Preferred, cb);
self.peer_list_ui(ui, &PeerType::Preferred);
ui.add_space(6.0);
@@ -210,21 +210,21 @@ impl P2PSetup {
ui.add_space(6.0);
// Show ban window setup.
self.ban_window_ui(ui, cb);
self.ban_window_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show maximum inbound peers value setup.
self.max_inbound_ui(ui, cb);
self.max_inbound_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show maximum outbound peers value setup.
self.max_outbound_ui(ui, cb);
self.max_outbound_ui(ui);
if !Node::is_restarting() && !self.peers_reset {
ui.add_space(6.0);
@@ -238,7 +238,7 @@ impl P2PSetup {
}
/// Draw p2p port setup content.
fn port_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn port_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.p2p_port"))
.size(16.0)
.color(Colors::gray())
@@ -257,7 +257,6 @@ impl P2PSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
@@ -312,7 +311,6 @@ impl P2PSetup {
Node::restart();
}
self.is_port_available = true;
cb.hide_keyboard();
modal.close();
}
};
@@ -326,7 +324,6 @@ impl P2PSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -340,7 +337,7 @@ impl P2PSetup {
}
/// Draw peer list content based on provided [`PeerType`].
fn peer_list_ui(&mut self, ui: &mut egui::Ui, peer_type: &PeerType, cb: &dyn PlatformCallbacks) {
fn peer_list_ui(&mut self, ui: &mut egui::Ui, peer_type: &PeerType) {
let peers = match peer_type {
PeerType::DefaultSeed => {
if AppConfig::chain_type() == ChainTypes::Testnet {
@@ -409,7 +406,6 @@ impl P2PSetup {
.position(ModalPosition::CenterTop)
.title(modal_title)
.show();
cb.show_keyboard();
});
}
ui.add_space(6.0);
@@ -462,7 +458,6 @@ impl P2PSetup {
}
self.is_port_available = true;
cb.hide_keyboard();
modal.close();
}
};
@@ -476,7 +471,6 @@ impl P2PSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -490,7 +484,7 @@ impl P2PSetup {
}
/// Draw seeding type setup content.
fn seeding_type_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn seeding_type_ui(&mut self, ui: &mut egui::Ui) {
let title = Self::DNS_SEEDS_TITLE;
ui.label(RichText::new(title).size(16.0).color(Colors::gray()));
ui.add_space(2.0);
@@ -506,11 +500,11 @@ impl P2PSetup {
} else {
PeerType::CustomSeed
};
self.peer_list_ui(ui, &peers_type, cb);
self.peer_list_ui(ui, &peers_type);
}
/// Draw ban window setup content.
fn ban_window_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn ban_window_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.ban_window"))
.size(16.0)
.color(Colors::gray())
@@ -528,7 +522,6 @@ impl P2PSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
ui.label(RichText::new(t!("network_settings.ban_window_desc"))
@@ -572,7 +565,6 @@ impl P2PSetup {
let on_save = || {
if let Ok(ban_window) = self.ban_window_edit.parse::<i64>() {
NodeConfig::save_p2p_ban_window(ban_window);
cb.hide_keyboard();
modal.close();
}
};
@@ -586,7 +578,6 @@ impl P2PSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -599,7 +590,7 @@ impl P2PSetup {
}
/// Draw maximum number of inbound peers setup content.
fn max_inbound_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn max_inbound_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.max_inbound_count"))
.size(16.0)
.color(Colors::gray())
@@ -617,7 +608,6 @@ impl P2PSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -656,7 +646,6 @@ impl P2PSetup {
let on_save = || {
if let Ok(max_inbound) = self.max_inbound_count.parse::<u32>() {
NodeConfig::save_max_inbound_peers(max_inbound);
cb.hide_keyboard();
modal.close();
}
};
@@ -670,7 +659,6 @@ impl P2PSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -683,7 +671,7 @@ impl P2PSetup {
}
/// Draw maximum number of outbound peers setup content.
fn max_outbound_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn max_outbound_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.max_outbound_count"))
.size(16.0)
.color(Colors::gray())
@@ -700,7 +688,6 @@ impl P2PSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -739,7 +726,6 @@ impl P2PSetup {
let on_save = || {
if let Ok(max_outbound) = self.max_outbound_count.parse::<u32>() {
NodeConfig::save_max_outbound_peers(max_outbound);
cb.hide_keyboard();
modal.close();
}
};
@@ -753,7 +739,6 @@ impl P2PSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
+10 -25
View File
@@ -104,40 +104,40 @@ impl PoolSetup {
ui.vertical_centered(|ui| {
// Show base fee setup.
self.fee_base_ui(ui, cb);
self.fee_base_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show reorg cache retention period setup.
self.reorg_period_ui(ui, cb);
self.reorg_period_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show pool size setup.
self.pool_size_ui(ui, cb);
self.pool_size_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show stem pool size setup.
self.stem_size_ui(ui, cb);
self.stem_size_ui(ui);
ui.add_space(6.0);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show max weight of transactions setup.
self.max_weight_ui(ui, cb);
self.max_weight_ui(ui);
});
}
/// Draw fee base setup content.
fn fee_base_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn fee_base_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.pool_fee"))
.size(16.0)
.color(Colors::gray())
@@ -153,7 +153,6 @@ impl PoolSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -191,7 +190,6 @@ impl PoolSetup {
let on_save = || {
if let Ok(fee) = self.fee_base_edit.parse::<u64>() {
NodeConfig::save_base_fee(fee);
cb.hide_keyboard();
modal.close();
}
};
@@ -205,7 +203,6 @@ impl PoolSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -219,7 +216,7 @@ impl PoolSetup {
}
/// Draw reorg cache retention period setup content.
fn reorg_period_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn reorg_period_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.reorg_period"))
.size(16.0)
.color(Colors::gray())
@@ -236,7 +233,6 @@ impl PoolSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -274,7 +270,6 @@ impl PoolSetup {
let on_save = || {
if let Ok(period) = self.reorg_period_edit.parse::<u32>() {
NodeConfig::save_reorg_cache_period(period);
cb.hide_keyboard();
modal.close();
}
};
@@ -288,7 +283,6 @@ impl PoolSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -302,7 +296,7 @@ impl PoolSetup {
}
/// Draw maximum number of transactions in the pool setup content.
fn pool_size_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn pool_size_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.max_tx_pool"))
.size(16.0)
.color(Colors::gray())
@@ -318,7 +312,6 @@ impl PoolSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -356,7 +349,6 @@ impl PoolSetup {
let on_save = || {
if let Ok(size) = self.pool_size_edit.parse::<usize>() {
NodeConfig::save_max_pool_size(size);
cb.hide_keyboard();
modal.close();
}
};
@@ -370,7 +362,6 @@ impl PoolSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -384,7 +375,7 @@ impl PoolSetup {
}
/// Draw maximum number of transactions in the stempool setup content.
fn stem_size_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn stem_size_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.max_tx_stempool"))
.size(16.0)
.color(Colors::gray())
@@ -402,7 +393,6 @@ impl PoolSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -440,7 +430,6 @@ impl PoolSetup {
let on_save = || {
if let Ok(size) = self.stempool_size_edit.parse::<usize>() {
NodeConfig::save_max_stempool_size(size);
cb.hide_keyboard();
modal.close();
}
};
@@ -454,7 +443,6 @@ impl PoolSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -468,7 +456,7 @@ impl PoolSetup {
}
/// Draw maximum total weight of transactions setup content.
fn max_weight_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn max_weight_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.max_tx_weight"))
.size(16.0)
.color(Colors::gray())
@@ -486,7 +474,6 @@ impl PoolSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -524,7 +511,6 @@ impl PoolSetup {
let on_save = || {
if let Ok(weight) = self.max_weight_edit.parse::<u64>() {
NodeConfig::save_mineable_max_weight(weight);
cb.hide_keyboard();
modal.close();
}
};
@@ -538,7 +524,6 @@ impl PoolSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
+6 -15
View File
@@ -227,19 +227,19 @@ impl StratumSetup {
});
// Show stratum port setup.
self.port_setup_ui(ui, cb);
self.port_setup_ui(ui);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show attempt time setup.
self.attempt_time_ui(ui, cb);
self.attempt_time_ui(ui);
View::horizontal_line(ui, Colors::item_stroke());
ui.add_space(6.0);
// Show minimum acceptable share difficulty setup.
self.min_diff_ui(ui, cb);
self.min_diff_ui(ui);
});
}
@@ -254,7 +254,7 @@ impl StratumSetup {
}
/// Draw stratum port value setup content.
fn port_setup_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn port_setup_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.stratum_port"))
.size(16.0)
.color(Colors::gray())
@@ -271,7 +271,6 @@ impl StratumSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(12.0);
@@ -330,7 +329,6 @@ impl StratumSetup {
NodeConfig::save_stratum_address(&stratum_ip, &self.stratum_port_edit);
self.is_port_available = true;
cb.hide_keyboard();
modal.close();
}
};
@@ -344,7 +342,6 @@ impl StratumSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -358,7 +355,7 @@ impl StratumSetup {
}
/// Draw attempt time value setup content.
fn attempt_time_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn attempt_time_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.attempt_time"))
.size(16.0)
.color(Colors::gray())
@@ -375,7 +372,6 @@ impl StratumSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
ui.label(RichText::new(t!("network_settings.attempt_time_desc"))
@@ -419,7 +415,6 @@ impl StratumSetup {
let on_save = || {
if let Ok(time) = self.attempt_time_edit.parse::<u32>() {
NodeConfig::save_stratum_attempt_time(time);
cb.hide_keyboard();
modal.close();
}
};
@@ -433,7 +428,6 @@ impl StratumSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -446,7 +440,7 @@ impl StratumSetup {
}
/// Draw minimum share difficulty value setup content.
fn min_diff_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn min_diff_ui(&mut self, ui: &mut egui::Ui) {
ui.label(RichText::new(t!("network_settings.min_share_diff"))
.size(16.0)
.color(Colors::gray())
@@ -463,7 +457,6 @@ impl StratumSetup {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(6.0);
}
@@ -502,7 +495,6 @@ impl StratumSetup {
let on_save = || {
if let Ok(diff) = self.min_share_diff_edit.parse::<u64>() {
NodeConfig::save_stratum_min_share_diff(diff);
cb.hide_keyboard();
modal.close();
}
};
@@ -516,7 +508,6 @@ impl StratumSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
+3
View File
@@ -94,6 +94,8 @@ pub struct TextEditOptions {
pub scan_qr: bool,
/// Callback when scan button was pressed.
pub scan_pressed: bool,
/// Callback when Enter key was pressed.
pub enter_pressed: bool,
}
impl TextEditOptions {
@@ -107,6 +109,7 @@ impl TextEditOptions {
paste: false,
scan_qr: false,
scan_pressed: false,
enter_pressed: false,
}
}
+61 -71
View File
@@ -13,8 +13,6 @@
// limitations under the License.
use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::Arc;
use parking_lot::RwLock;
use lazy_static::lazy_static;
use egui::{Align, Button, CursorIcon, Layout, lerp, PointerState, Rect, Response, Rgba, RichText, Sense, SizeHint, Spinner, TextBuffer, TextStyle, TextureHandle, TextureOptions, Widget, UiBuilder};
@@ -30,6 +28,7 @@ use crate::AppConfig;
use crate::gui::Colors;
use crate::gui::icons::{CHECK_SQUARE, CLIPBOARD_TEXT, COPY, EYE, EYE_SLASH, SCAN, SQUARE};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::views::{KeyboardContent, KeyboardInput};
use crate::gui::views::types::{LinePosition, TextEditOptions};
pub struct View;
@@ -450,59 +449,80 @@ impl View {
.password(show_pass)
.cursor_at_end(true)
.ui(ui);
// Show keyboard on click.
if text_edit_resp.clicked() {
text_edit_resp.request_focus();
cb.show_keyboard();
// Store focusing state on click or from options.
let focus_id = egui::Id::new("focused_input");
if text_edit_resp.clicked() || options.focus {
ui.data_mut(|data| {
data.insert_temp(focus_id, options.id);
});
}
// Setup focus on input field.
if options.focus {
let focus = ui.data(|data| {
data.get_temp(focus_id)
}).unwrap_or(egui::Id::from(""));
if focus == options.id {
text_edit_resp.request_focus();
cb.show_keyboard();
KeyboardContent::show();
}
// Apply text from input on Android as temporary fix for egui.
// Apply text from software input.
if text_edit_resp.has_focus() {
Self::on_soft_input(ui, options.id, value);
options.focus = true;
options.enter_pressed = Self::on_soft_input(ui, options.id, false, value);
}
});
});
}
/// Apply soft keyboard input data to provided String.
pub fn on_soft_input(ui: &mut egui::Ui, id: egui::Id, value: &mut String) {
let os = OperatingSystem::from_target_os();
if os == OperatingSystem::Android {
let mut w_input = LAST_SOFT_KEYBOARD_INPUT.write();
if !w_input.is_empty() {
let mut state = TextEditState::load(ui.ctx(), id).unwrap();
match state.cursor.char_range() {
None => {}
Some(range) => {
let mut r = range.clone();
let mut index = r.primary.index;
value.insert_text(w_input.as_str(), index);
index = index + 1;
if index == 0 {
r.primary.index = value.len();
r.secondary.index = r.primary.index;
} else {
r.primary.index = index;
r.secondary.index = r.primary.index;
/// Apply soft keyboard input data to provided String, returns `true` if Enter was pressed.
fn on_soft_input(ui: &mut egui::Ui, id: egui::Id, multiline: bool, value: &mut String) -> bool {
if let Some(input) = KeyboardContent::consume_action() {
let mut enter_pressed = false;
let mut state = TextEditState::load(ui.ctx(), id).unwrap();
match state.cursor.char_range() {
None => {}
Some(range) => {
let mut r = range.clone();
let mut index = r.primary.index;
match input {
KeyboardInput::TEXT(text) => {
value.insert_text(text.as_str(), index);
index = index + 1;
}
KeyboardInput::CLEAR => {
if index != 0 {
*value = {
let part1: String = value.chars()
.skip(0)
.take(index - 1)
.collect();
let part2: String = value.chars()
.skip(index)
.take(value.len() - index)
.collect();
format!("{}{}", part1, part2)
};
index = index - 1;
}
}
KeyboardInput::ENTER => {
if multiline {
value.insert_text("\n", index);
index = index + 1;
} else {
enter_pressed = true;
}
}
state.cursor.set_char_range(Some(r));
TextEditState::store(state, ui.ctx(), id);
}
// Setup cursor index.
r.primary.index = index;
r.secondary.index = r.primary.index;
state.cursor.set_char_range(Some(r));
TextEditState::store(state, ui.ctx(), id);
}
}
*w_input = "".to_string();
ui.ctx().request_repaint();
return enter_pressed;
}
false
}
/// Calculate item background/button rounding based on item index.
@@ -806,34 +826,4 @@ pub extern "C" fn Java_mw_gri_android_MainActivity_onDisplayInsets(
RIGHT_DISPLAY_INSET.store(array[1], Ordering::Relaxed);
BOTTOM_DISPLAY_INSET.store(array[2], Ordering::Relaxed);
LEFT_DISPLAY_INSET.store(array[3], Ordering::Relaxed);
}
lazy_static! {
static ref LAST_SOFT_KEYBOARD_INPUT: Arc<RwLock<String>> = Arc::new(RwLock::new("".into()));
}
#[allow(dead_code)]
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
#[no_mangle]
/// Callback from Java code with last entered character from soft keyboard.
pub extern "C" fn Java_mw_gri_android_MainActivity_onInput(
_env: jni::JNIEnv,
_class: jni::objects::JObject,
char: jni::sys::jstring
) {
use jni::objects::JString;
use std::ops::Add;
unsafe {
let j_obj = JString::from_raw(char);
let j_str = _env.get_string_unchecked(j_obj.as_ref()).unwrap();
match j_str.to_str() {
Ok(str) => {
let mut w_input = LAST_SOFT_KEYBOARD_INPUT.write();
*w_input = w_input.clone().add(str);
}
Err(_) => {}
}
}
}
+12 -22
View File
@@ -202,7 +202,7 @@ impl WalletsContent {
ui.vertical_centered(|ui| {
let pressed = Modal::opened() == Some(ADD_WALLET_MODAL);
View::tab_button(ui, PLUS, pressed, |_| {
self.show_add_wallet_modal(cb);
self.show_add_wallet_modal();
});
});
@@ -239,7 +239,7 @@ impl WalletsContent {
ui.ctx().request_repaint_after(Duration::from_millis(1000));
}
// Show wallet list.
self.wallet_list_ui(ui, cb);
self.wallet_list_ui(ui);
});
}
@@ -282,7 +282,7 @@ impl WalletsContent {
// Show wallet creation button.
let add_text = format!("{} {}", FOLDER_PLUS, t!("wallets.add"));
View::button(ui, add_text, Colors::white_or_black(false), || {
self.show_add_wallet_modal(cb);
self.show_add_wallet_modal();
});
});
} else {
@@ -307,7 +307,7 @@ impl WalletsContent {
}
/// Handle data from deeplink or opened file.
pub fn on_data(&mut self, ui: &mut egui::Ui, data: Option<String>, cb: &dyn PlatformCallbacks) {
pub fn on_data(&mut self, ui: &mut egui::Ui, data: Option<String>) {
let wallets_size = self.wallets.list().len();
if wallets_size == 0 {
return;
@@ -322,7 +322,7 @@ impl WalletsContent {
if w.is_open() {
self.wallet_content = Some(WalletContent::new(w, data));
} else {
self.show_opening_modal(w, data, cb);
self.show_opening_modal(w, data);
}
} else {
self.show_wallet_selection_modal(data);
@@ -330,13 +330,12 @@ impl WalletsContent {
}
/// Show initial wallet creation [`Modal`].
pub fn show_add_wallet_modal(&mut self, cb: &dyn PlatformCallbacks) {
pub fn show_add_wallet_modal(&mut self) {
self.add_wallet_modal_content = Some(AddWalletModal::default());
Modal::new(ADD_WALLET_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("wallets.add"))
.show();
cb.show_keyboard();
}
/// Show wallet selection with provided optional data.
@@ -492,9 +491,7 @@ impl WalletsContent {
}
/// Draw list of wallets.
fn wallet_list_ui(&mut self,
ui: &mut egui::Ui,
cb: &dyn PlatformCallbacks) {
fn wallet_list_ui(&mut self, ui: &mut egui::Ui) {
ScrollArea::vertical()
.id_salt("wallet_list_scroll")
.scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden)
@@ -517,9 +514,9 @@ impl WalletsContent {
// Check if wallet reopen is needed.
if w.reopen_needed() && !w.is_open() {
w.set_reopen(false);
self.show_opening_modal(w.clone(), None, cb);
self.show_opening_modal(w.clone(), None);
}
self.wallet_item_ui(ui, w, cb);
self.wallet_item_ui(ui, w);
ui.add_space(5.0);
}
});
@@ -527,10 +524,7 @@ impl WalletsContent {
}
/// Draw wallet list item.
fn wallet_item_ui(&mut self,
ui: &mut egui::Ui,
wallet: &Wallet,
cb: &dyn PlatformCallbacks) {
fn wallet_item_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet) {
let config = wallet.get_config();
let current = if let Some(content) = &self.wallet_content {
content.wallet.get_config().id == config.id && wallet.is_open()
@@ -553,7 +547,7 @@ impl WalletsContent {
if !wallet.is_open() {
// Show button to open closed wallet.
View::item_button(ui, View::item_rounding(0, 1, true), FOLDER_OPEN, None, || {
self.show_opening_modal(wallet.clone(), None, cb);
self.show_opening_modal(wallet.clone(), None);
});
if !wallet.syncing() {
let mut show_selection = false;
@@ -628,17 +622,13 @@ impl WalletsContent {
}
/// Show [`Modal`] to select and open wallet.
fn show_opening_modal(&mut self,
wallet: Wallet,
data: Option<String>,
cb: &dyn PlatformCallbacks) {
fn show_opening_modal(&mut self, wallet: Wallet, data: Option<String>) {
self.wallet_content = Some(WalletContent::new(wallet.clone(), None));
self.open_wallet_content = Some(OpenWalletModal::new(wallet, data));
Modal::new(OPEN_WALLET_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("wallets.open"))
.show();
cb.show_keyboard();
}
/// Check if it's possible to show [`WalletsContent`] and [`WalletContent`] panels at same time.
+9 -13
View File
@@ -87,7 +87,7 @@ impl MnemonicSetup {
ui.add_space(6.0);
// Show words setup.
self.word_list_ui(ui, self.mnemonic.mode() == PhraseMode::Import, cb);
self.word_list_ui(ui, self.mnemonic.mode() == PhraseMode::Import);
}
/// Draw content for phrase confirmation step.
@@ -101,7 +101,7 @@ impl MnemonicSetup {
ui.label(RichText::new(text).size(16.0).color(Colors::gray()));
});
ui.add_space(4.0);
self.word_list_ui(ui, true, cb);
self.word_list_ui(ui, true);
}
/// Draw mode and size setup.
@@ -149,7 +149,7 @@ impl MnemonicSetup {
}
/// Draw grid of words for mnemonic phrase.
fn word_list_ui(&mut self, ui: &mut egui::Ui, edit: bool, cb: &dyn PlatformCallbacks) {
fn word_list_ui(&mut self, ui: &mut egui::Ui, edit: bool) {
ui.add_space(6.0);
ui.scope(|ui| {
// Setup spacing between columns.
@@ -167,25 +167,25 @@ impl MnemonicSetup {
ui.columns(cols, |columns| {
columns[0].horizontal(|ui| {
let word = chunk.get(0).unwrap();
self.word_item_ui(ui, word_number, word, edit, cb);
self.word_item_ui(ui, word_number, word, edit);
});
columns[1].horizontal(|ui| {
word_number += 1;
let word = chunk.get(1).unwrap();
self.word_item_ui(ui, word_number, word, edit, cb);
self.word_item_ui(ui, word_number, word, edit);
});
if size > 2 {
columns[2].horizontal(|ui| {
word_number += 1;
let word = chunk.get(2).unwrap();
self.word_item_ui(ui, word_number, word, edit, cb);
self.word_item_ui(ui, word_number, word, edit);
});
}
if size > 3 {
columns[3].horizontal(|ui| {
word_number += 1;
let word = chunk.get(3).unwrap();
self.word_item_ui(ui, word_number, word, edit, cb);
self.word_item_ui(ui, word_number, word, edit);
});
}
});
@@ -193,7 +193,7 @@ impl MnemonicSetup {
ui.columns(cols, |columns| {
columns[0].horizontal(|ui| {
let word = chunk.get(0).unwrap();
self.word_item_ui(ui, word_number, word, edit, cb);
self.word_item_ui(ui, word_number, word, edit);
});
});
}
@@ -207,8 +207,7 @@ impl MnemonicSetup {
ui: &mut egui::Ui,
num: usize,
word: &PhraseWord,
edit: bool,
cb: &dyn PlatformCallbacks) {
edit: bool) {
let color = if !word.valid || (word.text.is_empty() && !self.mnemonic.valid()) {
Colors::red()
} else {
@@ -225,7 +224,6 @@ impl MnemonicSetup {
.position(ModalPosition::CenterTop)
.title(t!("wallets.recovery_phrase"))
.show();
cb.show_keyboard();
});
ui.label(RichText::new(format!("#{} {}", num, word.text))
.size(17.0)
@@ -276,7 +274,6 @@ impl MnemonicSetup {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -295,7 +292,6 @@ impl MnemonicSetup {
(!next_word.as_ref().unwrap().text.is_empty() &&
next_word.unwrap().valid);
if close_modal {
cb.hide_keyboard();
modal.close();
} else {
self.word_index_edit += 1;
+14 -3
View File
@@ -48,6 +48,8 @@ impl AddWalletModal {
cb: &dyn PlatformCallbacks,
mut on_input: impl FnMut(String, ZeroingString)) {
ui.add_space(6.0);
let mut enter_pressed = false;
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("wallets.name"))
.size(17.0)
@@ -62,8 +64,11 @@ impl AddWalletModal {
name_edit_opts.focus = true;
}
View::text_edit(ui, cb, &mut self.name_edit, &mut name_edit_opts);
ui.add_space(8.0);
View::on_enter_key(ui, || {
name_edit_opts.enter_pressed = name_edit_opts.focus;
});
ui.add_space(8.0);
ui.label(RichText::new(t!("wallets.pass"))
.size(17.0)
.color(Colors::gray()));
@@ -73,7 +78,12 @@ impl AddWalletModal {
let mut pass_text_edit_opts = TextEditOptions::new(Id::from(modal.id).with("pass"))
.password()
.no_focus();
if name_edit_opts.enter_pressed {
pass_text_edit_opts.focus = true;
}
View::text_edit(ui, cb, &mut self.pass_edit, &mut pass_text_edit_opts);
enter_pressed = pass_text_edit_opts.enter_pressed;
ui.add_space(12.0);
});
@@ -86,7 +96,6 @@ impl AddWalletModal {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -97,7 +106,6 @@ impl AddWalletModal {
if name.is_empty() || pass.is_empty() {
return;
}
cb.hide_keyboard();
modal.close();
on_input(name, ZeroingString::from(pass));
};
@@ -106,6 +114,9 @@ impl AddWalletModal {
View::on_enter_key(ui, || {
(on_next)();
});
if enter_pressed {
(on_next)();
}
View::button(ui, t!("continue"), Colors::white_or_black(false), on_next);
});
+6 -2
View File
@@ -52,6 +52,8 @@ impl OpenWalletModal {
cb: &dyn PlatformCallbacks,
mut on_continue: impl FnMut(Wallet, Option<String>)) {
ui.add_space(6.0);
let mut enter_pressed = false;
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("wallets.pass"))
.size(17.0)
@@ -61,6 +63,7 @@ impl OpenWalletModal {
// Show password input.
let mut pass_edit_opts = TextEditOptions::new(Id::from(modal.id)).password();
View::text_edit(ui, cb, &mut self.pass_edit, &mut pass_edit_opts);
enter_pressed = pass_edit_opts.enter_pressed;
// Show information when password is empty.
if self.pass_edit.is_empty() {
@@ -87,7 +90,6 @@ impl OpenWalletModal {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -101,7 +103,6 @@ impl OpenWalletModal {
match self.wallet.open(ZeroingString::from(pass)) {
Ok(_) => {
self.pass_edit = "".to_string();
cb.hide_keyboard();
modal.close();
on_continue(self.wallet.clone(), self.data.clone());
}
@@ -113,6 +114,9 @@ impl OpenWalletModal {
View::on_enter_key(ui, || {
(on_continue)();
});
if enter_pressed {
(on_continue)();
}
View::button(ui, t!("continue"), Colors::white_or_black(false), on_continue);
});
+2 -2
View File
@@ -138,7 +138,7 @@ impl WalletContent {
match result {
QrScanResult::Address(address) => {
self.current_tab =
Box::new(WalletTransport::new(Some(address.to_string()), cb));
Box::new(WalletTransport::new(Some(address.to_string())));
}
_ => {
self.current_tab =
@@ -409,7 +409,7 @@ impl WalletContent {
});
columns[2].vertical_centered_justified(|ui| {
View::tab_button(ui, BRIDGE, current_type == WalletTabType::Transport, |_| {
self.current_tab = Box::new(WalletTransport::new(None, cb));
self.current_tab = Box::new(WalletTransport::new(None));
});
});
columns[3].vertical_centered_justified(|ui| {
@@ -107,7 +107,7 @@ impl WalletMessages {
ui.add_space(3.0);
// Show creation of request to send or receive funds.
self.request_ui(ui, wallet, cb);
self.request_ui(ui, wallet);
ui.add_space(12.0);
View::horizontal_line(ui, Colors::item_stroke());
@@ -173,10 +173,7 @@ impl WalletMessages {
}
/// Draw creation of request to send or receive funds.
fn request_ui(&mut self,
ui: &mut egui::Ui,
wallet: &Wallet,
cb: &dyn PlatformCallbacks) {
fn request_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet) {
ui.label(RichText::new(t!("wallets.create_request_desc"))
.size(16.0)
.color(Colors::inactive_text()));
@@ -195,31 +192,31 @@ impl WalletMessages {
send_text,
Colors::red(),
Colors::white_or_black(false), || {
self.show_request_modal(false, cb);
self.show_request_modal(false);
});
});
columns[1].vertical_centered_justified(|ui| {
self.receive_button_ui(ui, cb);
self.receive_button_ui(ui);
});
});
} else {
self.receive_button_ui(ui, cb);
self.receive_button_ui(ui);
}
}
/// Draw invoice request creation button.
fn receive_button_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn receive_button_ui(&mut self, ui: &mut egui::Ui) {
let receive_text = format!("{} {}", DOWNLOAD_SIMPLE, t!("wallets.receive"));
View::colored_text_button(ui,
receive_text,
Colors::green(),
Colors::white_or_black(false), || {
self.show_request_modal(true, cb);
self.show_request_modal(true);
});
}
/// Show [`Modal`] to create invoice or sending request.
fn show_request_modal(&mut self, invoice: bool, cb: &dyn PlatformCallbacks) {
fn show_request_modal(&mut self, invoice: bool) {
self.request_modal_content = Some(MessageRequestModal::new(invoice));
let title = if invoice {
t!("wallets.receive")
@@ -230,7 +227,6 @@ impl WalletMessages {
.position(ModalPosition::CenterTop)
.title(title)
.show();
cb.show_keyboard();
}
/// Draw Slatepack message input content.
@@ -274,14 +270,8 @@ impl WalletMessages {
.desired_width(f32::INFINITY)
.show(ui)
.response;
// Show soft keyboard on click.
if resp.clicked() {
resp.request_focus();
cb.show_keyboard();
}
if resp.has_focus() {
// Apply text from input on Android as temporary fix for egui.
View::on_soft_input(ui, input_id, &mut self.message_edit);
}
ui.add_space(6.0);
});
@@ -102,7 +102,6 @@ impl MessageRequestModal {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
self.amount_edit = "".to_string();
self.request_error = None;
cb.hide_keyboard();
modal.close();
});
});
@@ -113,7 +112,6 @@ impl MessageRequestModal {
return;
}
if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) {
cb.hide_keyboard();
modal.disable_closing();
// Setup data for request.
let wallet = wallet.clone();
@@ -96,7 +96,6 @@ impl WalletAccountsModal {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -108,7 +107,6 @@ impl WalletAccountsModal {
match wallet.create_account(label) {
Ok(_) => {
let _ = wallet.set_active_account(label);
cb.hide_keyboard();
modal.close();
},
Err(_) => self.account_creation_error = true
@@ -165,7 +163,6 @@ impl WalletAccountsModal {
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("create"), Colors::white_or_black(false), || {
self.account_creating = true;
cb.show_keyboard();
});
});
});
@@ -87,7 +87,6 @@ impl CommonSettings {
.position(ModalPosition::CenterTop)
.title(t!("wallets.wallet"))
.show();
cb.show_keyboard();
});
ui.add_space(12.0);
@@ -109,7 +108,6 @@ impl CommonSettings {
.position(ModalPosition::CenterTop)
.title(t!("wallets.wallet"))
.show();
cb.show_keyboard();
});
ui.add_space(12.0);
@@ -127,7 +125,6 @@ impl CommonSettings {
.position(ModalPosition::CenterTop)
.title(t!("network_settings.change_value"))
.show();
cb.show_keyboard();
});
ui.add_space(12.0);
@@ -202,7 +199,6 @@ impl CommonSettings {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -211,7 +207,6 @@ impl CommonSettings {
let on_save = || {
if !self.name_edit.is_empty() {
wallet.change_name(self.name_edit.clone());
cb.hide_keyboard();
modal.close();
}
};
@@ -288,7 +283,6 @@ impl CommonSettings {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -306,7 +300,6 @@ impl CommonSettings {
self.old_pass_edit = "".to_string();
self.new_pass_edit = "".to_string();
// Close modal.
cb.hide_keyboard();
modal.close();
}
Err(_) => self.wrong_pass = true
@@ -361,7 +354,6 @@ impl CommonSettings {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
// Close modal.
cb.hide_keyboard();
modal.close();
});
});
@@ -370,7 +362,6 @@ impl CommonSettings {
let on_save = || {
if let Ok(min_conf) = self.min_confirmations_edit.parse::<u64>() {
wallet.update_min_confirmations(min_conf);
cb.hide_keyboard();
modal.close();
}
};
@@ -129,7 +129,6 @@ impl ConnectionSettings {
.position(ModalPosition::CenterTop)
.title(t!("wallets.add_node"))
.show();
cb.show_keyboard();
});
ui.add_space(4.0);
@@ -115,7 +115,7 @@ impl RecoverySettings {
// Draw button to show recovery phrase.
let show_text = format!("{} {}", EYE, t!("show"));
View::button(ui, show_text, Colors::white_or_black(false), || {
self.show_recovery_phrase_modal(cb);
self.show_recovery_phrase_modal();
});
ui.add_space(12.0);
@@ -164,7 +164,7 @@ impl RecoverySettings {
}
/// Show recovery phrase [`Modal`].
fn show_recovery_phrase_modal(&mut self, cb: &dyn PlatformCallbacks) {
fn show_recovery_phrase_modal(&mut self) {
// Setup modal values.
self.pass_edit = "".to_string();
self.wrong_pass = false;
@@ -174,7 +174,6 @@ impl RecoverySettings {
.position(ModalPosition::CenterTop)
.title(t!("wallets.recovery_phrase"))
.show();
cb.show_keyboard();
}
/// Draw recovery phrase [`Modal`] content.
@@ -242,7 +241,6 @@ impl RecoverySettings {
Ok(phrase) => {
self.wrong_pass = false;
self.recovery_phrase = Some(phrase);
cb.hide_keyboard();
}
Err(_) => {
self.wrong_pass = true;
@@ -61,14 +61,14 @@ const QR_ADDRESS_MODAL: &'static str = "qr_address_modal";
impl WalletTransport {
/// Create new transport content instance, opening sending `Modal` if address was provided.
pub fn new(address: Option<String>, cb: &dyn PlatformCallbacks) -> Self {
pub fn new(address: Option<String>) -> Self {
let mut content = Self {
send_modal_content: None,
qr_address_content: None,
settings_modal_content: None,
};
if address.is_some() {
content.show_send_tor_modal(cb, address)
content.show_send_tor_modal(address)
}
content
}
@@ -135,7 +135,7 @@ impl WalletTransport {
let service_id = &wallet.identifier();
if data.info.amount_currently_spendable > 0 && wallet.foreign_api_port().is_some() &&
!Tor::is_service_starting(service_id) {
self.tor_send_ui(ui, cb);
self.tor_send_ui(ui);
}
}
@@ -330,7 +330,7 @@ impl WalletTransport {
}
/// Draw Tor send content.
fn tor_send_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
fn tor_send_ui(&mut self, ui: &mut egui::Ui) {
// Setup layout size.
let mut rect = ui.available_rect_before_wrap();
rect.set_height(55.0);
@@ -346,20 +346,19 @@ impl WalletTransport {
// Draw button to open sending modal.
let send_text = format!("{} {}", EXPORT, t!("wallets.send"));
View::button(ui, send_text, Colors::white_or_black(false), || {
self.show_send_tor_modal(cb, None);
self.show_send_tor_modal(None);
});
});
});
}
/// Show [`Modal`] to send over Tor.
fn show_send_tor_modal(&mut self, cb: &dyn PlatformCallbacks, address: Option<String>) {
fn show_send_tor_modal(&mut self, address: Option<String>) {
self.send_modal_content = Some(TransportSendModal::new(address));
// Show modal.
Modal::new(SEND_TOR_MODAL)
.position(ModalPosition::CenterTop)
.title(t!("wallets.send"))
.show();
cb.show_keyboard();
}
}
+12 -18
View File
@@ -85,14 +85,17 @@ impl TransportSendModal {
if self.sending {
self.progress_ui(ui, wallet);
} else if self.error {
self.error_ui(ui, wallet, modal, cb);
self.error_ui(ui, wallet, modal);
} else {
self.content_ui(ui, wallet, modal, cb);
}
}
/// Draw content to send.
fn content_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, modal: &Modal,
fn content_ui(&mut self,
ui: &mut egui::Ui,
wallet: &Wallet,
modal: &Modal,
cb: &dyn PlatformCallbacks) {
ui.add_space(6.0);
// Draw QR code scanner content if requested.
@@ -107,7 +110,6 @@ impl TransportSendModal {
self.address_edit = result.text();
on_stop();
self.address_scan_content = None;
cb.show_keyboard();
} else {
scanner.ui(ui, cb);
ui.add_space(6.0);
@@ -128,7 +130,6 @@ impl TransportSendModal {
View::button(ui, t!("back"), Colors::white_or_black(false), || {
on_stop();
self.address_scan_content = None;
cb.show_keyboard();
});
});
});
@@ -217,7 +218,6 @@ impl TransportSendModal {
View::text_edit(ui, cb, &mut self.address_edit, &mut address_edit_opts);
// Check if scan button was pressed.
if address_edit_opts.scan_pressed {
cb.hide_keyboard();
modal.disable_closing();
address_edit_opts.scan_pressed = false;
self.address_scan_content = Some(CameraContent::default());
@@ -235,12 +235,12 @@ impl TransportSendModal {
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
self.close(modal, cb);
self.close(modal);
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("continue"), Colors::white_or_black(false), || {
self.send(wallet, modal, cb);
self.send(wallet, modal);
});
});
});
@@ -248,11 +248,7 @@ impl TransportSendModal {
}
/// Draw error content.
fn error_ui(&mut self,
ui: &mut egui::Ui,
wallet: &Wallet,
modal: &Modal,
cb: &dyn PlatformCallbacks) {
fn error_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, modal: &Modal) {
ui.add_space(6.0);
ui.vertical_centered(|ui| {
ui.label(RichText::new(t!("transport.tor_send_error"))
@@ -267,12 +263,12 @@ impl TransportSendModal {
ui.columns(2, |columns| {
columns[0].vertical_centered_justified(|ui| {
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
self.close(modal, cb);
self.close(modal);
});
});
columns[1].vertical_centered_justified(|ui| {
View::button(ui, t!("repeat"), Colors::white_or_black(false), || {
self.send(wallet, modal, cb);
self.send(wallet, modal);
});
});
});
@@ -280,7 +276,7 @@ impl TransportSendModal {
}
/// Close modal and clear data.
fn close(&mut self, modal: &Modal, cb: &dyn PlatformCallbacks) {
fn close(&mut self, modal: &Modal) {
self.amount_edit = "".to_string();
self.address_edit = "".to_string();
@@ -290,19 +286,17 @@ impl TransportSendModal {
self.tx_info_content = None;
self.address_scan_content = None;
cb.hide_keyboard();
modal.close();
}
/// Send entered amount to address.
fn send(&mut self, wallet: &Wallet, modal: &Modal, cb: &dyn PlatformCallbacks) {
fn send(&mut self, wallet: &Wallet, modal: &Modal) {
if self.amount_edit.is_empty() {
return;
}
let addr_str = self.address_edit.as_str();
if let Ok(addr) = SlatepackAddress::try_from(addr_str.trim()) {
if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) {
cb.hide_keyboard();
modal.disable_closing();
// Send amount over Tor.
let mut wallet = wallet.clone();
@@ -72,7 +72,6 @@ impl TransportSettingsModal {
self.bridge_conn_line_edit = result.text();
on_stop();
self.bridge_qr_scan_content = None;
cb.show_keyboard();
} else {
scanner.ui(ui, cb);
ui.add_space(12.0);
@@ -191,7 +190,6 @@ impl TransportSettingsModal {
View::text_edit(ui, cb, &mut self.bridge_conn_line_edit, &mut conn_edit_opts);
// Check if scan button was pressed.
if conn_edit_opts.scan_pressed {
cb.hide_keyboard();
modal.disable_closing();
conn_edit_opts.scan_pressed = false;
self.bridge_qr_scan_content = Some(CameraContent::default());
+4 -9
View File
@@ -61,7 +61,7 @@ impl WalletTab for WalletTransactions {
fn ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
self.modal_content_ui(ui, wallet, cb);
self.txs_ui(ui, wallet, cb);
self.txs_ui(ui, wallet);
}
}
@@ -75,10 +75,7 @@ impl WalletTransactions {
pub const TX_ITEM_HEIGHT: f32 = 75.0;
/// Draw transactions content.
fn txs_ui(&mut self,
ui: &mut egui::Ui,
wallet: &Wallet,
cb: &dyn PlatformCallbacks) {
fn txs_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet) {
let data = wallet.get_data().unwrap();
if data.txs.is_none() {
ui.centered_and_justified(|ui| {
@@ -123,7 +120,7 @@ impl WalletTransactions {
.show_rows(ui, Self::TX_ITEM_HEIGHT, txs.len(), |ui, row_range| {
ui.add_space(1.0);
View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| {
self.tx_list_ui(ui, awaiting_amount, row_range, wallet, txs, cb);
self.tx_list_ui(ui, awaiting_amount, row_range, wallet, txs);
});
})
});
@@ -143,8 +140,7 @@ impl WalletTransactions {
awaiting: bool,
row_range: Range<usize>,
wallet: &Wallet,
txs: &Vec<WalletTransaction>,
cb: &dyn PlatformCallbacks) {
txs: &Vec<WalletTransaction>) {
for index in row_range {
let mut rect = if awaiting {
let mut rect = ui.available_rect_before_wrap();
@@ -179,7 +175,6 @@ impl WalletTransactions {
if wallet_loaded && tx.can_finalize {
let (icon, color) = (CHECK, Some(Colors::green()));
View::item_button(ui, Rounding::default(), icon, color, || {
cb.hide_keyboard();
self.show_tx_info_modal(wallet, tx, true);
});
}
+5 -23
View File
@@ -116,7 +116,6 @@ impl WalletTransactionModal {
.filter(|tx| tx.data.id == self.tx_id)
.collect::<Vec<WalletTransaction>>();
if txs.is_empty() {
cb.hide_keyboard();
modal.close();
return;
}
@@ -178,7 +177,6 @@ impl WalletTransactionModal {
// Show button to close modal.
ui.vertical_centered_justified(|ui| {
View::button(ui, t!("close"), Colors::white_or_black(false), || {
cb.hide_keyboard();
modal.close();
});
});
@@ -266,7 +264,6 @@ impl WalletTransactionModal {
};
let r = View::item_rounding(0, 2, true);
View::item_button(ui, r, icon, color, || {
cb.hide_keyboard();
if self.show_finalization {
self.show_finalization = false;
return;
@@ -282,7 +279,6 @@ impl WalletTransactionModal {
View::item_rounding(0, 2, true)
};
View::item_button(ui, r, PROHIBIT, Some(Colors::red()), || {
cb.hide_keyboard();
wallet.cancel(tx.data.id);
});
}
@@ -321,7 +317,7 @@ impl WalletTransactionModal {
// Setup value to finalization input field.
self.finalize_edit = result.text();
self.on_finalization_input_change(tx, wallet, modal, cb);
self.on_finalization_input_change(tx, wallet, modal);
modal.enable_closing();
self.scan_qr_content = None;
@@ -409,14 +405,8 @@ impl WalletTransactionModal {
.hint_text(SLATEPACK_MESSAGE_HINT)
.desired_width(f32::INFINITY)
.show(ui).response;
// Show soft keyboard on click.
if self.show_finalization && resp.clicked() {
resp.request_focus();
cb.show_keyboard();
}
if self.show_finalization && resp.has_focus() {
// Apply text from input on Android as temporary fix for egui.
View::on_soft_input(ui, input_id, message_edit);
}
ui.add_space(6.0);
});
@@ -440,7 +430,6 @@ impl WalletTransactionModal {
// Draw button to scan Slatepack message QR code.
let qr_text = format!("{} {}", SCAN, t!("scan"));
View::button(ui, qr_text, Colors::fill_lite(), || {
cb.hide_keyboard();
modal.disable_closing();
cb.start_camera();
self.scan_qr_content = Some(CameraContent::default());
@@ -473,7 +462,7 @@ impl WalletTransactionModal {
// Callback on finalization message input change.
if message_before != self.finalize_edit {
self.on_finalization_input_change(tx, wallet, modal, cb);
self.on_finalization_input_change(tx, wallet, modal);
}
} else {
ui.columns(2, |columns| {
@@ -481,7 +470,6 @@ impl WalletTransactionModal {
// Draw button to show Slatepack message as QR code.
let qr_text = format!("{} {}", QR_CODE, t!("qr_code"));
View::button(ui, qr_text.clone(), Colors::white_or_black(false), || {
cb.hide_keyboard();
let text = self.response_edit.clone();
self.qr_code_content = Some(QrCodeContent::new(text, true));
});
@@ -495,7 +483,6 @@ impl WalletTransactionModal {
if tx.can_finalize {
self.show_finalization = true;
} else {
cb.hide_keyboard();
modal.close();
}
});
@@ -521,27 +508,22 @@ impl WalletTransactionModal {
}
/// Parse Slatepack message on transaction finalization input change.
fn on_finalization_input_change(&mut self,
tx: &WalletTransaction,
wallet: &Wallet,
modal: &Modal,
cb: &dyn PlatformCallbacks) {
fn on_finalization_input_change(&mut self, tx: &WalletTransaction, w: &Wallet, modal: &Modal) {
let message = &self.finalize_edit;
if message.is_empty() {
self.finalize_error = false;
} else {
// Parse input message to finalize.
if let Ok(slate) = wallet.parse_slatepack(message) {
if let Ok(slate) = w.parse_slatepack(message) {
let send = slate.state == SlateState::Standard2 &&
tx.data.tx_type == TxLogEntryType::TxSent;
let receive = slate.state == SlateState::Invoice2 &&
tx.data.tx_type == TxLogEntryType::TxReceived;
if Some(slate.id) == tx.data.tx_slate_id && (send || receive) {
let message = message.clone();
let wallet = wallet.clone();
let wallet = w.clone();
let final_res = self.final_result.clone();
// Finalize transaction at separate thread.
cb.hide_keyboard();
self.finalizing = true;
modal.disable_closing();
thread::spawn(move || {