mirror of
https://code.gri.mw/GUI/grim.git
synced 2026-07-13 10:18:54 +00:00
ui: input refactoring
This commit is contained in:
+1
-1
@@ -139,7 +139,7 @@ impl<Platform: PlatformCallbacks> App<Platform> {
|
||||
}
|
||||
|
||||
// Show soft keyboard content.
|
||||
self.keyboard_content.ui(ctx);
|
||||
self.keyboard_content.window_ui(ctx);
|
||||
|
||||
// Provide incoming data to wallets.
|
||||
if let Some(data) = crate::consume_incoming_data() {
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
// 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 eframe::emath::Align;
|
||||
use egui::{Layout, TextBuffer, TextStyle, Widget};
|
||||
use egui::text_edit::TextEditState;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CLIPBOARD_TEXT, COPY, EYE, EYE_SLASH, SCAN};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::input::keyboard::KeyboardContent;
|
||||
use crate::gui::views::{KeyboardEvent, View};
|
||||
|
||||
/// Text input content.
|
||||
pub struct TextEdit {
|
||||
/// View identifier.
|
||||
pub id: egui::Id,
|
||||
/// Check if horizontal centering is needed.
|
||||
pub h_center: bool,
|
||||
/// Check if focus is needed.
|
||||
pub focus: bool,
|
||||
/// Check if focus request was passed.
|
||||
focus_request: bool,
|
||||
/// Hide letters and draw button to show/hide letters.
|
||||
pub password: bool,
|
||||
/// Show copy button.
|
||||
pub copy: bool,
|
||||
/// Show paste button.
|
||||
pub paste: bool,
|
||||
/// Show button to scan QR code into text.
|
||||
pub scan_qr: bool,
|
||||
/// Callback when scan button was pressed.
|
||||
pub scan_pressed: bool,
|
||||
/// Callback when Enter key was pressed.
|
||||
pub enter_pressed: bool,
|
||||
/// Flag to enter only numbers.
|
||||
pub numeric: bool,
|
||||
}
|
||||
|
||||
impl TextEdit {
|
||||
/// Default height of [`egui::TextEdit`] view.
|
||||
const TEXT_EDIT_HEIGHT: f32 = 37.0;
|
||||
|
||||
pub fn new(id: egui::Id) -> Self {
|
||||
Self {
|
||||
id,
|
||||
h_center: false,
|
||||
focus: true,
|
||||
focus_request: false,
|
||||
password: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
scan_qr: false,
|
||||
scan_pressed: false,
|
||||
enter_pressed: false,
|
||||
numeric: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw text input content.
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui, input: &mut String, cb: &dyn PlatformCallbacks) {
|
||||
let mut layout_rect = ui.available_rect_before_wrap();
|
||||
layout_rect.set_height(Self::TEXT_EDIT_HEIGHT);
|
||||
ui.allocate_ui_with_layout(layout_rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
||||
let mut hide_input = false;
|
||||
if self.password {
|
||||
let show_pass_id = egui::Id::new(self.id).with("_show_pass");
|
||||
hide_input = ui.data(|data| {
|
||||
data.get_temp(show_pass_id)
|
||||
}).unwrap_or(true);
|
||||
// Draw button to show/hide current password.
|
||||
let eye_icon = if hide_input { EYE } else { EYE_SLASH };
|
||||
View::button_ui(ui, eye_icon.to_string(), Colors::white_or_black(false), |ui| {
|
||||
hide_input = !hide_input;
|
||||
ui.data_mut(|data| {
|
||||
data.insert_temp(show_pass_id, hide_input);
|
||||
});
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
// Setup copy button.
|
||||
if self.copy {
|
||||
let copy_icon = COPY.to_string();
|
||||
View::button(ui, copy_icon, Colors::white_or_black(false), || {
|
||||
cb.copy_string_to_buffer(input.clone());
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
// Setup paste button.
|
||||
if self.paste {
|
||||
let paste_icon = CLIPBOARD_TEXT.to_string();
|
||||
View::button(ui, paste_icon, Colors::white_or_black(false), || {
|
||||
*input = cb.get_string_from_buffer();
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
// Setup scan QR code button.
|
||||
if self.scan_qr {
|
||||
let scan_icon = SCAN.to_string();
|
||||
View::button(ui, scan_icon, Colors::white_or_black(false), || {
|
||||
cb.start_camera();
|
||||
self.scan_pressed = true;
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
let layout_size = ui.available_size();
|
||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||
// Setup text edit size.
|
||||
let mut edit_rect = ui.available_rect_before_wrap();
|
||||
edit_rect.set_height(Self::TEXT_EDIT_HEIGHT);
|
||||
|
||||
// Setup focused input value.
|
||||
let focused_input_id = egui::Id::new("focused_input_id");
|
||||
let focused = ui.data(|data| {
|
||||
data.get_temp(focused_input_id)
|
||||
}).unwrap_or(egui::Id::new("")) == self.id;
|
||||
|
||||
// Show text edit.
|
||||
let text_edit_resp = egui::TextEdit::singleline(input)
|
||||
.id(self.id)
|
||||
.margin(egui::Vec2::new(2.0, 0.0))
|
||||
.font(TextStyle::Heading)
|
||||
.min_size(edit_rect.size())
|
||||
.horizontal_align(if self.h_center { Align::Center } else { Align::Min })
|
||||
.vertical_align(Align::Center)
|
||||
.password(hide_input)
|
||||
.cursor_at_end(true)
|
||||
.ui(ui);
|
||||
|
||||
// Setup focus state.
|
||||
let clicked = text_edit_resp.clicked();
|
||||
if !text_edit_resp.has_focus() &&
|
||||
(self.focus || self.focus_request || clicked || focused) {
|
||||
text_edit_resp.request_focus();
|
||||
}
|
||||
|
||||
// Apply text from software input.
|
||||
if text_edit_resp.has_focus() {
|
||||
ui.data_mut(|data| {
|
||||
data.insert_temp(focused_input_id, self.id);
|
||||
});
|
||||
self.enter_pressed = self.on_soft_input(ui, self.id, false, input);
|
||||
// Check Enter key input.
|
||||
if !self.focus_request {
|
||||
if ui.ctx().input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
self.enter_pressed = true;
|
||||
}
|
||||
}
|
||||
if self.enter_pressed {
|
||||
KeyboardContent::unshift();
|
||||
}
|
||||
KeyboardContent::show(self.numeric);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Apply soft keyboard input data to provided String, returns `true` if Enter was pressed.
|
||||
fn on_soft_input(&self, ui: &mut egui::Ui, id: egui::Id, multiline: bool, value: &mut String)
|
||||
-> bool {
|
||||
if let Some(input) = KeyboardContent::consume_event() {
|
||||
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;
|
||||
|
||||
let selected = r.primary.index != r.secondary.index;
|
||||
let start_select = f32::min(r.primary.index as f32,
|
||||
r.secondary.index as f32) as usize;
|
||||
let end_select = f32::max(r.primary.index as f32,
|
||||
r.secondary.index as f32) as usize;
|
||||
match input {
|
||||
KeyboardEvent::TEXT(text) => {
|
||||
if selected {
|
||||
*value = {
|
||||
let part1: String = value.chars()
|
||||
.skip(0)
|
||||
.take(start_select)
|
||||
.collect();
|
||||
let part2: String = value.chars()
|
||||
.skip(end_select)
|
||||
.take(value.len() - end_select)
|
||||
.collect();
|
||||
format!("{}{}{}", part1, text, part2)
|
||||
};
|
||||
index = start_select + 1;
|
||||
} else {
|
||||
value.insert_text(text.as_str(), index);
|
||||
index = index + 1;
|
||||
}
|
||||
}
|
||||
KeyboardEvent::CLEAR => {
|
||||
if selected {
|
||||
*value = {
|
||||
let part1: String = value.chars()
|
||||
.skip(0)
|
||||
.take(start_select)
|
||||
.collect();
|
||||
let part2: String = value.chars()
|
||||
.skip(end_select)
|
||||
.take(value.len() - end_select)
|
||||
.collect();
|
||||
format!("{}{}", part1, part2)
|
||||
};
|
||||
index = start_select;
|
||||
} else 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;
|
||||
}
|
||||
}
|
||||
KeyboardEvent::ENTER => {
|
||||
if multiline {
|
||||
value.insert_text("\n", index);
|
||||
index = index + 1;
|
||||
} else {
|
||||
enter_pressed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
return enter_pressed;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Center text horizontally.
|
||||
pub fn h_center(mut self) -> Self {
|
||||
self.h_center = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable or disable constant focus.
|
||||
pub fn focus(mut self, focus: bool) -> Self {
|
||||
self.focus = focus;
|
||||
self
|
||||
}
|
||||
|
||||
/// Focus on field.
|
||||
pub fn focus_request(&mut self) {
|
||||
self.focus_request = true;
|
||||
}
|
||||
|
||||
/// Allow input of numbers only.
|
||||
pub fn numeric(mut self) -> Self {
|
||||
self.numeric = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Hide letters and draw button to show/hide letters.
|
||||
pub fn password(mut self) -> Self {
|
||||
self.password = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show button to copy text.
|
||||
pub fn copy(mut self) -> Self {
|
||||
self.copy = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show button to paste text.
|
||||
pub fn paste(mut self) -> Self {
|
||||
self.paste = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show button to scan QR code to text.
|
||||
pub fn scan_qr(mut self) -> Self {
|
||||
self.scan_qr = true;
|
||||
self.scan_pressed = false;
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -19,39 +19,32 @@ 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::AppConfig;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{ARROW_FAT_UP, BACKSPACE, KEY_RETURN, TRANSLATE};
|
||||
use crate::gui::views::View;
|
||||
use crate::gui::views::{KeyboardEvent, KeyboardLayout, 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.
|
||||
/// Last input event.
|
||||
static ref LAST_EVENT: Arc<RwLock<Option<KeyboardEvent >>> = Arc::new(RwLock::new(None));
|
||||
/// Flag to show 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
|
||||
/// Flag to enable text shifting.
|
||||
static ref UPPERCASE: AtomicBool = AtomicBool::new(false);
|
||||
/// Flag to show numeric layout.
|
||||
static ref NUMERIC: AtomicBool = AtomicBool::new(false);
|
||||
}
|
||||
|
||||
/// Software keyboard content.
|
||||
pub struct KeyboardContent {
|
||||
/// Flag to check if uppercase text entering is enabled.
|
||||
uppercase: bool,
|
||||
/// Flag to show symbols layout.
|
||||
symbols: bool,
|
||||
/// Keyboard layout.
|
||||
mode: KeyboardLayout
|
||||
}
|
||||
|
||||
impl Default for KeyboardContent {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
uppercase: false,
|
||||
symbols: false,
|
||||
mode: KeyboardLayout::TEXT,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,8 +55,8 @@ impl KeyboardContent {
|
||||
/// Buttons content margin.
|
||||
const MARGIN: f32 = 5.0;
|
||||
|
||||
/// Draw keyboard content.
|
||||
pub fn ui(&mut self, ctx: &egui::Context) {
|
||||
/// Draw keyboard content as separate [`Window`].
|
||||
pub fn window_ui(&mut self, ctx: &egui::Context) {
|
||||
if !KeyboardContent::showing() {
|
||||
return;
|
||||
}
|
||||
@@ -80,7 +73,7 @@ impl KeyboardContent {
|
||||
offset: Default::default(),
|
||||
blur: 30.0,
|
||||
spread: 3.0,
|
||||
color: egui::Color32::from_black_alpha(32),
|
||||
color: Color32::from_black_alpha(32),
|
||||
},
|
||||
inner_margin: Margin {
|
||||
left: View::get_left_inset() + Self::MARGIN,
|
||||
@@ -98,66 +91,7 @@ impl KeyboardContent {
|
||||
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;
|
||||
});
|
||||
});
|
||||
self.ui(ui);
|
||||
});
|
||||
}).unwrap().response.layer_id;
|
||||
|
||||
@@ -165,13 +99,79 @@ impl KeyboardContent {
|
||||
ctx.move_to_top(layer_id);
|
||||
}
|
||||
|
||||
/// Draw keyboard content.
|
||||
pub fn ui(&mut self, ui: &mut egui::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 = match self.mode {
|
||||
KeyboardLayout::TEXT => Self::text_ui(ui),
|
||||
KeyboardLayout::SYMBOLS => Self::symbols_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::input_event(KeyboardEvent::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::input_event(KeyboardEvent::TEXT(l));
|
||||
});
|
||||
});
|
||||
ui.horizontal_centered(|ui| {
|
||||
ui.set_max_width(button_width);
|
||||
Self::button_ui(TRANSLATE,
|
||||
Colors::text_button(),
|
||||
Some(Colors::fill_lite()),
|
||||
ui,
|
||||
|_| {
|
||||
AppConfig::toggle_english_keyboard()
|
||||
});
|
||||
});
|
||||
ui.horizontal_centered(|ui| {
|
||||
let label = if self.mode == KeyboardLayout::SYMBOLS {
|
||||
"ABC"
|
||||
} else {
|
||||
"?/ツ"
|
||||
};
|
||||
let mut mode = self.mode.clone();
|
||||
Self::button_ui(label, Colors::text_button(), Some(Colors::fill_lite()), ui, |_| {
|
||||
if self.mode == KeyboardLayout::SYMBOLS {
|
||||
mode = KeyboardLayout::TEXT;
|
||||
} else {
|
||||
mode = KeyboardLayout::SYMBOLS;
|
||||
}
|
||||
});
|
||||
self.mode = mode;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw text content returning button [`Rect`].
|
||||
fn text_ui(&mut self, ui: &mut egui::Ui) -> Rect {
|
||||
fn text_ui(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]);
|
||||
button_rect = Self::input_button_ui(s, &mut columns[index]);
|
||||
}
|
||||
});
|
||||
ui.add_space(View::TAB_ITEMS_PADDING);
|
||||
@@ -179,7 +179,7 @@ impl KeyboardContent {
|
||||
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]);
|
||||
Self::input_button_ui(s, &mut columns[index]);
|
||||
}
|
||||
});
|
||||
ui.add_space(View::TAB_ITEMS_PADDING);
|
||||
@@ -187,7 +187,7 @@ impl KeyboardContent {
|
||||
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]);
|
||||
Self::input_button_ui(s, &mut columns[index]);
|
||||
}
|
||||
});
|
||||
ui.add_space(View::TAB_ITEMS_PADDING);
|
||||
@@ -197,27 +197,26 @@ impl KeyboardContent {
|
||||
for (index, s) in tl_3.iter().enumerate() {
|
||||
if index == 0 {
|
||||
// Check for shift input.
|
||||
let color = if self.uppercase {
|
||||
let uppercase = UPPERCASE.load(Ordering::Relaxed);
|
||||
let color = if 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::button_ui(s, color, Some(Colors::fill_lite()), &mut columns[index], |_| {
|
||||
UPPERCASE.store(!uppercase, Ordering::Relaxed);
|
||||
});
|
||||
self.uppercase = uppercase;
|
||||
} else if index == tl_3.len() - 1 {
|
||||
// Check for backspace input.
|
||||
self.button_ui(s,
|
||||
Self::button_ui(s,
|
||||
Colors::red(),
|
||||
Some(Colors::fill_lite()),
|
||||
&mut columns[index],
|
||||
|_| {
|
||||
Self::on_input(KeyboardInput::CLEAR);
|
||||
Self::input_event(KeyboardEvent::CLEAR);
|
||||
});
|
||||
} else {
|
||||
self.input_button_ui(s, &mut columns[index]);
|
||||
Self::input_button_ui(s, &mut columns[index]);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -225,12 +224,12 @@ impl KeyboardContent {
|
||||
}
|
||||
|
||||
/// Draw symbols content returning button [`Rect`].
|
||||
fn symbols_ui(&mut self, ui: &mut egui::Ui) -> Rect {
|
||||
fn symbols_ui(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]);
|
||||
button_rect = Self::input_button_ui(s, &mut columns[index]);
|
||||
}
|
||||
});
|
||||
ui.add_space(View::TAB_ITEMS_PADDING);
|
||||
@@ -238,7 +237,7 @@ impl KeyboardContent {
|
||||
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]);
|
||||
Self::input_button_ui(s, &mut columns[index]);
|
||||
}
|
||||
});
|
||||
ui.add_space(View::TAB_ITEMS_PADDING);
|
||||
@@ -246,7 +245,7 @@ impl KeyboardContent {
|
||||
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]);
|
||||
Self::input_button_ui(s, &mut columns[index]);
|
||||
}
|
||||
});
|
||||
ui.add_space(View::TAB_ITEMS_PADDING);
|
||||
@@ -256,14 +255,14 @@ impl KeyboardContent {
|
||||
for (index, s) in tl_3.iter().enumerate() {
|
||||
if index == tl_3.len() - 1 {
|
||||
// Check for backspace input.
|
||||
self.button_ui(s,
|
||||
Self::button_ui(s,
|
||||
Colors::red(),
|
||||
Some(Colors::fill_lite()),
|
||||
&mut columns[index], |_| {
|
||||
Self::on_input(KeyboardInput::CLEAR);
|
||||
Self::input_event(KeyboardEvent::CLEAR);
|
||||
});
|
||||
} else {
|
||||
self.input_button_ui(s, &mut columns[index]);
|
||||
Self::input_button_ui(s, &mut columns[index]);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -272,8 +271,7 @@ impl KeyboardContent {
|
||||
}
|
||||
|
||||
/// Draw keyboard button.
|
||||
fn button_ui(&self,
|
||||
s: &str,
|
||||
fn button_ui(s: &str,
|
||||
color: Color32,
|
||||
bg: Option<Color32>,
|
||||
ui: &mut egui::Ui,
|
||||
@@ -291,7 +289,7 @@ impl KeyboardContent {
|
||||
ui.visuals_mut().widgets.hovered.bg_stroke = View::hover_stroke();
|
||||
ui.visuals_mut().widgets.active.bg_stroke = Stroke::NONE;
|
||||
|
||||
let label = if self.uppercase {
|
||||
let label = if UPPERCASE.load(Ordering::Relaxed) {
|
||||
s.to_uppercase()
|
||||
} else {
|
||||
s.to_string()
|
||||
@@ -308,32 +306,28 @@ impl KeyboardContent {
|
||||
}
|
||||
|
||||
/// 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));
|
||||
fn input_button_ui(s: &str, ui: &mut egui::Ui) -> Rect {
|
||||
let rect = Self::button_ui(s, Colors::text_button(), None, ui, |l| {
|
||||
Self::input_event(KeyboardEvent::TEXT(l));
|
||||
UPPERCASE.store(false, Ordering::Relaxed);
|
||||
}).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);
|
||||
/// Save keyboard event to consume later.
|
||||
fn input_event(event: KeyboardEvent) {
|
||||
let mut w_input = LAST_EVENT.write();
|
||||
*w_input = Some(event);
|
||||
}
|
||||
|
||||
/// Check last keyboard input action.
|
||||
pub fn consume_action() -> Option<KeyboardInput> {
|
||||
/// Check last keyboard input event.
|
||||
pub fn consume_event() -> Option<KeyboardEvent> {
|
||||
let empty = {
|
||||
let r_input = LAST_INPUT.read();
|
||||
let r_input = LAST_EVENT.read();
|
||||
r_input.is_none()
|
||||
};
|
||||
if !empty {
|
||||
let mut w_input = LAST_INPUT.write();
|
||||
let mut w_input = LAST_EVENT.write();
|
||||
let res = w_input.clone();
|
||||
*w_input = None;
|
||||
return res;
|
||||
@@ -347,10 +341,21 @@ impl KeyboardContent {
|
||||
}
|
||||
|
||||
/// Show keyboard.
|
||||
pub fn show() {
|
||||
pub fn show(numeric: bool) {
|
||||
NUMERIC.store(numeric, Ordering::Relaxed);
|
||||
SHOW_KEYBOARD.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Emulate Shift key pressing.
|
||||
pub fn shift() {
|
||||
UPPERCASE.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Emulate Shift key pressing.
|
||||
pub fn unshift() {
|
||||
UPPERCASE.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Hide keyboard.
|
||||
pub fn hide() {
|
||||
SHOW_KEYBOARD.store(false, Ordering::Relaxed);
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.
|
||||
|
||||
mod types;
|
||||
pub use types::*;
|
||||
|
||||
mod edit;
|
||||
pub use edit::*;
|
||||
|
||||
mod keyboard;
|
||||
pub use keyboard::*;
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
|
||||
/// Software keyboard input type.
|
||||
#[derive(Clone, PartialOrd, PartialEq)]
|
||||
pub enum KeyboardLayout {
|
||||
TEXT, SYMBOLS
|
||||
}
|
||||
|
||||
/// Software keyboard input event.
|
||||
#[derive(Clone)]
|
||||
pub enum KeyboardEvent {
|
||||
TEXT(String), CLEAR, ENTER
|
||||
}
|
||||
@@ -45,5 +45,5 @@ pub use pull_to_refresh::*;
|
||||
mod scan;
|
||||
pub use scan::*;
|
||||
|
||||
mod keyboard;
|
||||
pub use keyboard::*;
|
||||
mod input;
|
||||
pub use input::*;
|
||||
+20
-2
@@ -40,6 +40,8 @@ pub struct Modal {
|
||||
closeable: Arc<AtomicBool>,
|
||||
/// Title text.
|
||||
title: Option<String>,
|
||||
/// Flag to check first content render.
|
||||
first_draw: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl Modal {
|
||||
@@ -55,6 +57,7 @@ impl Modal {
|
||||
position: ModalPosition::Center,
|
||||
closeable: Arc::new(AtomicBool::new(true)),
|
||||
title: None,
|
||||
first_draw: Arc::new(AtomicBool::new(true)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +77,6 @@ impl Modal {
|
||||
pub fn close(&self) {
|
||||
let mut w_nav = MODAL_STATE.write();
|
||||
w_nav.modal = None;
|
||||
// Hide keyboard.
|
||||
KeyboardContent::hide();
|
||||
}
|
||||
|
||||
@@ -157,7 +159,6 @@ impl Modal {
|
||||
|
||||
/// Set title text for current opened [`Modal`].
|
||||
pub fn set_title(title: String) {
|
||||
// Save state.
|
||||
let mut w_state = MODAL_STATE.write();
|
||||
if w_state.modal.is_some() {
|
||||
let mut modal = w_state.modal.clone().unwrap();
|
||||
@@ -166,6 +167,16 @@ impl Modal {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for first [`Modal`] content rendering.
|
||||
pub fn first_draw() -> bool {
|
||||
if Self::opened().is_none() {
|
||||
return false;
|
||||
}
|
||||
let r_state = MODAL_STATE.read();
|
||||
let modal = r_state.modal.as_ref().unwrap();
|
||||
modal.first_draw.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Draw opened [`Modal`] content.
|
||||
pub fn ui(ctx: &egui::Context, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
|
||||
let has_modal = {
|
||||
@@ -247,6 +258,13 @@ impl Modal {
|
||||
|
||||
// Always show main content window above background window.
|
||||
ctx.move_to_top(layer_id);
|
||||
|
||||
// Setup first draw flag.
|
||||
if Self::first_draw() {
|
||||
let r_state = MODAL_STATE.read();
|
||||
let modal = r_state.modal.as_ref().unwrap();
|
||||
modal.first_draw.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get [`egui::Window`] position based on [`ModalPosition`].
|
||||
|
||||
@@ -14,16 +14,14 @@
|
||||
|
||||
use egui::{Id, RichText};
|
||||
use url::Url;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::types::TextEditOptions;
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::wallet::{ConnectionsConfig, ExternalConnection};
|
||||
|
||||
/// Content to create or update external wallet connection.
|
||||
pub struct ExternalConnectionModal {
|
||||
/// Flag to check if [`Modal`] was just opened to focus on input field.
|
||||
first_modal_launch: bool,
|
||||
/// External connection URL value for [`Modal`].
|
||||
ext_node_url_edit: String,
|
||||
/// External connection API secret value for [`Modal`].
|
||||
@@ -34,8 +32,6 @@ pub struct ExternalConnectionModal {
|
||||
ext_conn_id: Option<i64>,
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl ExternalConnectionModal {
|
||||
/// Network [`Modal`] identifier.
|
||||
pub const NETWORK_ID: &'static str = "net_ext_conn_modal";
|
||||
@@ -50,7 +46,6 @@ impl ExternalConnectionModal {
|
||||
("".to_string(), "".to_string(), None)
|
||||
};
|
||||
Self {
|
||||
first_modal_launch: true,
|
||||
ext_node_url_edit,
|
||||
ext_node_secret_edit,
|
||||
ext_node_url_error: false,
|
||||
@@ -64,23 +59,54 @@ impl ExternalConnectionModal {
|
||||
cb: &dyn PlatformCallbacks,
|
||||
modal: &Modal,
|
||||
on_save: impl Fn(ExternalConnection)) {
|
||||
ui.add_space(6.0);
|
||||
// Add connection button callback.
|
||||
let on_add = |ui: &mut egui::Ui, m: &mut ExternalConnectionModal| {
|
||||
let url = if !m.ext_node_url_edit.starts_with("http") {
|
||||
format!("http://{}", m.ext_node_url_edit)
|
||||
} else {
|
||||
m.ext_node_url_edit.clone()
|
||||
};
|
||||
let error = Url::parse(url.trim()).is_err();
|
||||
m.ext_node_url_error = error;
|
||||
if !error {
|
||||
let secret = if m.ext_node_secret_edit.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(m.ext_node_secret_edit.clone())
|
||||
};
|
||||
|
||||
// Update or create new connection.
|
||||
let mut ext_conn = ExternalConnection::new(url, secret);
|
||||
if let Some(id) = m.ext_conn_id {
|
||||
ext_conn.id = id;
|
||||
}
|
||||
ConnectionsConfig::add_ext_conn(ext_conn.clone());
|
||||
ExternalConnection::check(Some(ext_conn.id), ui.ctx());
|
||||
on_save(ext_conn);
|
||||
|
||||
// Close modal.
|
||||
m.ext_node_url_edit = "".to_string();
|
||||
m.ext_node_secret_edit = "".to_string();
|
||||
m.ext_node_url_error = false;
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(6.0);
|
||||
ui.label(RichText::new(t!("wallets.node_url"))
|
||||
.size(17.0)
|
||||
.color(Colors::gray()));
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw node URL text edit.
|
||||
let url_edit_id = Id::from(modal.id).with(self.ext_conn_id);
|
||||
let mut url_edit_opts = TextEditOptions::new(url_edit_id).paste().no_focus();
|
||||
if self.first_modal_launch {
|
||||
self.first_modal_launch = false;
|
||||
url_edit_opts.focus = true;
|
||||
}
|
||||
View::text_edit(ui, cb, &mut self.ext_node_url_edit, &mut url_edit_opts);
|
||||
ui.add_space(8.0);
|
||||
let url_edit_id = Id::from(modal.id).with(self.ext_conn_id).with("node_url");
|
||||
let mut url_edit = TextEdit::new(url_edit_id)
|
||||
.paste()
|
||||
.focus(Modal::first_draw());
|
||||
url_edit.ui(ui, &mut self.ext_node_url_edit, cb);
|
||||
|
||||
ui.add_space(8.0);
|
||||
ui.label(RichText::new(t!("wallets.node_secret"))
|
||||
.size(17.0)
|
||||
.color(Colors::gray()));
|
||||
@@ -88,8 +114,17 @@ impl ExternalConnectionModal {
|
||||
|
||||
// Draw node API secret text edit.
|
||||
let secret_edit_id = Id::from(modal.id).with(self.ext_conn_id).with("node_secret");
|
||||
let mut secret_edit_opts = TextEditOptions::new(secret_edit_id).paste().no_focus();
|
||||
View::text_edit(ui, cb, &mut self.ext_node_secret_edit, &mut secret_edit_opts);
|
||||
let mut secret_edit = TextEdit::new(secret_edit_id)
|
||||
.password()
|
||||
.paste()
|
||||
.focus(false);
|
||||
if url_edit.enter_pressed {
|
||||
secret_edit.focus_request();
|
||||
}
|
||||
secret_edit.ui(ui, &mut self.ext_node_secret_edit, cb);
|
||||
if secret_edit.enter_pressed {
|
||||
(on_add)(ui, self);
|
||||
}
|
||||
|
||||
// Show error when specified URL is not valid.
|
||||
if self.ext_node_url_error {
|
||||
@@ -117,52 +152,13 @@ impl ExternalConnectionModal {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Add connection button callback.
|
||||
let mut on_add = |ui: &mut egui::Ui| {
|
||||
if !self.ext_node_url_edit.starts_with("http") {
|
||||
self.ext_node_url_edit = format!("http://{}", self.ext_node_url_edit)
|
||||
}
|
||||
let error = Url::parse(self.ext_node_url_edit.as_str()).is_err();
|
||||
self.ext_node_url_error = error;
|
||||
if !error {
|
||||
let url = self.ext_node_url_edit.to_owned();
|
||||
let secret = if self.ext_node_secret_edit.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.ext_node_secret_edit.to_owned())
|
||||
};
|
||||
|
||||
// Update or create new connection.
|
||||
let mut ext_conn = ExternalConnection::new(url, secret);
|
||||
if let Some(id) = self.ext_conn_id {
|
||||
ext_conn.id = id;
|
||||
}
|
||||
ConnectionsConfig::add_ext_conn(ext_conn.clone());
|
||||
ExternalConnection::check(Some(ext_conn.id), ui.ctx());
|
||||
on_save(ext_conn);
|
||||
|
||||
// Close modal.
|
||||
self.ext_node_url_edit = "".to_string();
|
||||
self.ext_node_secret_edit = "".to_string();
|
||||
self.ext_node_url_error = false;
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Handle Enter key press.
|
||||
let mut enter = false;
|
||||
View::on_enter_key(ui, || {
|
||||
enter = true;
|
||||
});
|
||||
if enter {
|
||||
(on_add)(ui);
|
||||
}
|
||||
|
||||
View::button_ui(ui, if self.ext_conn_id.is_some() {
|
||||
t!("modal.save")
|
||||
} else {
|
||||
t!("modal.add")
|
||||
}, Colors::white_or_black(false), on_add);
|
||||
}, Colors::white_or_black(false), |ui| {
|
||||
(on_add)(ui, self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
|
||||
use egui::{Id, RichText};
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CLOCK_COUNTDOWN, GRAPH, TIMER, WATCH};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::network::settings::NetworkSettings;
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions};
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition};
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::views::network::NetworkSettings;
|
||||
use crate::node::NodeConfig;
|
||||
|
||||
/// Dandelion server setup section content.
|
||||
@@ -155,6 +155,14 @@ impl DandelionSetup {
|
||||
|
||||
/// Draw epoch duration [`Modal`] content.
|
||||
fn epoch_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
// Save button callback.
|
||||
let on_save = |c: &mut DandelionSetup| {
|
||||
if let Ok(epoch) = c.epoch_edit.parse::<u16>() {
|
||||
NodeConfig::save_dandelion_epoch(epoch);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.epoch_duration"))
|
||||
@@ -163,8 +171,11 @@ impl DandelionSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw epoch text edit.
|
||||
let mut epoch_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.epoch_edit, &mut epoch_edit_opts);
|
||||
let mut epoch_edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
epoch_edit.ui(ui, &mut self.epoch_edit, cb);
|
||||
if epoch_edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.epoch_edit.parse::<u16>().is_err() {
|
||||
@@ -183,19 +194,6 @@ impl DandelionSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(epoch) = self.epoch_edit.parse::<u16>() {
|
||||
NodeConfig::save_dandelion_epoch(epoch);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -204,7 +202,9 @@ impl DandelionSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -233,6 +233,14 @@ impl DandelionSetup {
|
||||
|
||||
/// Draw epoch duration [`Modal`] content.
|
||||
fn embargo_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
// Save button callback.
|
||||
let on_save = |c: &mut DandelionSetup| {
|
||||
if let Ok(embargo) = c.embargo_edit.parse::<u16>() {
|
||||
NodeConfig::save_dandelion_embargo(embargo);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.embargo_timer"))
|
||||
@@ -241,8 +249,11 @@ impl DandelionSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw embargo text edit.
|
||||
let mut embargo_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.embargo_edit, &mut embargo_edit_opts);
|
||||
let mut embargo_edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
embargo_edit.ui(ui, &mut self.embargo_edit, cb);
|
||||
if embargo_edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.embargo_edit.parse::<u16>().is_err() {
|
||||
@@ -261,19 +272,6 @@ impl DandelionSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(embargo) = self.embargo_edit.parse::<u16>() {
|
||||
NodeConfig::save_dandelion_embargo(embargo);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -282,7 +280,9 @@ impl DandelionSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -312,6 +312,14 @@ impl DandelionSetup {
|
||||
|
||||
/// Draw aggregation period [`Modal`] content.
|
||||
fn aggregation_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
// Save button callback.
|
||||
let on_save = |c: &mut DandelionSetup| {
|
||||
if let Ok(embargo) = c.aggregation_edit.parse::<u16>() {
|
||||
NodeConfig::save_dandelion_aggregation(embargo);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.aggregation_period"))
|
||||
@@ -320,8 +328,11 @@ impl DandelionSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw aggregation period text edit.
|
||||
let mut aggregation_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.aggregation_edit, &mut aggregation_edit_opts);
|
||||
let mut aggregation_edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
aggregation_edit.ui(ui, &mut self.aggregation_edit, cb);
|
||||
if aggregation_edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.aggregation_edit.parse::<u16>().is_err() {
|
||||
@@ -340,19 +351,6 @@ impl DandelionSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(embargo) = self.aggregation_edit.parse::<u16>() {
|
||||
NodeConfig::save_dandelion_aggregation(embargo);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -361,7 +359,9 @@ impl DandelionSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -391,6 +391,14 @@ impl DandelionSetup {
|
||||
|
||||
/// Draw stem phase probability [`Modal`] content.
|
||||
fn stem_prob_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
// Save button callback.
|
||||
let on_save = |c: &mut DandelionSetup| {
|
||||
if let Ok(prob) = c.stem_prob_edit.parse::<u8>() {
|
||||
NodeConfig::save_stem_probability(prob);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.stem_probability"))
|
||||
@@ -399,8 +407,11 @@ impl DandelionSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw stem phase probability text edit.
|
||||
let mut stem_prob_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.stem_prob_edit, &mut stem_prob_edit_opts);
|
||||
let mut stem_prob_edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
stem_prob_edit.ui(ui, &mut self.stem_prob_edit, cb);
|
||||
if stem_prob_edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.stem_prob_edit.parse::<u8>().is_err() {
|
||||
@@ -419,19 +430,6 @@ impl DandelionSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(prob) = self.stem_prob_edit.parse::<u8>() {
|
||||
NodeConfig::save_stem_probability(prob);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -440,7 +438,9 @@ impl DandelionSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
use egui::{Id, RichText};
|
||||
use grin_core::global::ChainTypes;
|
||||
|
||||
use crate::AppConfig;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CLOCK_CLOCKWISE, COMPUTER_TOWER, PLUG, POWER, SHIELD, SHIELD_SLASH};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::network::NetworkContent;
|
||||
use crate::gui::views::network::settings::NetworkSettings;
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions};
|
||||
use crate::gui::views::network::NetworkContent;
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition};
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::node::{Node, NodeConfig};
|
||||
use crate::AppConfig;
|
||||
|
||||
/// Integrated node general setup section content.
|
||||
pub struct NodeSetup {
|
||||
@@ -280,6 +280,22 @@ impl NodeSetup {
|
||||
|
||||
/// Draw API port [`Modal`] content.
|
||||
fn api_port_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut NodeSetup| {
|
||||
// Check if port is available.
|
||||
let (api_ip, _) = NodeConfig::get_api_ip_port();
|
||||
let available = NodeConfig::is_api_port_available(&api_ip, &c.api_port_edit);
|
||||
c.api_port_available_edit = available;
|
||||
if available {
|
||||
// Save port at config if it's available.
|
||||
NodeConfig::save_api_address(&api_ip, &c.api_port_edit);
|
||||
if Node::is_running() {
|
||||
Node::restart();
|
||||
}
|
||||
c.is_api_port_available = true;
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.api_port"))
|
||||
@@ -288,8 +304,11 @@ impl NodeSetup {
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Draw API port text edit.
|
||||
let mut api_port_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.api_port_edit, &mut api_port_edit_opts);
|
||||
let mut api_port_edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
api_port_edit.ui(ui, &mut self.api_port_edit, cb);
|
||||
if api_port_edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified port is unavailable or reminder to restart enabled node.
|
||||
if !self.api_port_available_edit {
|
||||
@@ -308,31 +327,6 @@ impl NodeSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let mut on_save = || {
|
||||
// Check if port is available.
|
||||
let (api_ip, _) = NodeConfig::get_api_ip_port();
|
||||
let available = NodeConfig::is_api_port_available(&api_ip, &self.api_port_edit);
|
||||
self.api_port_available_edit = available;
|
||||
|
||||
if available {
|
||||
// Save port at config if it's available.
|
||||
NodeConfig::save_api_address(&api_ip, &self.api_port_edit);
|
||||
|
||||
if Node::is_running() {
|
||||
Node::restart();
|
||||
}
|
||||
|
||||
self.is_api_port_available = true;
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -340,7 +334,9 @@ impl NodeSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -383,6 +379,19 @@ impl NodeSetup {
|
||||
|
||||
/// Draw API secret token [`Modal`] content.
|
||||
fn secret_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut NodeSetup| {
|
||||
let secret = c.secret_edit.clone();
|
||||
match modal.id {
|
||||
API_SECRET_MODAL => {
|
||||
NodeConfig::save_api_secret(&secret);
|
||||
}
|
||||
_ => {
|
||||
NodeConfig::save_foreign_api_secret(&secret);
|
||||
}
|
||||
};
|
||||
modal.close();
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
let description = match modal.id {
|
||||
@@ -393,8 +402,14 @@ impl NodeSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw API secret token value text edit.
|
||||
let mut secret_edit_opts = TextEditOptions::new(Id::from(modal.id)).copy().paste();
|
||||
View::text_edit(ui, cb, &mut self.secret_edit, &mut secret_edit_opts);
|
||||
let mut secret_edit = TextEdit::new(Id::from(modal.id))
|
||||
.copy()
|
||||
.paste();
|
||||
secret_edit.ui(ui, &mut self.secret_edit, cb);
|
||||
if secret_edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
ui.add_space(6.0);
|
||||
|
||||
// Show reminder to restart enabled node.
|
||||
@@ -413,25 +428,6 @@ impl NodeSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
let secret = self.secret_edit.clone();
|
||||
match modal.id {
|
||||
API_SECRET_MODAL => {
|
||||
NodeConfig::save_api_secret(&secret);
|
||||
}
|
||||
_ => {
|
||||
NodeConfig::save_foreign_api_secret(&secret);
|
||||
}
|
||||
};
|
||||
modal.close();
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -439,7 +435,9 @@ impl NodeSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -475,6 +473,14 @@ impl NodeSetup {
|
||||
|
||||
/// Draw FTL [`Modal`] content.
|
||||
fn ftl_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
// Save button callback.
|
||||
let on_save = |c: &mut NodeSetup| {
|
||||
if let Ok(ftl) = c.ftl_edit.parse::<u64>() {
|
||||
NodeConfig::save_ftl(ftl);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.ftl"))
|
||||
@@ -483,8 +489,11 @@ impl NodeSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw ftl value text edit.
|
||||
let mut ftl_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.ftl_edit, &mut ftl_edit_opts);
|
||||
let mut ftl_edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
ftl_edit.ui(ui, &mut self.ftl_edit, cb);
|
||||
if ftl_edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.ftl_edit.parse::<u64>().is_err() {
|
||||
@@ -503,19 +512,6 @@ impl NodeSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(ftl) = self.ftl_edit.parse::<u64>() {
|
||||
NodeConfig::save_ftl(ftl);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -524,7 +520,9 @@ impl NodeSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
+100
-107
@@ -19,9 +19,9 @@ use crate::AppConfig;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{ARROW_FAT_LINES_DOWN, ARROW_FAT_LINES_UP, GLOBE_SIMPLE, HANDSHAKE, PLUG, PLUS_CIRCLE, PROHIBIT_INSET, TRASH};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::gui::views::network::settings::NetworkSettings;
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions};
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition};
|
||||
use crate::node::{Node, NodeConfig, PeersConfig};
|
||||
|
||||
/// Type of peer.
|
||||
@@ -272,6 +272,22 @@ impl P2PSetup {
|
||||
|
||||
/// Draw p2p port [`Modal`] content.
|
||||
fn port_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut P2PSetup| {
|
||||
// Check if port is available.
|
||||
let available = NodeConfig::is_p2p_port_available(&c.port_edit);
|
||||
c.port_available_edit = available;
|
||||
|
||||
// Save port at config if it's available.
|
||||
if available {
|
||||
NodeConfig::save_p2p_port(c.port_edit.parse::<u16>().unwrap());
|
||||
if Node::is_running() {
|
||||
Node::restart();
|
||||
}
|
||||
c.is_port_available = true;
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.p2p_port"))
|
||||
@@ -280,8 +296,11 @@ impl P2PSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw p2p port text edit.
|
||||
let mut text_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.port_edit, &mut text_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
edit.ui(ui, &mut self.port_edit, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified port is unavailable.
|
||||
if !self.port_available_edit {
|
||||
@@ -298,28 +317,6 @@ impl P2PSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let mut on_save = || {
|
||||
// Check if port is available.
|
||||
let available = NodeConfig::is_p2p_port_available(&self.port_edit);
|
||||
self.port_available_edit = available;
|
||||
|
||||
// Save port at config if it's available.
|
||||
if available {
|
||||
NodeConfig::save_p2p_port(self.port_edit.parse::<u16>().unwrap());
|
||||
if Node::is_running() {
|
||||
Node::restart();
|
||||
}
|
||||
self.is_port_available = true;
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -328,7 +325,9 @@ impl P2PSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -413,6 +412,27 @@ impl P2PSetup {
|
||||
|
||||
/// Draw peer creation [`Modal`] content.
|
||||
fn peer_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut P2PSetup| {
|
||||
// Check if peer is correct and/or available.
|
||||
let peer = c.peer_edit.clone();
|
||||
let is_correct_address = PeersConfig::peer_to_addr(peer.clone()).is_some();
|
||||
c.is_correct_address_edit = is_correct_address;
|
||||
|
||||
// Save peer at config.
|
||||
if is_correct_address {
|
||||
match modal.id {
|
||||
CUSTOM_SEED_MODAL => NodeConfig::save_custom_seed(peer),
|
||||
ALLOW_PEER_MODAL => NodeConfig::allow_peer(peer),
|
||||
DENY_PEER_MODAL => NodeConfig::deny_peer(peer),
|
||||
PREFER_PEER_MODAL => NodeConfig::prefer_peer(peer),
|
||||
&_ => {}
|
||||
}
|
||||
|
||||
c.is_port_available = true;
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
let label_text = match modal.id {
|
||||
@@ -423,8 +443,11 @@ impl P2PSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw peer address text edit.
|
||||
let mut peer_text_edit_opts = TextEditOptions::new(Id::from(modal.id)).paste();
|
||||
View::text_edit(ui, cb, &mut self.peer_edit, &mut peer_text_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).paste();
|
||||
edit.ui(ui, &mut self.peer_edit, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified address is incorrect.
|
||||
if !self.is_correct_address_edit {
|
||||
@@ -440,33 +463,6 @@ impl P2PSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let mut on_save = || {
|
||||
// Check if peer is correct and/or available.
|
||||
let peer = self.peer_edit.clone();
|
||||
let is_correct_address = PeersConfig::peer_to_addr(peer.clone()).is_some();
|
||||
self.is_correct_address_edit = is_correct_address;
|
||||
|
||||
// Save peer at config.
|
||||
if is_correct_address {
|
||||
match modal.id {
|
||||
CUSTOM_SEED_MODAL => NodeConfig::save_custom_seed(peer),
|
||||
ALLOW_PEER_MODAL => NodeConfig::allow_peer(peer),
|
||||
DENY_PEER_MODAL => NodeConfig::deny_peer(peer),
|
||||
PREFER_PEER_MODAL => NodeConfig::prefer_peer(peer),
|
||||
&_ => {}
|
||||
}
|
||||
|
||||
self.is_port_available = true;
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -475,7 +471,9 @@ impl P2PSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -533,6 +531,13 @@ impl P2PSetup {
|
||||
|
||||
/// Draw ban window [`Modal`] content.
|
||||
fn ban_window_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut P2PSetup| {
|
||||
if let Ok(ban_window) = c.ban_window_edit.parse::<i64>() {
|
||||
NodeConfig::save_p2p_ban_window(ban_window);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.ban_window"))
|
||||
@@ -541,8 +546,11 @@ impl P2PSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw ban window text edit.
|
||||
let mut text_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.ban_window_edit, &mut text_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
edit.ui(ui, &mut self.ban_window_edit, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.ban_window_edit.parse::<i64>().is_err() {
|
||||
@@ -561,28 +569,16 @@ impl P2PSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(ban_window) = self.ban_window_edit.parse::<i64>() {
|
||||
NodeConfig::save_p2p_ban_window(ban_window);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
// Close modal.
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -614,6 +610,13 @@ impl P2PSetup {
|
||||
|
||||
/// Draw maximum number of inbound peers [`Modal`] content.
|
||||
fn max_inbound_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut P2PSetup| {
|
||||
if let Ok(max_inbound) = c.max_inbound_count.parse::<u32>() {
|
||||
NodeConfig::save_max_inbound_peers(max_inbound);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.max_inbound_count"))
|
||||
@@ -622,8 +625,11 @@ impl P2PSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw maximum number of inbound peers text edit.
|
||||
let mut text_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.max_inbound_count, &mut text_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
edit.ui(ui, &mut self.max_inbound_count, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.max_inbound_count.parse::<u32>().is_err() {
|
||||
@@ -642,28 +648,16 @@ impl P2PSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(max_inbound) = self.max_inbound_count.parse::<u32>() {
|
||||
NodeConfig::save_max_inbound_peers(max_inbound);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
// Close modal.
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -694,6 +688,13 @@ impl P2PSetup {
|
||||
|
||||
/// Draw maximum number of outbound peers [`Modal`] content.
|
||||
fn max_outbound_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut P2PSetup| {
|
||||
if let Ok(max_outbound) = c.max_outbound_count.parse::<u32>() {
|
||||
NodeConfig::save_max_outbound_peers(max_outbound);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.max_outbound_count"))
|
||||
@@ -702,8 +703,11 @@ impl P2PSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw maximum number of outbound peers text edit.
|
||||
let mut text_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.max_outbound_count, &mut text_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
edit.ui(ui, &mut self.max_outbound_count, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.max_outbound_count.parse::<u32>().is_err() {
|
||||
@@ -722,19 +726,6 @@ impl P2PSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(max_outbound) = self.max_outbound_count.parse::<u32>() {
|
||||
NodeConfig::save_max_outbound_peers(max_outbound);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -743,7 +734,9 @@ impl P2PSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -17,9 +17,9 @@ use egui::{Id, RichText};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{BEZIER_CURVE, BOUNDING_BOX, CHART_SCATTER, CIRCLES_THREE, CLOCK_COUNTDOWN, HAND_COINS};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::gui::views::network::settings::NetworkSettings;
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions};
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition};
|
||||
use crate::node::NodeConfig;
|
||||
|
||||
/// Memory pool setup section content.
|
||||
@@ -159,6 +159,13 @@ impl PoolSetup {
|
||||
|
||||
/// Draw fee base [`Modal`] content.
|
||||
fn fee_base_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut PoolSetup| {
|
||||
if let Ok(fee) = c.fee_base_edit.parse::<u64>() {
|
||||
NodeConfig::save_base_fee(fee);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.pool_fee"))
|
||||
@@ -167,8 +174,9 @@ impl PoolSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw fee base text edit.
|
||||
let mut fee_base_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.fee_base_edit, &mut fee_base_edit_opts);
|
||||
TextEdit::new(Id::from(modal.id))
|
||||
.h_center()
|
||||
.ui(ui, &mut self.fee_base_edit, cb);
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.fee_base_edit.parse::<u64>().is_err() {
|
||||
@@ -186,19 +194,6 @@ impl PoolSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(fee) = self.fee_base_edit.parse::<u64>() {
|
||||
NodeConfig::save_base_fee(fee);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -207,7 +202,9 @@ impl PoolSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -239,6 +236,13 @@ impl PoolSetup {
|
||||
|
||||
/// Draw reorg cache retention period [`Modal`] content.
|
||||
fn reorg_period_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut PoolSetup| {
|
||||
if let Ok(period) = c.reorg_period_edit.parse::<u32>() {
|
||||
NodeConfig::save_reorg_cache_period(period);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.reorg_period"))
|
||||
@@ -247,8 +251,11 @@ impl PoolSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw reorg period text edit.
|
||||
let mut reorg_period_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.reorg_period_edit, &mut reorg_period_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
edit.ui(ui, &mut self.reorg_period_edit, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.reorg_period_edit.parse::<u32>().is_err() {
|
||||
@@ -266,19 +273,6 @@ impl PoolSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(period) = self.reorg_period_edit.parse::<u32>() {
|
||||
NodeConfig::save_reorg_cache_period(period);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -287,7 +281,9 @@ impl PoolSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -318,6 +314,13 @@ impl PoolSetup {
|
||||
|
||||
/// Draw maximum number of transactions in the pool [`Modal`] content.
|
||||
fn pool_size_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut PoolSetup| {
|
||||
if let Ok(size) = c.pool_size_edit.parse::<usize>() {
|
||||
NodeConfig::save_max_pool_size(size);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.max_tx_pool"))
|
||||
@@ -326,8 +329,12 @@ impl PoolSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw pool size text edit.
|
||||
let mut pool_size_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.pool_size_edit, &mut pool_size_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id))
|
||||
.h_center();
|
||||
edit.ui(ui, &mut self.pool_size_edit, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.pool_size_edit.parse::<usize>().is_err() {
|
||||
@@ -345,19 +352,6 @@ impl PoolSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(size) = self.pool_size_edit.parse::<usize>() {
|
||||
NodeConfig::save_max_pool_size(size);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -366,7 +360,9 @@ impl PoolSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -399,6 +395,13 @@ impl PoolSetup {
|
||||
|
||||
/// Draw maximum number of transactions in the stempool [`Modal`] content.
|
||||
fn stem_size_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut PoolSetup| {
|
||||
if let Ok(size) = c.stempool_size_edit.parse::<usize>() {
|
||||
NodeConfig::save_max_stempool_size(size);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.max_tx_stempool"))
|
||||
@@ -407,8 +410,11 @@ impl PoolSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw stempool size text edit.
|
||||
let mut stem_pool_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.stempool_size_edit, &mut stem_pool_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
edit.ui(ui, &mut self.stempool_size_edit, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.stempool_size_edit.parse::<usize>().is_err() {
|
||||
@@ -426,19 +432,6 @@ impl PoolSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(size) = self.stempool_size_edit.parse::<usize>() {
|
||||
NodeConfig::save_max_stempool_size(size);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -447,7 +440,9 @@ impl PoolSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -480,6 +475,13 @@ impl PoolSetup {
|
||||
|
||||
/// Draw maximum total weight of transactions [`Modal`] content.
|
||||
fn max_weight_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut PoolSetup| {
|
||||
if let Ok(weight) = c.max_weight_edit.parse::<u64>() {
|
||||
NodeConfig::save_mineable_max_weight(weight);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.max_tx_weight"))
|
||||
@@ -488,8 +490,11 @@ impl PoolSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw tx weight text edit.
|
||||
let mut mac_weight_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.max_weight_edit, &mut mac_weight_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
edit.ui(ui, &mut self.max_weight_edit, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.max_weight_edit.parse::<u64>().is_err() {
|
||||
@@ -507,19 +512,6 @@ impl PoolSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(weight) = self.max_weight_edit.parse::<u64>() {
|
||||
NodeConfig::save_mineable_max_weight(weight);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -528,7 +520,9 @@ impl PoolSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -18,9 +18,9 @@ use grin_chain::SyncStatus;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{BARBELL, HARD_DRIVES, PLUG, POWER, TIMER};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::gui::views::network::settings::NetworkSettings;
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions};
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition};
|
||||
use crate::gui::views::wallets::modals::WalletsModal;
|
||||
use crate::node::{Node, NodeConfig};
|
||||
use crate::wallet::{WalletConfig, WalletList};
|
||||
@@ -286,6 +286,24 @@ impl StratumSetup {
|
||||
|
||||
/// Draw stratum port [`Modal`] content.
|
||||
fn port_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut StratumSetup| {
|
||||
// Check if port is available.
|
||||
let (stratum_ip, _) = NodeConfig::get_stratum_address();
|
||||
let available = NodeConfig::is_stratum_port_available(
|
||||
&stratum_ip,
|
||||
&c.stratum_port_edit
|
||||
);
|
||||
c.stratum_port_available_edit = available;
|
||||
|
||||
// Save port at config if it's available.
|
||||
if available {
|
||||
NodeConfig::save_stratum_address(&stratum_ip, &c.stratum_port_edit);
|
||||
|
||||
c.is_port_available = true;
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.stratum_port"))
|
||||
@@ -294,8 +312,11 @@ impl StratumSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw stratum port text edit.
|
||||
let mut text_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.stratum_port_edit, &mut text_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
edit.ui(ui, &mut self.stratum_port_edit, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified port is unavailable.
|
||||
if !self.stratum_port_available_edit {
|
||||
@@ -314,30 +335,6 @@ impl StratumSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let mut on_save = || {
|
||||
// Check if port is available.
|
||||
let (stratum_ip, _) = NodeConfig::get_stratum_address();
|
||||
let available = NodeConfig::is_stratum_port_available(
|
||||
&stratum_ip,
|
||||
&self.stratum_port_edit
|
||||
);
|
||||
self.stratum_port_available_edit = available;
|
||||
|
||||
// Save port at config if it's available.
|
||||
if available {
|
||||
NodeConfig::save_stratum_address(&stratum_ip, &self.stratum_port_edit);
|
||||
|
||||
self.is_port_available = true;
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -346,7 +343,9 @@ impl StratumSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -383,6 +382,13 @@ impl StratumSetup {
|
||||
|
||||
/// Draw attempt time [`Modal`] content.
|
||||
fn attempt_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut StratumSetup| {
|
||||
if let Ok(time) = c.attempt_time_edit.parse::<u32>() {
|
||||
NodeConfig::save_stratum_attempt_time(time);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.attempt_time"))
|
||||
@@ -391,8 +397,11 @@ impl StratumSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw attempt time text edit.
|
||||
let mut text_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.attempt_time_edit, &mut text_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
edit.ui(ui, &mut self.attempt_time_edit, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.attempt_time_edit.parse::<u32>().is_err() {
|
||||
@@ -411,19 +420,6 @@ impl StratumSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(time) = self.attempt_time_edit.parse::<u32>() {
|
||||
NodeConfig::save_stratum_attempt_time(time);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -432,7 +428,9 @@ impl StratumSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -463,6 +461,13 @@ impl StratumSetup {
|
||||
|
||||
/// Draw minimum acceptable share difficulty [`Modal`] content.
|
||||
fn min_diff_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut StratumSetup| {
|
||||
if let Ok(diff) = c.min_share_diff_edit.parse::<u64>() {
|
||||
NodeConfig::save_stratum_min_share_diff(diff);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("network_settings.min_share_diff"))
|
||||
@@ -471,8 +476,11 @@ impl StratumSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw share difficulty text edit.
|
||||
let mut text_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.min_share_diff_edit, &mut text_edit_opts);
|
||||
let mut edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
edit.ui(ui, &mut self.min_share_diff_edit, cb);
|
||||
if edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.min_share_diff_edit.parse::<u64>().is_err() {
|
||||
@@ -491,19 +499,6 @@ impl StratumSetup {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(diff) = self.min_share_diff_edit.parse::<u64>() {
|
||||
NodeConfig::save_stratum_min_share_diff(diff);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
on_save();
|
||||
});
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || {
|
||||
@@ -512,7 +507,9 @@ impl StratumSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -76,81 +76,6 @@ pub trait ModalContainer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for [`egui::TextEdit`] view.
|
||||
pub struct TextEditOptions {
|
||||
/// View identifier.
|
||||
pub id: egui::Id,
|
||||
/// Check if horizontal centering is needed.
|
||||
pub h_center: bool,
|
||||
/// Check if initial focus on field is needed.
|
||||
pub focus: bool,
|
||||
/// Hide letters and draw button to show/hide letters.
|
||||
pub password: bool,
|
||||
/// Show copy button.
|
||||
pub copy: bool,
|
||||
/// Show paste button.
|
||||
pub paste: bool,
|
||||
/// Show button to scan QR code into text.
|
||||
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 {
|
||||
pub fn new(id: egui::Id) -> Self {
|
||||
Self {
|
||||
id,
|
||||
h_center: false,
|
||||
focus: true,
|
||||
password: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
scan_qr: false,
|
||||
scan_pressed: false,
|
||||
enter_pressed: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Center text horizontally.
|
||||
pub fn h_center(mut self) -> Self {
|
||||
self.h_center = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Disable initial focus.
|
||||
pub fn no_focus(mut self) -> Self {
|
||||
self.focus = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Hide letters and draw button to show/hide letters.
|
||||
pub fn password(mut self) -> Self {
|
||||
self.password = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show button to copy text.
|
||||
pub fn copy(mut self) -> Self {
|
||||
self.copy = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show button to paste text.
|
||||
pub fn paste(mut self) -> Self {
|
||||
self.paste = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show button to scan QR code to text.
|
||||
pub fn scan_qr(mut self) -> Self {
|
||||
self.scan_qr = true;
|
||||
self.scan_pressed = false;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// QR code scan result.
|
||||
#[derive(Clone)]
|
||||
pub enum QrScanResult {
|
||||
|
||||
+7
-173
@@ -12,24 +12,21 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
|
||||
use egui::{Align, Button, CursorIcon, Layout, lerp, PointerState, Rect, Response, Rgba, RichText, Sense, SizeHint, Spinner, TextBuffer, TextStyle, TextureHandle, TextureOptions, Widget, UiBuilder};
|
||||
use egui::epaint::{Color32, FontId, PathShape, PathStroke, RectShape, Rounding, Stroke};
|
||||
use egui::epaint::text::TextWrapping;
|
||||
use egui::epaint::{Color32, FontId, PathShape, PathStroke, RectShape, Rounding, Stroke};
|
||||
use egui::load::SizedTexture;
|
||||
use egui::os::OperatingSystem;
|
||||
use egui::text::{LayoutJob, TextFormat};
|
||||
use egui::text_edit::TextEditState;
|
||||
use egui::{lerp, Button, CursorIcon, PointerState, Rect, Response, Rgba, RichText, Sense, SizeHint, Spinner, TextureHandle, TextureOptions, UiBuilder, Widget};
|
||||
use egui_extras::image::load_svg_bytes_with_size;
|
||||
|
||||
use crate::AppConfig;
|
||||
use crate::gui::icons::{CHECK_SQUARE, SQUARE};
|
||||
use crate::gui::views::types::LinePosition;
|
||||
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};
|
||||
use crate::AppConfig;
|
||||
|
||||
pub struct View;
|
||||
|
||||
@@ -90,13 +87,6 @@ impl View {
|
||||
(rect.width(), rect.height())
|
||||
}
|
||||
|
||||
/// Callback on Enter key press event.
|
||||
pub fn on_enter_key(ui: &mut egui::Ui, cb: impl FnOnce()) {
|
||||
if ui.ctx().input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
(cb)();
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate margin for far left view based on display insets (cutouts).
|
||||
pub fn far_left_inset_margin(ui: &mut egui::Ui) -> f32 {
|
||||
if ui.available_rect_before_wrap().min.x == 0.0 {
|
||||
@@ -159,7 +149,7 @@ impl View {
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw horizontally centered sub-title with space below.
|
||||
/// Draw horizontally centered subtitle with space below.
|
||||
pub fn sub_title(ui: &mut egui::Ui, text: String) {
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
ui.label(RichText::new(text.to_uppercase()).size(16.0).color(Colors::text(false)));
|
||||
@@ -369,162 +359,6 @@ impl View {
|
||||
});
|
||||
}
|
||||
|
||||
/// Default height of [`egui::TextEdit`] view.
|
||||
const TEXT_EDIT_HEIGHT: f32 = 37.0;
|
||||
|
||||
/// Draw [`egui::TextEdit`] widget.
|
||||
pub fn text_edit(ui: &mut egui::Ui,
|
||||
cb: &dyn PlatformCallbacks,
|
||||
value: &mut String,
|
||||
options: &mut TextEditOptions) {
|
||||
let mut layout_rect = ui.available_rect_before_wrap();
|
||||
layout_rect.set_height(Self::TEXT_EDIT_HEIGHT);
|
||||
ui.allocate_ui_with_layout(layout_rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
||||
// Setup password button.
|
||||
let mut show_pass = false;
|
||||
if options.password {
|
||||
// Set password button state value.
|
||||
let show_pass_id = egui::Id::new(options.id).with("_show_pass");
|
||||
show_pass = ui.data(|data| {
|
||||
data.get_temp(show_pass_id)
|
||||
}).unwrap_or(true);
|
||||
// Draw button to show/hide current password.
|
||||
let eye_icon = if show_pass { EYE } else { EYE_SLASH };
|
||||
let mut changed = false;
|
||||
View::button(ui, eye_icon.to_string(), Colors::white_or_black(false), || {
|
||||
show_pass = !show_pass;
|
||||
changed = true;
|
||||
});
|
||||
// Save state if changed.
|
||||
if changed {
|
||||
ui.data_mut(|data| {
|
||||
data.insert_temp(show_pass_id, show_pass);
|
||||
});
|
||||
}
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
// Setup copy button.
|
||||
if options.copy {
|
||||
let copy_icon = COPY.to_string();
|
||||
View::button(ui, copy_icon, Colors::white_or_black(false), || {
|
||||
cb.copy_string_to_buffer(value.clone());
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
// Setup paste button.
|
||||
if options.paste {
|
||||
let paste_icon = CLIPBOARD_TEXT.to_string();
|
||||
View::button(ui, paste_icon, Colors::white_or_black(false), || {
|
||||
*value = cb.get_string_from_buffer();
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
// Setup scan QR code button.
|
||||
if options.scan_qr {
|
||||
let scan_icon = SCAN.to_string();
|
||||
View::button(ui, scan_icon, Colors::white_or_black(false), || {
|
||||
cb.start_camera();
|
||||
options.scan_pressed = true;
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
let layout_size = ui.available_size();
|
||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||
// Setup text edit size.
|
||||
let mut edit_rect = ui.available_rect_before_wrap();
|
||||
edit_rect.set_height(Self::TEXT_EDIT_HEIGHT);
|
||||
|
||||
// Show text edit.
|
||||
let text_edit_resp = egui::TextEdit::singleline(value)
|
||||
.id(options.id)
|
||||
.margin(egui::Vec2::new(2.0, 0.0))
|
||||
.font(TextStyle::Heading)
|
||||
.min_size(edit_rect.size())
|
||||
.horizontal_align(if options.h_center { Align::Center } else { Align::Min })
|
||||
.vertical_align(Align::Center)
|
||||
.password(show_pass)
|
||||
.cursor_at_end(true)
|
||||
.ui(ui);
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
let focus = ui.data(|data| {
|
||||
data.get_temp(focus_id)
|
||||
}).unwrap_or(egui::Id::from(""));
|
||||
if focus == options.id {
|
||||
text_edit_resp.request_focus();
|
||||
KeyboardContent::show();
|
||||
}
|
||||
// Apply text from software input.
|
||||
if text_edit_resp.has_focus() {
|
||||
options.focus = true;
|
||||
options.enter_pressed = Self::on_soft_input(ui, options.id, false, value);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
return enter_pressed;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Calculate item background/button rounding based on item index.
|
||||
pub fn item_rounding(index: usize, len: usize, is_button: bool) -> Rounding {
|
||||
let corners = if is_button {
|
||||
|
||||
@@ -17,8 +17,8 @@ use egui::{Id, RichText};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::PENCIL;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, Content, View};
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions};
|
||||
use crate::gui::views::{Modal, Content, View, TextEdit};
|
||||
use crate::gui::views::types::{ModalContainer, ModalPosition};
|
||||
use crate::wallet::Mnemonic;
|
||||
use crate::wallet::types::{PhraseMode, PhraseSize, PhraseWord};
|
||||
|
||||
@@ -242,6 +242,26 @@ impl MnemonicSetup {
|
||||
|
||||
/// Draw word input [`Modal`] content.
|
||||
fn word_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut MnemonicSetup| {
|
||||
// Insert word checking validity.
|
||||
let word = &c.word_edit.trim().to_string();
|
||||
c.valid_word_edit = c.mnemonic.insert(c.word_index_edit, word);
|
||||
if !c.valid_word_edit {
|
||||
return;
|
||||
}
|
||||
// Close modal or go to next word to edit.
|
||||
let next_word = c.mnemonic.get(c.word_index_edit + 1);
|
||||
let close_modal = next_word.is_none() ||
|
||||
(!next_word.as_ref().unwrap().text.is_empty() &&
|
||||
next_word.unwrap().valid);
|
||||
if close_modal {
|
||||
modal.close();
|
||||
} else {
|
||||
c.word_index_edit += 1;
|
||||
c.word_edit = String::from("");
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("wallets.enter_word", "number" => self.word_index_edit + 1))
|
||||
@@ -250,10 +270,11 @@ impl MnemonicSetup {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw word value text edit.
|
||||
let mut text_edit_opts = TextEditOptions::new(
|
||||
Id::from(modal.id).with(self.word_index_edit)
|
||||
);
|
||||
View::text_edit(ui, cb, &mut self.word_edit, &mut text_edit_opts);
|
||||
let mut word_edit = TextEdit::new(Id::from(modal.id).with(self.word_index_edit));
|
||||
word_edit.ui(ui, &mut self.word_edit, cb);
|
||||
if word_edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified word is not valid.
|
||||
if !self.valid_word_edit {
|
||||
@@ -278,32 +299,10 @@ impl MnemonicSetup {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Callback to save the word.
|
||||
let mut save = || {
|
||||
// Insert word checking validity.
|
||||
let word = &self.word_edit.trim().to_string();
|
||||
self.valid_word_edit = self.mnemonic.insert(self.word_index_edit, word);
|
||||
if !self.valid_word_edit {
|
||||
return;
|
||||
}
|
||||
// Close modal or go to next word to edit.
|
||||
let next_word = self.mnemonic.get(self.word_index_edit + 1);
|
||||
let close_modal = next_word.is_none() ||
|
||||
(!next_word.as_ref().unwrap().text.is_empty() &&
|
||||
next_word.unwrap().valid);
|
||||
if close_modal {
|
||||
modal.close();
|
||||
} else {
|
||||
self.word_index_edit += 1;
|
||||
self.word_edit = String::from("");
|
||||
}
|
||||
};
|
||||
// Call save on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
(save)();
|
||||
});
|
||||
// Show save button.
|
||||
View::button(ui, t!("continue"), Colors::white_or_black(false), save);
|
||||
View::button(ui, t!("continue"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -17,13 +17,10 @@ use grin_util::ZeroingString;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::types::TextEditOptions;
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
|
||||
/// Initial wallet creation [`Modal`] content.
|
||||
pub struct AddWalletModal {
|
||||
/// Flag to check if it's first draw to focus on first field.
|
||||
first_draw: bool,
|
||||
/// Wallet name.
|
||||
pub name_edit: String,
|
||||
/// Password to encrypt created wallet.
|
||||
@@ -33,7 +30,6 @@ pub struct AddWalletModal {
|
||||
impl Default for AddWalletModal {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
first_draw: true,
|
||||
name_edit: t!("wallets.default_wallet"),
|
||||
pass_edit: "".to_string(),
|
||||
}
|
||||
@@ -47,26 +43,27 @@ impl AddWalletModal {
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks,
|
||||
mut on_input: impl FnMut(String, ZeroingString)) {
|
||||
ui.add_space(6.0);
|
||||
|
||||
let mut enter_pressed = false;
|
||||
let mut on_next = |m: &mut AddWalletModal| {
|
||||
let name = m.name_edit.clone();
|
||||
let pass = m.pass_edit.clone();
|
||||
if name.is_empty() || pass.is_empty() {
|
||||
return;
|
||||
}
|
||||
modal.close();
|
||||
on_input(name, ZeroingString::from(pass));
|
||||
};
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(6.0);
|
||||
ui.label(RichText::new(t!("wallets.name"))
|
||||
.size(17.0)
|
||||
.color(Colors::gray()));
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Show wallet name text edit.
|
||||
let mut name_edit_opts = TextEditOptions::new(Id::from(modal.id).with("name"))
|
||||
.no_focus();
|
||||
if self.first_draw {
|
||||
self.first_draw = false;
|
||||
name_edit_opts.focus = true;
|
||||
}
|
||||
View::text_edit(ui, cb, &mut self.name_edit, &mut name_edit_opts);
|
||||
View::on_enter_key(ui, || {
|
||||
name_edit_opts.enter_pressed = name_edit_opts.focus;
|
||||
});
|
||||
let mut name_input = TextEdit::new(Id::from(modal.id).with("name"))
|
||||
.focus(Modal::first_draw());
|
||||
|
||||
name_input.ui(ui, &mut self.name_edit, cb);
|
||||
|
||||
ui.add_space(8.0);
|
||||
ui.label(RichText::new(t!("wallets.pass"))
|
||||
@@ -74,16 +71,17 @@ impl AddWalletModal {
|
||||
.color(Colors::gray()));
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw wallet password text edit.
|
||||
let mut pass_text_edit_opts = TextEditOptions::new(Id::from(modal.id).with("pass"))
|
||||
// Show wallet password text edit.
|
||||
let mut pass_input = TextEdit::new(Id::from(modal.id).with("pass"))
|
||||
.password()
|
||||
.no_focus();
|
||||
if name_edit_opts.enter_pressed {
|
||||
pass_text_edit_opts.focus = true;
|
||||
.focus(false);
|
||||
if name_input.enter_pressed {
|
||||
pass_input.focus_request();
|
||||
}
|
||||
pass_input.ui(ui, &mut self.pass_edit, cb);
|
||||
if pass_input.enter_pressed {
|
||||
(on_next)(self);
|
||||
}
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -100,25 +98,9 @@ impl AddWalletModal {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
let mut on_next = || {
|
||||
let name = self.name_edit.clone();
|
||||
let pass = self.pass_edit.clone();
|
||||
if name.is_empty() || pass.is_empty() {
|
||||
return;
|
||||
}
|
||||
modal.close();
|
||||
on_input(name, ZeroingString::from(pass));
|
||||
};
|
||||
|
||||
// Go to next creation step on Enter button press.
|
||||
View::on_enter_key(ui, || {
|
||||
(on_next)();
|
||||
View::button(ui, t!("continue"), Colors::white_or_black(false), || {
|
||||
(on_next)(self);
|
||||
});
|
||||
if enter_pressed {
|
||||
(on_next)();
|
||||
}
|
||||
|
||||
View::button(ui, t!("continue"), Colors::white_or_black(false), on_next);
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -17,8 +17,7 @@ use grin_util::ZeroingString;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::types::TextEditOptions;
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Wallet opening [`Modal`] content.
|
||||
@@ -51,19 +50,35 @@ impl OpenWalletModal {
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks,
|
||||
mut on_continue: impl FnMut(Wallet, Option<String>)) {
|
||||
ui.add_space(6.0);
|
||||
|
||||
let mut enter_pressed = false;
|
||||
// Callback for button to continue.
|
||||
let mut on_continue = |m: &mut OpenWalletModal| {
|
||||
let pass = m.pass_edit.clone();
|
||||
if pass.is_empty() {
|
||||
return;
|
||||
}
|
||||
match m.wallet.open(ZeroingString::from(pass)) {
|
||||
Ok(_) => {
|
||||
m.pass_edit = "".to_string();
|
||||
modal.close();
|
||||
on_continue(m.wallet.clone(), m.data.clone());
|
||||
}
|
||||
Err(_) => m.wrong_pass = true
|
||||
}
|
||||
};
|
||||
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(6.0);
|
||||
ui.label(RichText::new(t!("wallets.pass"))
|
||||
.size(17.0)
|
||||
.color(Colors::gray()));
|
||||
ui.add_space(8.0);
|
||||
|
||||
// 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;
|
||||
let mut pass_edit = TextEdit::new(Id::from(modal.id).with("pass_edit")).password();
|
||||
pass_edit.ui(ui, &mut self.pass_edit, cb);
|
||||
if pass_edit.enter_pressed {
|
||||
(on_continue)(self);
|
||||
}
|
||||
|
||||
// Show information when password is empty.
|
||||
if self.pass_edit.is_empty() {
|
||||
@@ -94,31 +109,9 @@ impl OpenWalletModal {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Callback for button to continue.
|
||||
let mut on_continue = || {
|
||||
let pass = self.pass_edit.clone();
|
||||
if pass.is_empty() {
|
||||
return;
|
||||
}
|
||||
match self.wallet.open(ZeroingString::from(pass)) {
|
||||
Ok(_) => {
|
||||
self.pass_edit = "".to_string();
|
||||
modal.close();
|
||||
on_continue(self.wallet.clone(), self.data.clone());
|
||||
}
|
||||
Err(_) => self.wrong_pass = true
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
(on_continue)();
|
||||
View::button(ui, t!("continue"), Colors::white_or_black(false), || {
|
||||
(on_continue)(self);
|
||||
});
|
||||
if enter_pressed {
|
||||
(on_continue)();
|
||||
}
|
||||
|
||||
View::button(ui, t!("continue"), Colors::white_or_black(false), on_continue);
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -21,8 +21,7 @@ use grin_wallet_libwallet::Error;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::types::TextEditOptions;
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::gui::views::wallets::wallet::WalletTransactionModal;
|
||||
use crate::wallet::types::WalletTransaction;
|
||||
use crate::wallet::Wallet;
|
||||
@@ -163,10 +162,10 @@ impl MessageRequestModal {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw request amount text input.
|
||||
let amount_edit_id = Id::from(modal.id).with(wallet.get_config().id);
|
||||
let mut amount_edit_opts = TextEditOptions::new(amount_edit_id).h_center();
|
||||
let mut amount_edit = TextEdit::new(Id::from(modal.id).with(wallet.get_config().id))
|
||||
.h_center();
|
||||
let amount_edit_before = self.amount_edit.clone();
|
||||
View::text_edit(ui, cb, &mut self.amount_edit, &mut amount_edit_opts);
|
||||
amount_edit.ui(ui, &mut self.amount_edit, cb);
|
||||
|
||||
// Check value if input was changed.
|
||||
if amount_edit_before != self.amount_edit {
|
||||
|
||||
@@ -19,8 +19,7 @@ use grin_core::core::amount_to_hr_string;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CHECK, CHECK_FAT, FOLDER_USER, PATH};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::types::TextEditOptions;
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::gui::views::wallets::wallet::types::GRIN;
|
||||
use crate::wallet::types::WalletAccount;
|
||||
use crate::wallet::{Wallet, WalletConfig};
|
||||
@@ -66,6 +65,20 @@ impl WalletAccountsModal {
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
if self.account_creating {
|
||||
let on_create = |m: &mut WalletAccountsModal| {
|
||||
if m.account_label_edit.is_empty() {
|
||||
return;
|
||||
}
|
||||
let label = &m.account_label_edit;
|
||||
match wallet.create_account(label) {
|
||||
Ok(_) => {
|
||||
let _ = wallet.set_active_account(label);
|
||||
modal.close();
|
||||
},
|
||||
Err(_) => m.account_creation_error = true
|
||||
};
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("wallets.new_account_desc"))
|
||||
@@ -74,11 +87,13 @@ impl WalletAccountsModal {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw account name edit.
|
||||
let text_edit_id = Id::from(modal.id).with(wallet.get_config().id);
|
||||
let mut text_edit_opts = TextEditOptions::new(text_edit_id);
|
||||
View::text_edit(ui, cb, &mut self.account_label_edit, &mut text_edit_opts);
|
||||
let mut name_edit = TextEdit::new(Id::from(modal.id).with(wallet.get_config().id));
|
||||
name_edit.ui(ui, &mut self.account_label_edit, cb);
|
||||
if name_edit.enter_pressed {
|
||||
on_create(self);
|
||||
}
|
||||
|
||||
// Show error occurred during account creation..
|
||||
// Show error occurred during account creation.
|
||||
if self.account_creation_error {
|
||||
ui.add_space(12.0);
|
||||
ui.label(RichText::new(t!("error"))
|
||||
@@ -100,25 +115,9 @@ impl WalletAccountsModal {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Create button callback.
|
||||
let mut on_create = || {
|
||||
if !self.account_label_edit.is_empty() {
|
||||
let label = &self.account_label_edit;
|
||||
match wallet.create_account(label) {
|
||||
Ok(_) => {
|
||||
let _ = wallet.set_active_account(label);
|
||||
modal.close();
|
||||
},
|
||||
Err(_) => self.account_creation_error = true
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
View::on_enter_key(ui, || {
|
||||
(on_create)();
|
||||
View::button(ui, t!("create"), Colors::white_or_black(false), || {
|
||||
on_create(self);
|
||||
});
|
||||
|
||||
View::button(ui, t!("create"), Colors::white_or_black(false), on_create);
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -17,8 +17,8 @@ use egui::{Id, RichText};
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CLOCK_COUNTDOWN, PASSWORD, PENCIL};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::types::{ModalPosition, TextEditOptions};
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::gui::views::types::ModalPosition;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Common wallet settings content.
|
||||
@@ -26,8 +26,6 @@ pub struct CommonSettings {
|
||||
/// Wallet name [`Modal`] value.
|
||||
name_edit: String,
|
||||
|
||||
/// Flag to check if password change [`Modal`] was opened at first time to focus input field.
|
||||
first_edit_pass_opening: bool,
|
||||
/// Flag to check if wrong password was entered.
|
||||
wrong_pass: bool,
|
||||
/// Current wallet password [`Modal`] value.
|
||||
@@ -50,7 +48,6 @@ impl Default for CommonSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name_edit: "".to_string(),
|
||||
first_edit_pass_opening: true,
|
||||
wrong_pass: false,
|
||||
old_pass_edit: "".to_string(),
|
||||
new_pass_edit: "".to_string(),
|
||||
@@ -99,7 +96,6 @@ impl CommonSettings {
|
||||
let pass_text = format!("{} {}", PASSWORD, t!("change"));
|
||||
View::button(ui, pass_text, Colors::white_or_black(false), || {
|
||||
// Setup modal values.
|
||||
self.first_edit_pass_opening = true;
|
||||
self.old_pass_edit = "".to_string();
|
||||
self.new_pass_edit = "".to_string();
|
||||
self.wrong_pass = false;
|
||||
@@ -176,17 +172,25 @@ impl CommonSettings {
|
||||
wallet: &Wallet,
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut CommonSettings| {
|
||||
if !c.name_edit.is_empty() {
|
||||
wallet.change_name(c.name_edit.clone());
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("wallets.name"))
|
||||
.size(17.0)
|
||||
.color(Colors::gray()));
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Show wallet name text edit.
|
||||
let name_edit_id = Id::from(modal.id).with(wallet.get_config().id);
|
||||
let mut name_edit_opts = TextEditOptions::new(name_edit_id);
|
||||
View::text_edit(ui, cb, &mut self.name_edit, &mut name_edit_opts);
|
||||
let mut name_edit = TextEdit::new(Id::from(modal.id).with(wallet.get_config().id));
|
||||
name_edit.ui(ui, &mut self.name_edit, cb);
|
||||
if name_edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
ui.add_space(12.0);
|
||||
});
|
||||
|
||||
@@ -203,19 +207,9 @@ impl CommonSettings {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if !self.name_edit.is_empty() {
|
||||
wallet.change_name(self.name_edit.clone());
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
View::on_enter_key(ui, || {
|
||||
(on_save)();
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -229,6 +223,23 @@ impl CommonSettings {
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let wallet_id = wallet.get_config().id;
|
||||
let on_continue = |c: &mut CommonSettings| {
|
||||
if c.new_pass_edit.is_empty() {
|
||||
return;
|
||||
}
|
||||
let old_pass = c.old_pass_edit.clone();
|
||||
let new_pass = c.new_pass_edit.clone();
|
||||
match wallet.change_password(old_pass, new_pass) {
|
||||
Ok(_) => {
|
||||
// Clear password values.
|
||||
c.old_pass_edit = "".to_string();
|
||||
c.new_pass_edit = "".to_string();
|
||||
// Close modal.
|
||||
modal.close();
|
||||
}
|
||||
Err(_) => c.wrong_pass = true
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
@@ -239,12 +250,10 @@ impl CommonSettings {
|
||||
|
||||
// Draw old password text edit.
|
||||
let pass_edit_id = Id::from(modal.id).with(wallet_id).with("old_pass");
|
||||
let mut pass_edit_opts = TextEditOptions::new(pass_edit_id).password().no_focus();
|
||||
if self.first_edit_pass_opening {
|
||||
self.first_edit_pass_opening = false;
|
||||
pass_edit_opts.focus = true;
|
||||
}
|
||||
View::text_edit(ui, cb, &mut self.old_pass_edit, &mut pass_edit_opts);
|
||||
let mut pass_edit = TextEdit::new(pass_edit_id)
|
||||
.password()
|
||||
.focus(Modal::first_draw());
|
||||
pass_edit.ui(ui, &mut self.old_pass_edit, cb);
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.label(RichText::new(t!("wallets.new_pass"))
|
||||
@@ -254,10 +263,16 @@ impl CommonSettings {
|
||||
|
||||
// Draw new password text edit.
|
||||
let new_pass_edit_id = Id::from(modal.id).with(wallet_id).with("new_pass");
|
||||
let mut new_pass_edit_opts = TextEditOptions::new(new_pass_edit_id)
|
||||
let mut new_pass_edit = TextEdit::new(new_pass_edit_id)
|
||||
.password()
|
||||
.no_focus();
|
||||
View::text_edit(ui, cb, &mut self.new_pass_edit, &mut new_pass_edit_opts);
|
||||
.focus(false);
|
||||
if pass_edit.enter_pressed {
|
||||
new_pass_edit.focus_request();
|
||||
}
|
||||
new_pass_edit.ui(ui, &mut self.new_pass_edit, cb);
|
||||
if new_pass_edit.enter_pressed {
|
||||
on_continue(self);
|
||||
}
|
||||
|
||||
// Show information when password is empty.
|
||||
if self.old_pass_edit.is_empty() || self.new_pass_edit.is_empty() {
|
||||
@@ -287,31 +302,9 @@ impl CommonSettings {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Callback for button to continue.
|
||||
let mut on_continue = || {
|
||||
if self.new_pass_edit.is_empty() {
|
||||
return;
|
||||
}
|
||||
let old_pass = self.old_pass_edit.clone();
|
||||
let new_pass = self.new_pass_edit.clone();
|
||||
match wallet.change_password(old_pass, new_pass) {
|
||||
Ok(_) => {
|
||||
// Clear password values.
|
||||
self.old_pass_edit = "".to_string();
|
||||
self.new_pass_edit = "".to_string();
|
||||
// Close modal.
|
||||
modal.close();
|
||||
}
|
||||
Err(_) => self.wrong_pass = true
|
||||
}
|
||||
};
|
||||
|
||||
// Continue on Enter key press.
|
||||
View::on_enter_key(ui, || {
|
||||
(on_continue)();
|
||||
View::button(ui, t!("change"), Colors::white_or_black(false), || {
|
||||
on_continue(self);
|
||||
});
|
||||
|
||||
View::button(ui, t!("change"), Colors::white_or_black(false), on_continue);
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
@@ -324,6 +317,13 @@ impl CommonSettings {
|
||||
wallet: &Wallet,
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let on_save = |c: &mut CommonSettings| {
|
||||
if let Ok(min_conf) = c.min_confirmations_edit.parse::<u64>() {
|
||||
wallet.update_min_confirmations(min_conf);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("wallets.min_tx_conf_count"))
|
||||
@@ -332,8 +332,11 @@ impl CommonSettings {
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Minimum amount of confirmations text edit.
|
||||
let mut text_edit_opts = TextEditOptions::new(Id::from(modal.id)).h_center();
|
||||
View::text_edit(ui, cb, &mut self.min_confirmations_edit, &mut text_edit_opts);
|
||||
let mut min_confirmations_edit = TextEdit::new(Id::from(modal.id)).h_center();
|
||||
min_confirmations_edit.ui(ui, &mut self.min_confirmations_edit, cb);
|
||||
if min_confirmations_edit.enter_pressed {
|
||||
on_save(self);
|
||||
}
|
||||
|
||||
// Show error when specified value is not valid or reminder to restart enabled node.
|
||||
if self.min_confirmations_edit.parse::<u64>().is_err() {
|
||||
@@ -358,19 +361,9 @@ impl CommonSettings {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Save button callback.
|
||||
let on_save = || {
|
||||
if let Ok(min_conf) = self.min_confirmations_edit.parse::<u64>() {
|
||||
wallet.update_min_confirmations(min_conf);
|
||||
modal.close();
|
||||
}
|
||||
};
|
||||
|
||||
View::on_enter_key(ui, || {
|
||||
(on_save)();
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), || {
|
||||
on_save(self);
|
||||
});
|
||||
|
||||
View::button(ui, t!("modal.save"), Colors::white_or_black(false), on_save);
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
|
||||
@@ -19,8 +19,8 @@ use grin_util::ZeroingString;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{EYE, LIFEBUOY, STETHOSCOPE, TRASH, WRENCH};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Modal, View};
|
||||
use crate::gui::views::types::{ModalPosition, TextEditOptions};
|
||||
use crate::gui::views::{Modal, TextEdit, View};
|
||||
use crate::gui::views::types::ModalPosition;
|
||||
use crate::node::Node;
|
||||
use crate::wallet::types::ConnectionMethod;
|
||||
use crate::wallet::Wallet;
|
||||
@@ -182,6 +182,18 @@ impl RecoverySettings {
|
||||
wallet: &Wallet,
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let on_next = |c: &mut RecoverySettings| {
|
||||
match wallet.get_recovery(c.pass_edit.clone()) {
|
||||
Ok(phrase) => {
|
||||
c.wrong_pass = false;
|
||||
c.recovery_phrase = Some(phrase);
|
||||
}
|
||||
Err(_) => {
|
||||
c.wrong_pass = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ui.add_space(6.0);
|
||||
if self.recovery_phrase.is_some() {
|
||||
ui.vertical_centered(|ui| {
|
||||
@@ -205,8 +217,12 @@ impl RecoverySettings {
|
||||
|
||||
// Draw current wallet password text edit.
|
||||
let pass_edit_id = Id::from(modal.id).with(wallet.get_config().id);
|
||||
let mut pass_edit_opts = TextEditOptions::new(pass_edit_id).password();
|
||||
View::text_edit(ui, cb, &mut self.pass_edit, &mut pass_edit_opts);
|
||||
let mut pass_edit = TextEdit::new(pass_edit_id)
|
||||
.password();
|
||||
pass_edit.ui(ui, &mut self.pass_edit, cb);
|
||||
if pass_edit.enter_pressed {
|
||||
on_next(self);
|
||||
}
|
||||
|
||||
// Show information when password is empty or wrong.
|
||||
if self.pass_edit.is_empty() {
|
||||
@@ -236,22 +252,8 @@ impl RecoverySettings {
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
let mut on_next = || {
|
||||
match wallet.get_recovery(self.pass_edit.clone()) {
|
||||
Ok(phrase) => {
|
||||
self.wrong_pass = false;
|
||||
self.recovery_phrase = Some(phrase);
|
||||
}
|
||||
Err(_) => {
|
||||
self.wrong_pass = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
View::on_enter_key(ui, || {
|
||||
(on_next)();
|
||||
});
|
||||
View::button(ui, "OK".to_owned(), Colors::white_or_black(false), || {
|
||||
on_next();
|
||||
on_next(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,17 +21,13 @@ use parking_lot::RwLock;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{CameraContent, Modal, View};
|
||||
use crate::gui::views::types::TextEditOptions;
|
||||
use crate::gui::views::{CameraContent, Modal, TextEdit, View};
|
||||
use crate::gui::views::wallets::wallet::WalletTransactionModal;
|
||||
use crate::wallet::types::WalletTransaction;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Transport sending [`Modal`] content.
|
||||
pub struct TransportSendModal {
|
||||
/// Flag to focus on first input field after opening.
|
||||
first_draw: bool,
|
||||
|
||||
/// Flag to check if transaction is sending to show progress.
|
||||
sending: bool,
|
||||
/// Flag to check if there is an error to repeat.
|
||||
@@ -57,7 +53,6 @@ impl TransportSendModal {
|
||||
/// Create new instance from provided address.
|
||||
pub fn new(addr: Option<String>) -> Self {
|
||||
Self {
|
||||
first_draw: true,
|
||||
sending: false,
|
||||
error: false,
|
||||
send_result: Arc::new(RwLock::new(None)),
|
||||
@@ -100,8 +95,7 @@ impl TransportSendModal {
|
||||
ui.add_space(6.0);
|
||||
// Draw QR code scanner content if requested.
|
||||
if let Some(scanner) = self.address_scan_content.as_mut() {
|
||||
let mut on_stop = || {
|
||||
self.first_draw = true;
|
||||
let on_stop = || {
|
||||
cb.stop_camera();
|
||||
modal.enable_closing();
|
||||
};
|
||||
@@ -150,13 +144,11 @@ impl TransportSendModal {
|
||||
|
||||
// Draw amount text edit.
|
||||
let amount_edit_id = Id::from(modal.id).with("amount").with(wallet.get_config().id);
|
||||
let mut amount_edit_opts = TextEditOptions::new(amount_edit_id).h_center().no_focus();
|
||||
let mut amount_edit = TextEdit::new(amount_edit_id)
|
||||
.h_center()
|
||||
.focus(Modal::first_draw());
|
||||
let amount_edit_before = self.amount_edit.clone();
|
||||
if self.first_draw {
|
||||
self.first_draw = false;
|
||||
amount_edit_opts.focus = true;
|
||||
}
|
||||
View::text_edit(ui, cb, &mut self.amount_edit, &mut amount_edit_opts);
|
||||
amount_edit.ui(ui, &mut self.amount_edit, cb);
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Check value if input was changed.
|
||||
@@ -173,7 +165,7 @@ impl TransportSendModal {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Check input after ".".
|
||||
// Check input after `.`.
|
||||
let parts = self.amount_edit.split(".").collect::<Vec<&str>>();
|
||||
if parts.len() == 2 && parts[1].len() > 9 {
|
||||
self.amount_edit = amount_edit_before;
|
||||
@@ -211,15 +203,17 @@ impl TransportSendModal {
|
||||
// Draw address text edit.
|
||||
let addr_edit_before = self.address_edit.clone();
|
||||
let address_edit_id = Id::from(modal.id).with("_address").with(wallet.get_config().id);
|
||||
let mut address_edit_opts = TextEditOptions::new(address_edit_id)
|
||||
let mut address_edit = TextEdit::new(address_edit_id)
|
||||
.paste()
|
||||
.no_focus()
|
||||
.focus(false)
|
||||
.scan_qr();
|
||||
View::text_edit(ui, cb, &mut self.address_edit, &mut address_edit_opts);
|
||||
if amount_edit.enter_pressed {
|
||||
address_edit.focus_request();
|
||||
}
|
||||
address_edit.ui(ui, &mut self.address_edit, cb);
|
||||
// Check if scan button was pressed.
|
||||
if address_edit_opts.scan_pressed {
|
||||
if address_edit.scan_pressed {
|
||||
modal.disable_closing();
|
||||
address_edit_opts.scan_pressed = false;
|
||||
self.address_scan_content = Some(CameraContent::default());
|
||||
}
|
||||
ui.add_space(12.0);
|
||||
@@ -229,6 +223,11 @@ impl TransportSendModal {
|
||||
self.address_error = false;
|
||||
}
|
||||
|
||||
// Send on Enter press.
|
||||
if address_edit.enter_pressed {
|
||||
self.send(wallet, modal);
|
||||
}
|
||||
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ use egui::{Id, RichText};
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{CameraContent, Modal, View};
|
||||
use crate::gui::views::types::TextEditOptions;
|
||||
use crate::gui::views::{CameraContent, Modal, TextEdit, View};
|
||||
use crate::tor::{Tor, TorBridge, TorConfig};
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
@@ -160,16 +159,16 @@ impl TransportSettingsModal {
|
||||
let bin_edit_id = Id::from(modal.id)
|
||||
.with(wallet.get_config().id)
|
||||
.with("_bin_edit");
|
||||
let mut bin_edit_opts = TextEditOptions::new(bin_edit_id)
|
||||
let mut bin_edit = TextEdit::new(bin_edit_id)
|
||||
.paste()
|
||||
.no_focus();
|
||||
.focus(false);
|
||||
let bin_edit_before = self.bridge_bin_path_edit.clone();
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("transport.bin_file"))
|
||||
.size(17.0)
|
||||
.color(Colors::inactive_text()));
|
||||
ui.add_space(6.0);
|
||||
View::text_edit(ui, cb, &mut self.bridge_bin_path_edit, &mut bin_edit_opts);
|
||||
bin_edit.ui(ui, &mut self.bridge_bin_path_edit, cb);
|
||||
ui.add_space(6.0);
|
||||
});
|
||||
|
||||
@@ -178,20 +177,19 @@ impl TransportSettingsModal {
|
||||
let conn_edit_id = Id::from(modal.id)
|
||||
.with(wallet.get_config().id)
|
||||
.with("_conn_edit");
|
||||
let mut conn_edit_opts = TextEditOptions::new(conn_edit_id)
|
||||
let mut conn_edit = TextEdit::new(conn_edit_id)
|
||||
.paste()
|
||||
.no_focus()
|
||||
.focus(false)
|
||||
.scan_qr();
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("transport.conn_line"))
|
||||
.size(17.0)
|
||||
.color(Colors::inactive_text()));
|
||||
ui.add_space(6.0);
|
||||
View::text_edit(ui, cb, &mut self.bridge_conn_line_edit, &mut conn_edit_opts);
|
||||
conn_edit.ui(ui, &mut self.bridge_conn_line_edit, cb);
|
||||
// Check if scan button was pressed.
|
||||
if conn_edit_opts.scan_pressed {
|
||||
if conn_edit.scan_pressed {
|
||||
modal.disable_closing();
|
||||
conn_edit_opts.scan_pressed = false;
|
||||
self.bridge_qr_scan_content = Some(CameraContent::default());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -46,6 +46,8 @@ pub struct AppConfig {
|
||||
|
||||
/// Locale code for i18n.
|
||||
lang: Option<String>,
|
||||
/// Flag to use English locale layout on keyboard.
|
||||
english_keyboard: Option<bool>,
|
||||
|
||||
/// Flag to check if dark theme should be used, use system settings if not set.
|
||||
use_dark_theme: Option<bool>,
|
||||
@@ -64,6 +66,7 @@ impl Default for AppConfig {
|
||||
x: None,
|
||||
y: None,
|
||||
lang: None,
|
||||
english_keyboard: None,
|
||||
use_dark_theme: None,
|
||||
}
|
||||
}
|
||||
@@ -212,6 +215,20 @@ impl AppConfig {
|
||||
None
|
||||
}
|
||||
|
||||
/// Toggle English locale layout. for software keyboard.
|
||||
pub fn toggle_english_keyboard() {
|
||||
let english = Self::english_keyboard();
|
||||
let mut w_app_config = Settings::app_config_to_update();
|
||||
w_app_config.english_keyboard = Some(!english);
|
||||
w_app_config.save();
|
||||
}
|
||||
|
||||
/// Check if English locale layout should be used for software keyboard.
|
||||
pub fn english_keyboard() -> bool {
|
||||
let r_config = Settings::app_config_to_read();
|
||||
r_config.english_keyboard.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check if integrated node warning is needed for Android.
|
||||
pub fn android_integrated_node_warning_needed() -> bool {
|
||||
let r_config = Settings::app_config_to_read();
|
||||
|
||||
Reference in New Issue
Block a user