From 24e7af64f2e443df2ca3b11323fbbbe0dede2234 Mon Sep 17 00:00:00 2001 From: Surinder Singh Matoo Date: Thu, 8 Aug 2024 03:37:15 +0530 Subject: [PATCH 1/6] short info text with links to explore and copy btn, 1) short info text with links to explore and copy btn 2) now UI on account/wallet page wraps bellow screen width 800px --- core/src/egui/extensions.rs | 93 ++++++++- core/src/modules/account_manager/details.rs | 2 +- core/src/modules/account_manager/mod.rs | 10 +- core/src/primitives/descriptor.rs | 212 +++++++++++++++----- core/src/primitives/transaction.rs | 32 +-- 5 files changed, 280 insertions(+), 69 deletions(-) diff --git a/core/src/egui/extensions.rs b/core/src/egui/extensions.rs index f67d944..5ee3e67 100644 --- a/core/src/egui/extensions.rs +++ b/core/src/egui/extensions.rs @@ -233,6 +233,7 @@ pub struct LayoutJobBuilder { leading: f32, icon_font_id: Option, font_id: Option, + heading: Option<(f32, f32, String, Color32)>, } impl LayoutJobBuilder { @@ -251,6 +252,7 @@ impl LayoutJobBuilder { job, leading, font_id, + heading: None, ..Default::default() } } @@ -302,21 +304,106 @@ impl LayoutJobBuilder { self } + pub fn heading(mut self, ui: &mut Ui, width: f32, text: &str, color: Color32) -> Self { + let galley = ui.painter().layout_no_wrap( + text.to_string(), + self.font_id.clone().unwrap_or_default(), + color, + ); + self.heading = Some((width, galley.size().y, text.to_string(), color)); + self + } + pub fn label(self, ui: &mut Ui) -> Response { - ui.label(self.job) + Self::render_label(ui, self.job, self.heading) } - pub fn hyperlink(self, ui: &mut Ui, text: &str, url: &str, color: Color32) { + fn render_label( + ui: &mut Ui, + job: LayoutJob, + heading: Option<(f32, f32, String, Color32)>, + ) -> Response { + if let Some((x, y, text, color)) = heading { + let desired_size = Vec2 { x, y }; + ui.horizontal(|ui| { + ui.allocate_ui_with_layout( + desired_size, + Layout::right_to_left(Align::Center), + |ui| ui.label(RichText::new(text).color(color).font(FontId::default())), + ); + ui.label(job) + }) + .inner + } else { + ui.label(job) + } + } + + pub fn hyperlink_with_clipboard_icon( + self, + ui: &mut Ui, + text: &str, + url: &str, + color: Color32, + clipboard_text: Option, + ) { ui.horizontal(|ui| { - ui.label(self.job); + Self::render_label(ui, self.job, self.heading); ui.hyperlink_to_tab( RichText::new(text) .font(self.font_id.unwrap_or_default()) .color(color), url, ); + if let Some(text) = clipboard_text { + Self::clipboard_icon(ui, text); + } + }); + } + pub fn hyperlink(self, ui: &mut Ui, text: &str, url: &str, color: Color32) { + self.hyperlink_with_clipboard_icon(ui, text, url, color, None) + } + pub fn transaction_id(self, ui: &mut Ui, txid: &str, url: &str, color: Color32) { + self.hyperlink_with_clipboard_icon( + ui, + &format_partial_string(txid, Some(6)), + url, + color, + Some(txid.to_string()), + ) + } + pub fn script(self, ui: &mut Ui, script: &str, color: Color32) { + let this = self.text(&format_partial_string(script, Some(8)), color); + ui.horizontal(|ui| { + Self::render_label(ui, this.job, this.heading); + Self::clipboard_icon(ui, script.to_string()); }); } + pub fn with_clipboard_icon(self, ui: &mut Ui, text: &str) { + ui.horizontal(|ui| { + Self::render_label(ui, self.job, self.heading); + Self::clipboard_icon(ui, text.to_string()); + }); + } + pub fn address(self, ui: &mut Ui, address: &str, url: &str, color: Color32) { + self.hyperlink_with_clipboard_icon( + ui, + &format_address_string(address, Some(6)), + url, + color, + Some(address.to_string()), + ) + } + + pub fn clipboard_icon(ui: &mut Ui, text: String) { + if ui + .add(Label::new(egui_phosphor::light::CLIPBOARD_TEXT).sense(Sense::click())) + .clicked() + { + ui.output_mut(|o| o.copied_text = text); + runtime().notify_clipboard(i18n("Copied to clipboard")); + } + } } impl From for LayoutJob { diff --git a/core/src/modules/account_manager/details.rs b/core/src/modules/account_manager/details.rs index 43122fd..3e9b5b7 100644 --- a/core/src/modules/account_manager/details.rs +++ b/core/src/modules/account_manager/details.rs @@ -16,7 +16,7 @@ impl Details { let descriptor = account.descriptor(); - descriptor.render(ui); + descriptor.render(ui, account.network()); ui.add_space(8.); let mut address_kind : Option = None; diff --git a/core/src/modules/account_manager/mod.rs b/core/src/modules/account_manager/mod.rs index dfac97e..ce63242 100644 --- a/core/src/modules/account_manager/mod.rs +++ b/core/src/modules/account_manager/mod.rs @@ -275,10 +275,10 @@ impl AccountManager { account: account.clone(), }; - if device.orientation() == Orientation::Portrait { + if device.orientation() == Orientation::Portrait || Self::single_pane(&device){ self.section = AccountManagerSection::Overview; } else { - self.section = AccountManagerSection::Transactions; + self.section = AccountManagerSection::Details; } if notify { @@ -413,7 +413,7 @@ impl AccountManager { if core.device().mobile() { self.render_singular_layout(core,ui,&rc, self.section); - } else if core.device().single_pane() { + } else if core.device().single_pane() || Self::single_pane(core.device()) { self.render_menu(core,ui,&rc); @@ -430,6 +430,10 @@ impl AccountManager { Ok(()) } + fn single_pane(device: &Device)->bool{ + device.screen_size.x < 800. + } + pub fn account(&self) -> Option { if let AccountManagerState::Overview { account } = &self.state { Some(account.clone()) diff --git a/core/src/primitives/descriptor.rs b/core/src/primitives/descriptor.rs index 952509b..1e8af67 100644 --- a/core/src/primitives/descriptor.rs +++ b/core/src/primitives/descriptor.rs @@ -1,64 +1,178 @@ use crate::imports::*; +use crate::primitives::transaction::paint_header_icon; use convert_case::{Case, Casing}; -fn grid(ui: &mut Ui, id: &AccountId, add_contents: impl FnOnce(&mut Ui)) { - CollapsingHeader::new(id.to_string()) - .default_open(true) - .show(ui, |ui| { - Grid::new("bip32_descriptor") - .num_columns(2) - .spacing([20.0, 4.0]) - .show(ui, |ui| { - add_contents(ui); - }); - }); -} +// fn grid(ui: &mut Ui, id: &AccountId, add_contents: impl FnOnce(&mut Ui)) { +// CollapsingHeader::new(id.to_string()) +// .default_open(true) +// .show(ui, |ui| { +// Grid::new("bip32_descriptor") +// .num_columns(3) +// .spacing([20.0, 4.0]) +// .show(ui, |ui| { +// add_contents(ui); +// }); +// }); +// } pub trait RenderAccountDescriptor { - fn render(&self, ui: &mut Ui); + fn render(&self, ui: &mut Ui, network: Network); } impl RenderAccountDescriptor for AccountDescriptor { - fn render(&self, ui: &mut Ui) { - grid(ui, &self.account_id, |ui| { - let color = strong_color(); + fn render(&self, ui: &mut Ui, network: Network) { + let collapsing_header = CollapsingHeader::new(self.account_id.to_string()) + .id_source("bip32_descriptor") + .icon(paint_header_icon) + .default_open(true); + + collapsing_header.show(ui, |ui| { + let default_color = theme_color().default_color; + let color = theme_color().strong_color; + + let explorer = match network { + Network::Mainnet => MAINNET_EXPLORER, + Network::Testnet10 => TESTNET10_EXPLORER, + Network::Testnet11 => TESTNET11_EXPLORER, + }; + let pixels_per_point = ui.ctx().pixels_per_point(); + let one_char_width = ui + .painter() + .layout_no_wrap('x'.to_string(), Default::default(), color) + .size() + .x; + let heading_width = one_char_width * 18.0; + let width = ui.available_width() / pixels_per_point; + let content = LayoutJobBuilderSettings::new(width, 8.0, None); + ljb(&content) + .heading(ui, heading_width, i18n("Account Name"), default_color) + .text( + self.account_name.as_ref().unwrap_or(&"...".to_string()), + color, + ) + .label(ui); + ljb(&content) + .heading(ui, heading_width, i18n("Type"), default_color) + .text( + &self.account_kind().as_ref().to_case(Case::UpperCamel), + color, + ) + .label(ui); - ui.label(i18n("Account Name")); - ui.colored_label( - color, - self.account_name.as_ref().unwrap_or(&"...".to_string()), - ); - ui.end_row(); - ui.label(i18n("Type")); - ui.colored_label( - color, - self.account_kind().as_ref().to_case(Case::UpperCamel), - ); - ui.end_row(); - ui.label(i18n("Receive Address")); - ui.colored_label( - color, - self.receive_address - .as_ref() - .map(String::from) - .unwrap_or("N/A".to_string()), - ); - ui.end_row(); - ui.label(i18n("Change Address")); - ui.colored_label( - color, - self.change_address - .as_ref() - .map(String::from) - .unwrap_or("N/A".to_string()), - ); - ui.end_row(); + let job = + ljb(&content).heading(ui, heading_width, i18n("Receive Address"), default_color); + match self.receive_address.as_ref() { + Some(address) => { + job.address( + ui, + &address.to_string(), + &format!("{explorer}/addresses/{address}"), + color, + ); + } + None => { + job.text("N/A", color).label(ui); + } + } + + let job = + ljb(&content).heading(ui, heading_width, i18n("Change Address"), default_color); + match self.change_address.as_ref() { + Some(address) => { + job.address( + ui, + &address.to_string(), + &format!("{explorer}/addresses/{address}"), + color, + ); + } + None => { + job.text("N/A", color).label(ui); + } + } for (prop, value) in self.properties.iter() { - ui.label(i18n(prop.to_string().as_str())); - ui.colored_label(color, value.to_string()); - ui.end_row(); + let text = value.to_string(); + let job = ljb(&content).heading( + ui, + heading_width, + i18n(prop.to_string().as_str()), + default_color, + ); + + if text.len() > 20 { + job.text(&format_partial_string(&text, Some(10)), color) + .with_clipboard_icon(ui, &text); + } else { + job.text(&text, color).label(ui); + } } + + //grid(ui, &self.account_id, |ui| { + + // ui.label(i18n("Account Name")); + // ui.colored_label( + // color, + // self.account_name.as_ref().unwrap_or(&"...".to_string()), + // ); + // ui.label(""); + // ui.end_row(); + // ui.label(i18n("Type")); + // ui.colored_label( + // color, + // self.account_kind().as_ref().to_case(Case::UpperCamel), + // ); + // ui.label(""); + // ui.end_row(); + //ui.label(i18n("Receive Address")); + + // match self.receive_address.as_ref(){ + // Some(address)=>{ + // ui.hyperlink_to_tab( + // RichText::new(format_address(address, None)) + // //.font(self.font_id.unwrap_or_default()) + // .color(color), + // &format!("{explorer}/addresses/{address}"), + // ); + // ui.label("xxxx"); + // //LayoutJobBuilder::clipboard_icon(&mut ui[1], address.to_string()); + // } + // None=>{ + // ui.colored_label( + // color, + // "N/A".to_string() + // ); + // } + // } + + // ui.end_row(); + // ui.label(i18n("Change Address")); + // match self.change_address.as_ref(){ + // Some(address)=>{ + // ui.hyperlink_to_tab( + // RichText::new(format_address(address, None)) + // //.font(self.font_id.unwrap_or_default()) + // .color(color), + // &format!("{explorer}/addresses/{address}"), + // ); + // LayoutJobBuilder::clipboard_icon(ui, address.to_string()); + // } + // None=>{ + // ui.colored_label( + // color, + // "N/A".to_string() + // ); + // } + // } + + // ui.end_row(); + + // for (prop, value) in self.properties.iter() { + // ui.label(i18n(prop.to_string().as_str())); + // ui.colored_label(color, value.to_string()); + // ui.label(""); + // ui.end_row(); + // } }); } } diff --git a/core/src/primitives/transaction.rs b/core/src/primitives/transaction.rs index 90624be..041a729 100644 --- a/core/src/primitives/transaction.rs +++ b/core/src/primitives/transaction.rs @@ -264,7 +264,7 @@ impl Transaction { collapsing_header.show(ui, |ui| { ljb(&content) .padded(15, "Transaction id:", default_color) - .hyperlink( + .transaction_id( ui, &transaction_id, &format!("{explorer}/txs/{transaction_id}"), @@ -289,7 +289,7 @@ impl Transaction { .map(|addr| addr.to_string()) .unwrap_or_else(|| "n/a".to_string()); - ljb(&content).hyperlink( + ljb(&content).address( ui, &address, &format!("{explorer}/addresses/{address}"), @@ -306,12 +306,18 @@ impl Transaction { .label(ui); } - ljb(&content) - .text( - &format!("Script: {}", script_public_key.script_as_hex()), - default_color, - ) - .label(ui); + ljb(&content).text("Script:", default_color).script( + ui, + &script_public_key.script_as_hex(), + default_color, + ); + + // ljb(&content) + // .text( + // &format!("Script: {}", script_public_key.script_as_hex()), + // default_color, + // ) + // .label(ui); }); }); } @@ -432,7 +438,7 @@ impl Transaction { collapsing_header.show(ui, |ui| { ljb(&content) .padded(15, "Transaction id:", default_color) - .hyperlink( + .transaction_id( ui, &transaction_id, &format!("{explorer}/txs/{transaction_id}"), @@ -494,16 +500,16 @@ impl Transaction { transaction_id, index, } = previous_outpoint; - + let transaction_id = transaction_id.to_string(); ljb(&content) .text( &format!( " {sequence:>2}: {}:{index} SigOps: {sig_op_count}", - transaction_id + format_partial_string(&transaction_id, Some(6)) ), default_color, ) - .label(ui); + .with_clipboard_icon(ui, &transaction_id); } ljb(&content) @@ -537,7 +543,7 @@ impl Transaction { match address_info { Ok(address) => { let address = address.to_string(); - ljb(&content).padded(2, "", default_color).hyperlink( + ljb(&content).padded(2, "", default_color).address( ui, &address, &format!("{explorer}/addresses/{address}"), From aafe0c621bf47cda91de1cd3691608131c2ecb08 Mon Sep 17 00:00:00 2001 From: Surinder Singh Matoo Date: Sat, 10 Aug 2024 08:23:45 +0530 Subject: [PATCH 2/6] init --- core/resources/i18n/i18n.json | 10 +- core/src/egui/mod.rs | 2 + core/src/egui/selection_panels.rs | 265 ++++++++++++++++++++++++++++++ core/src/modules/testing.rs | 54 +++++- 4 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 core/src/egui/selection_panels.rs diff --git a/core/resources/i18n/i18n.json b/core/resources/i18n/i18n.json index fa09d71..0b16b11 100644 --- a/core/resources/i18n/i18n.json +++ b/core/resources/i18n/i18n.json @@ -3084,6 +3084,7 @@ "12 word mnemonic": "12 word mnemonic", "24 word mnemonic": "24 word mnemonic", "24h Change": "24h Change", + "3 hours or more": "3 hours or more", "A binary at another location is spawned a child process (experimental, for development purposes only).": "A binary at another location is spawned a child process (experimental, for development purposes only).", "A random node will be selected on startup": "A random node will be selected on startup", "A wallet is stored in a file on your computer.": "A wallet is stored in a file on your computer.", @@ -3184,6 +3185,7 @@ "Donations": "Donations", "Double click on the graph to re-center...": "Double click on the graph to re-center...", "ECDSA": "ECDSA", + "Economic": "Economic", "Enable Market Monitor": "Enable Market Monitor", "Enable UPnP": "Enable UPnP", "Enable custom daemon arguments": "Enable custom daemon arguments", @@ -3261,6 +3263,7 @@ "Local": "Local", "Local p2p Node Configuration": "Local p2p Node Configuration", "Logs": "Logs", + "Low-priority": "Low-priority", "MT": "MT", "Main Kaspa network": "Main Kaspa network", "Mainnet": "Mainnet", @@ -3278,6 +3281,7 @@ "Mempool Size": "Mempool Size", "Metrics": "Metrics", "Metrics are not currently available": "Metrics are not currently available", + "Miner Fee": "Miner Fee", "Minimize the window": "Minimize the window", "Mnemonic Import": "Mnemonic Import", "NEXT": "NEXT", @@ -3297,6 +3301,7 @@ "Node Status": "Node Status", "Noise": "Noise", "None": "None", + "Normal": "Normal", "Not Connected": "Not Connected", "Not connected": "Not connected", "Notifications": "Notifications", @@ -3403,6 +3408,7 @@ "Storage": "Storage", "Storage Read": "Storage Read", "Storage Read/s": "Storage Read/s", + "Storage Size": "Storage Size", "Storage Write": "Storage Write", "Storage Write/s": "Storage Write/s", "Strong": "Strong", @@ -3520,7 +3526,9 @@ "wRPC JSON Tx": "wRPC JSON Tx", "wRPC JSON Tx/s": "wRPC JSON Tx/s", "wRPC URL:": "wRPC URL:", - "words": "words" + "words": "words", + "~2 hours": "~2 hours", + "~30 minutes": "~30 minutes" }, "es": { "'Phishing hint' is a secret word or a phrase that is displayed when you open your wallet. If you do not see the hint when opening your wallet, you may be accessing a fake wallet designed to steal your funds. If this occurs, stop using the wallet immediately, check the browser URL domain name and seek help on social networks (Kaspa Discord or Telegram).": "'Phishing hint' is a secret word or a phrase that is displayed when you open your wallet. If you do not see the hint when opening your wallet, you may be accessing a fake wallet designed to steal your funds. If this occurs, stop using the wallet immediately, check the browser URL domain name and seek help on social networks (Kaspa Discord or Telegram).", diff --git a/core/src/egui/mod.rs b/core/src/egui/mod.rs index f96234f..557a64f 100644 --- a/core/src/egui/mod.rs +++ b/core/src/egui/mod.rs @@ -9,6 +9,7 @@ mod network; mod pagination; mod panel; mod popup; +mod selection_panels; mod theme; pub use collapsable::*; @@ -22,4 +23,5 @@ pub use network::NetworkInterfaceEditor; pub use pagination::*; pub use panel::Panel; pub use popup::PopupPanel; +pub use selection_panels::*; pub use theme::*; diff --git a/core/src/egui/selection_panels.rs b/core/src/egui/selection_panels.rs new file mode 100644 index 0000000..4216a76 --- /dev/null +++ b/core/src/egui/selection_panels.rs @@ -0,0 +1,265 @@ +use egui::*; + +// trait LayoutWithMaxRect{ +// fn layout_with_max_rect(&mut self, max_rect:Rect, layout:Layout, add_contents: impl FnOnce(&mut Ui) -> R)->InnerResponse; +// } + +// impl LayoutWithMaxRect for Ui{ +// fn layout_with_max_rect(&mut self, max_rect:Rect, layout:Layout, add_contents: impl FnOnce(&mut Ui) -> R)->InnerResponse { +// let mut child_ui = self.child_ui(max_rect, layout); +// let inner = add_contents(&mut child_ui); +// let rect = child_ui.min_rect(); +// let id = self.advance_cursor_after_rect(rect); + +// InnerResponse::new(inner, self.interact(rect, id, Sense::hover())) + +// } +// } + +type UiBuilderFn = Box; +type FooterUiBuilderFn = Box; + +pub struct SelectionPanel { + pub title: WidgetText, + pub sub: WidgetText, + pub value: V, + pub build_heading: Option, + pub build_footer: Option, +} + +impl SelectionPanel { + pub fn new(value: Value, title: impl Into, sub: impl Into) -> Self { + Self { + title: title.into(), + sub: sub.into(), + value, + build_heading: None, + build_footer: None, + } + } + pub fn heading(mut self, build_heading: impl FnOnce(&mut Ui) + 'static) -> Self { + self.build_heading = Some(Box::new(build_heading)); + self + } + pub fn footer(mut self, build_footer: impl FnOnce(&mut Ui) + 'static) -> Self { + self.build_footer = Some(Box::new(build_footer)); + self + } + + pub fn render( + self, + ui: &mut Ui, + bg_color: Color32, + width: f32, + min_height: &mut f32, + current_value: &mut Value, + ) -> Response { + let selected = *current_value == self.value; + let selected_bg = Color32::from_rgb(67, 76, 84); + let hover_stroke_color = Color32::WHITE; + // let mut rect = ui.cursor(); + // rect.set_width(width); + + // ui.painter().rect( + // rect, + // Rounding::ZERO, + // Color32::GRAY, + // Stroke::new(1.0, Color32::GREEN), + // ); + + // let res = ui.layout_with_max_rect(rect, Layout::top_down(Align::Center), |ui|{ + // ui.label(self.title); + // ui.label(self.sub); + // if let Some(build) = self.build_heading{ + // (build)(ui); + // } + // ui.checkbox(&mut selected, ""); + // if let Some(build) = self.build_footer{ + // (build)(ui); + // } + // }).response; + let frame = Frame::none().fill(if selected { selected_bg } else { bg_color }); + let mut prepared = frame.begin(ui); + + let add_contents = |ui: &mut Ui| { + ui.allocate_ui_with_layout( + egui::vec2(width, ui.available_height()), + Layout::top_down(Align::Center), + |ui| { + ui.label(" "); + ui.label(self.title.strong().heading()); + ui.label(self.sub); + if let Some(build) = self.build_heading { + (build)(ui); + } + let icon = if selected { + egui_phosphor::bold::CHECK + } else { + egui_phosphor::bold::DOT_OUTLINE + }; + ui.label(RichText::new(icon).heading().color(Color32::WHITE)); + if let Some(build) = self.build_footer { + ui.visuals_mut().override_text_color = Some(Color32::WHITE); + (build)(ui); + } + ui.label(" "); + }, + ) + .response + }; + + let _res = add_contents(&mut prepared.content_ui); + *min_height = min_height.max(prepared.content_ui.min_rect().height()); + prepared.content_ui.set_min_height(*min_height); + let rect = prepared + .frame + .inner_margin + .expand_rect(prepared.content_ui.min_rect()); + if ui.allocate_rect(rect, Sense::hover()).hovered() { + prepared.frame = prepared.frame.stroke(Stroke::new(1.0, hover_stroke_color)) + } + + let res = prepared.end(ui); + + let mut response = res.interact(Sense::click()); + if response.clicked() && *current_value != self.value { + *current_value = self.value; + response.mark_changed(); + } + response + } +} + +pub struct SelectionPanels { + pub title: WidgetText, + pub panel_min_width: f32, + pub panel_max_width: f32, + pub panels: Vec>, + pub build_footer: FooterUiBuilderFn, + pub panel_min_height: f32, + pub vertical: bool, +} + +impl SelectionPanels { + pub fn new( + panel_min_width: f32, + panel_max_width: f32, + title: impl Into, + build_footer: impl FnOnce(&mut Ui, &mut Value) + 'static, + ) -> Self { + Self { + title: title.into(), + panel_min_width, + panel_max_width, + build_footer: Box::new(build_footer), + panels: vec![], + panel_min_height: 0., + vertical: false, + } + } + pub fn add( + mut self, + value: Value, + title: impl Into, + sub: impl Into, + ) -> Self { + self.panels.push(SelectionPanel::new(value, title, sub)); + self + } + pub fn add_with_footer( + mut self, + value: Value, + title: impl Into, + sub: impl Into, + build_footer: impl FnOnce(&mut Ui) + 'static, + ) -> Self { + self.panels + .push(SelectionPanel::new(value, title, sub).footer(build_footer)); + self + } + pub fn panel_min_height(mut self, min_height: f32) -> Self { + self.panel_min_height = min_height; + self + } + + pub fn vertical(mut self, vertical: bool) -> Self { + self.vertical = vertical; + self + } + + pub fn render(self, ui: &mut Ui, current_value: &mut Value) -> Response { + let frame_bg = Color32::from_rgb(17, 19, 24); + let text_color = Color32::WHITE; + let even_panel_bg = Color32::from_rgb(44, 50, 59); + let odd_panel_bg = Color32::from_rgb(54, 63, 71); + + let frame = Frame::none() + .fill(frame_bg) + .rounding(egui::Rounding::same(10.0)) + .stroke(Stroke::NONE); + + let mut prepared = frame.begin(ui); + let add_contents = |ui: &mut Ui| { + let mut responce = ui.label(" "); + //ui.visuals_mut().override_text_color = Some(Color32::WHITE); + //ui.button(text) + { + let title = self.title.into_galley( + ui, + Some(true), + ui.available_width(), + TextStyle::Heading, + ); + let rect = ui.cursor().translate(Vec2::splat(10.0)); + ui.allocate_exact_size( + title.size() + Vec2::splat(10.0), + Sense::focusable_noninteractive(), + ); + title.paint_with_fallback_color(ui.painter(), rect.min, text_color); + } + + let before_wrap_width = ui.available_rect_before_wrap().width(); + let panel_width = self.panel_min_width.max( + self.panel_max_width + .min(before_wrap_width / self.panels.len() as f32), + ); + let vertical = self.vertical || (before_wrap_width - 2. < panel_width * 3.0); + // ui.label(format!("before_wrap_width: {before_wrap_width}")); + // ui.label(format!("panel_width: {panel_width}")); + let panels_res = ui.horizontal_wrapped(|ui| { + ui.spacing_mut().item_spacing = Vec2::ZERO; + let mut min_height = self.panel_min_height; + for (index, panel) in self.panels.into_iter().enumerate() { + let rect = ui.available_rect_before_wrap(); + if (index > 0 && vertical) || rect.width() - 2.0 < panel_width { + ui.end_row(); + } + let bg_color = if index % 2 == 0 { + even_panel_bg + } else { + odd_panel_bg + }; + responce |= + panel.render(ui, bg_color, panel_width, &mut min_height, current_value); + } + }); + + let total_width = panels_res.response.rect.width(); + ui.allocate_ui_with_layout( + egui::vec2(total_width, ui.available_height()), + Layout::top_down(Align::Center), + |ui| (self.build_footer)(ui, current_value), + ); + ui.label(" "); + // ui.label(format!(" vertical: {vertical}")); + // ui.label(format!("panels width {}", total_width)); + // ui.label(format!("bottom width {}", b.response.rect.width())); + // ui.label(format!("ui.min_rect().width() {}", ui.min_rect().width())); + responce + }; + + let res = add_contents(&mut prepared.content_ui); + //prepared.frame = prepared.frame.fill(Color32::GREEN); + res | prepared.end(ui) + } +} diff --git a/core/src/modules/testing.rs b/core/src/modules/testing.rs index 4501b48..bd50da9 100644 --- a/core/src/modules/testing.rs +++ b/core/src/modules/testing.rs @@ -1,5 +1,4 @@ use kaspa_bip32::{Mnemonic,WordCount}; - use crate::imports::*; // use egui_plot::PlotPoint; @@ -12,9 +11,18 @@ pub enum State { Unlocking, } +#[derive(PartialEq, Debug)] +pub enum FeeMode{ + None, + LowPriority, + Economic, + Normal, +} + pub struct Testing { #[allow(dead_code)] runtime: Runtime, + fee_mode: FeeMode, // pub state: State, // pub message: Option, @@ -44,6 +52,7 @@ impl Testing { Self { runtime, + fee_mode: FeeMode::None, // state: State::Select, // message: None, // graph_data, @@ -95,6 +104,49 @@ impl ModuleT for Testing { if ui.large_button("notify info").clicked() { runtime().notify(UserNotification::info("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, ").short()); } + + + let fee_selection = SelectionPanels::new( + 100.0, + 150.0, + i18n("Miner Fee"), + |ui, value|{ + ui.label("1 in / 2 outputs, ~1.2 Kg"); + ui.label(format!("Fee Mode: {:?}", value)); + }) + //.panel_min_height(300.) + //.vertical(true) + //.add(FeeMode::LowPriority, i18n("Low-priority"), i18n("3 hours or more")) + .add_with_footer(FeeMode::LowPriority, i18n("Low-priority"), i18n("3 hours or more"), |ui|{ + ui.label("12.88716 µKAS"); + ui.label(RichText::new("~0.00000215 USD").strong()); + ui.label("9 SOMPI/G"); + }) + .add_with_footer(FeeMode::Economic, i18n("Economic"), i18n("~2 hours"), |ui|{ + ui.label("15.83525 µKAS"); + ui.label(RichText::new("~0.00000264 USD").strong()); + ui.label("10 SOMPI/G"); + }) + .add_with_footer(FeeMode::Normal, i18n("Normal"), i18n("~30 minutes"), |ui|{ + ui.label("20.78334 µKAS"); + ui.label(RichText::new("~0.00000347 USD").strong()); + ui.label("10 SOMPI/G"); + }); + // .add_with_footer(FeeMode::Economic, i18n("Economic"), i18n("~2 hours"), |ui|{ + // ui.label("13.83525 µKAS"); + // ui.label(RichText::new("~608.83 USD").strong()); + // ui.label("10 SOMPI/G"); + // }) + // .add_with_footer(FeeMode::Normal, i18n("Normal"), i18n("~30 minutes"), |ui|{ + // ui.label("14.78334 µKAS"); + // ui.label(RichText::new("~650.56 USD").strong()); + // ui.label("10 SOMPI/G"); + // }); + + if fee_selection.render(ui, &mut self.fee_mode).clicked(){ + log_info!("clicked: self.fee_mode: {:?}", self.fee_mode); + runtime().toast(UserNotification::success(format!("selection: {:?}", self.fee_mode)).short()) + } } } From d47e2629b2162d9dc67ddc973a2d725a21a5de05 Mon Sep 17 00:00:00 2001 From: Surinder Singh Matoo Date: Sun, 11 Aug 2024 03:03:38 +0530 Subject: [PATCH 3/6] fee UX updated for h-center issue --- core/src/egui/selection_panels.rs | 186 +++++++++++++----- core/src/modules/account_manager/estimator.rs | 48 +++++ core/src/modules/account_manager/mod.rs | 10 + 3 files changed, 196 insertions(+), 48 deletions(-) diff --git a/core/src/egui/selection_panels.rs b/core/src/egui/selection_panels.rs index 4216a76..8aa7a19 100644 --- a/core/src/egui/selection_panels.rs +++ b/core/src/egui/selection_panels.rs @@ -1,20 +1,76 @@ use egui::*; +use std::hash::Hash; -// trait LayoutWithMaxRect{ -// fn layout_with_max_rect(&mut self, max_rect:Rect, layout:Layout, add_contents: impl FnOnce(&mut Ui) -> R)->InnerResponse; -// } +trait UILayoutExt{ + //fn layout_with_max_rect(&mut self, max_rect:Rect, layout:Layout, add_contents: impl FnOnce(&mut Ui) -> R)->InnerResponse; + fn indent_with_size<'c, R>( + &mut self, + id_source: impl Hash, + indent: f32, + add_contents: Box R + 'c>, + ) -> InnerResponse; +} + +impl UILayoutExt for Ui{ + // fn layout_with_max_rect(&mut self, max_rect:Rect, layout:Layout, add_contents: impl FnOnce(&mut Ui) -> R)->InnerResponse { + // let mut child_ui = self.child_ui(max_rect, layout); + // let inner = add_contents(&mut child_ui); + // let rect = child_ui.min_rect(); + // let id = self.advance_cursor_after_rect(rect); + + // InnerResponse::new(inner, self.interact(rect, id, Sense::hover())) + + // } -// impl LayoutWithMaxRect for Ui{ -// fn layout_with_max_rect(&mut self, max_rect:Rect, layout:Layout, add_contents: impl FnOnce(&mut Ui) -> R)->InnerResponse { -// let mut child_ui = self.child_ui(max_rect, layout); -// let inner = add_contents(&mut child_ui); -// let rect = child_ui.min_rect(); -// let id = self.advance_cursor_after_rect(rect); + fn indent_with_size<'c, R>( + &mut self, + id_source: impl Hash, + indent: f32, + add_contents: Box R + 'c>, + ) -> InnerResponse { + assert!( + self.layout().is_vertical(), + "You can only indent vertical layouts, found {:?}", + self.layout() + ); -// InnerResponse::new(inner, self.interact(rect, id, Sense::hover())) + let mut child_rect = self.available_rect_before_wrap(); + child_rect.min.x += indent; -// } -// } + let mut child_ui = self.child_ui_with_id_source(child_rect, *self.layout(), id_source); + let ret = add_contents(&mut child_ui); + + // let left_vline = self.visuals().indent_has_left_vline; + // let end_with_horizontal_line = self.spacing().indent_ends_with_horizontal_line; + + // if left_vline || end_with_horizontal_line { + // if end_with_horizontal_line { + // child_ui.add_space(4.0); + // } + + // let stroke = self.visuals().widgets.noninteractive.bg_stroke; + // let left_top = child_rect.min - 0.5 * indent * Vec2::X; + // let left_top = self.painter().round_pos_to_pixels(left_top); + // let left_bottom = pos2(left_top.x, child_ui.min_rect().bottom() - 2.0); + // let left_bottom = self.painter().round_pos_to_pixels(left_bottom); + + // if left_vline { + // // draw a faint line on the left to mark the indented section + // self.painter.line_segment([left_top, left_bottom], stroke); + // } + + // if end_with_horizontal_line { + // let fudge = 2.0; // looks nicer with button rounding in collapsing headers + // let right_bottom = pos2(child_ui.min_rect().right() - fudge, left_bottom.y); + // self.painter + // .line_segment([left_bottom, right_bottom], stroke); + // } + // } + + let response = self.allocate_rect(child_ui.min_rect(), Sense::hover()); + InnerResponse::new(ret, response) + } +} type UiBuilderFn = Box; type FooterUiBuilderFn = Box; @@ -55,8 +111,11 @@ impl SelectionPanel { current_value: &mut Value, ) -> Response { let selected = *current_value == self.value; - let selected_bg = Color32::from_rgb(67, 76, 84); - let hover_stroke_color = Color32::WHITE; + let visuals = ui.visuals(); + // let selected_bg = Color32::from_rgb(67, 76, 84); + // let hover_stroke_color = Color32::WHITE; + let selected_bg = visuals.selection.bg_fill; + let hover_stroke = visuals.window_stroke;//Color32::WHITE; // let mut rect = ui.cursor(); // rect.set_width(width); @@ -97,9 +156,9 @@ impl SelectionPanel { } else { egui_phosphor::bold::DOT_OUTLINE }; - ui.label(RichText::new(icon).heading().color(Color32::WHITE)); + ui.label(RichText::new(icon).heading()); if let Some(build) = self.build_footer { - ui.visuals_mut().override_text_color = Some(Color32::WHITE); + //ui.visuals_mut().override_text_color = Some(Color32::WHITE); (build)(ui); } ui.label(" "); @@ -116,7 +175,7 @@ impl SelectionPanel { .inner_margin .expand_rect(prepared.content_ui.min_rect()); if ui.allocate_rect(rect, Sense::hover()).hovered() { - prepared.frame = prepared.frame.stroke(Stroke::new(1.0, hover_stroke_color)) + prepared.frame = prepared.frame.stroke(hover_stroke) } let res = prepared.end(ui); @@ -188,45 +247,66 @@ impl SelectionPanels { } pub fn render(self, ui: &mut Ui, current_value: &mut Value) -> Response { - let frame_bg = Color32::from_rgb(17, 19, 24); - let text_color = Color32::WHITE; - let even_panel_bg = Color32::from_rgb(44, 50, 59); - let odd_panel_bg = Color32::from_rgb(54, 63, 71); + let visuals = ui.visuals(); + //let frame_bg = visuals.extreme_bg_color;//Color32::from_rgb(17, 19, 24); + let text_color = visuals.text_color(); + let even_panel_bg = visuals.extreme_bg_color;//Color32::from_rgb(44, 50, 59); + let odd_panel_bg = visuals.code_bg_color;//Color32::from_rgb(54, 63, 71); - let frame = Frame::none() - .fill(frame_bg) - .rounding(egui::Rounding::same(10.0)) - .stroke(Stroke::NONE); + // let frame = Frame::none() + // .fill(frame_bg) + // .rounding(egui::Rounding::same(if visuals.widgets.hovered.rounding == Rounding::ZERO{0.0}else{10.0})); + + // let mut prepared = frame.begin(ui); + + let before_wrap_width = ui.available_rect_before_wrap().width(); + let panel_width = self.panel_min_width.max( + self.panel_max_width + .min(before_wrap_width / self.panels.len() as f32), + ); + let vertical = self.vertical || (before_wrap_width < (panel_width + 2.0) * 3.0); + let panels_width = if vertical{ + panel_width + }else{ + let mut width = 0.0; + let mut available_width = ui.available_rect_before_wrap().width(); + for _ in 0..self.panels.len() { + if (available_width - 2.0) < panel_width { + break; + } + available_width -= panel_width; + width += panel_width; + } + width + }; + + let indent = (before_wrap_width - panels_width) / 2.0; - let mut prepared = frame.begin(ui); let add_contents = |ui: &mut Ui| { let mut responce = ui.label(" "); //ui.visuals_mut().override_text_color = Some(Color32::WHITE); //ui.button(text) { + let available_width = ui.available_width() - indent; let title = self.title.into_galley( ui, Some(true), - ui.available_width(), + available_width, TextStyle::Heading, ); - let rect = ui.cursor().translate(Vec2::splat(10.0)); + let text_indent = (available_width - title.size().x)/2.0; + let rect = ui.cursor().translate(Vec2::new(text_indent, 10.0)); ui.allocate_exact_size( - title.size() + Vec2::splat(10.0), + title.size() + Vec2::new(text_indent, 10.0), Sense::focusable_noninteractive(), ); title.paint_with_fallback_color(ui.painter(), rect.min, text_color); } + - let before_wrap_width = ui.available_rect_before_wrap().width(); - let panel_width = self.panel_min_width.max( - self.panel_max_width - .min(before_wrap_width / self.panels.len() as f32), - ); - let vertical = self.vertical || (before_wrap_width - 2. < panel_width * 3.0); // ui.label(format!("before_wrap_width: {before_wrap_width}")); // ui.label(format!("panel_width: {panel_width}")); - let panels_res = ui.horizontal_wrapped(|ui| { + let _panels_res = ui.horizontal_wrapped(|ui| { ui.spacing_mut().item_spacing = Vec2::ZERO; let mut min_height = self.panel_min_height; for (index, panel) in self.panels.into_iter().enumerate() { @@ -244,22 +324,32 @@ impl SelectionPanels { } }); - let total_width = panels_res.response.rect.width(); - ui.allocate_ui_with_layout( - egui::vec2(total_width, ui.available_height()), - Layout::top_down(Align::Center), - |ui| (self.build_footer)(ui, current_value), - ); - ui.label(" "); - // ui.label(format!(" vertical: {vertical}")); - // ui.label(format!("panels width {}", total_width)); + // let total_width = panels_res.response.rect.width(); + // ui.allocate_ui_with_layout( + // egui::vec2(total_width, ui.available_height()), + // Layout::top_down(Align::Center), + // |ui| { + // ui.set_width(total_width); + // (self.build_footer)(ui, current_value) + // } + // ); + // ui.label(format!("bottom width {}", b.response.rect.width())); // ui.label(format!("ui.min_rect().width() {}", ui.min_rect().width())); responce }; - let res = add_contents(&mut prepared.content_ui); - //prepared.frame = prepared.frame.fill(Color32::GREEN); - res | prepared.end(ui) + // let res = add_contents(&mut prepared.content_ui); + // crate::imports::log_info!("min_width {min_width}, min: {}", prepared.content_ui.min_size().x); + // res | prepared.end(ui) + + let mut response = ui.indent_with_size("selection-panels", indent, Box::new(add_contents)).response; + response |= ui.vertical_centered(|ui|{ + (self.build_footer)(ui, current_value) + }).response; + ui.label(" "); + // ui.label(format!(" vertical: {vertical}")); + // ui.label(format!("panels_width {}", panels_width)); + response } } diff --git a/core/src/modules/account_manager/estimator.rs b/core/src/modules/account_manager/estimator.rs index fda8113..8f5872e 100644 --- a/core/src/modules/account_manager/estimator.rs +++ b/core/src/modules/account_manager/estimator.rs @@ -69,6 +69,54 @@ impl<'context> Estimator<'context> { } ui.add_space(8.); + + if core.settings.developer.enable{ + //let child_ui = ui.child_ui(max_rect, Layout::top_down(Align::Center)); + let fee_selection = SelectionPanels::new( + 150.0, + 180.0, + i18n("Miner Fee"), + |ui, value|{ + ui.label("1 in / 2 outputs, ~1.2 Kg"); + ui.label(format!("Fee Mode: {:?}", value)); + }) + //.panel_min_height(300.) + //.vertical(true) + //.add(FeeMode::LowPriority, i18n("Low-priority"), i18n("3 hours or more")) + .add_with_footer(FeeMode::LowPriority, i18n("Low-priority"), i18n("3 hours or more"), |ui|{ + ui.label("12.88716 µKAS"); + ui.label(RichText::new("~0.00000215 USD").strong()); + ui.label("9 SOMPI/G"); + }) + .add_with_footer(FeeMode::Economic, i18n("Economic"), i18n("~2 hours"), |ui|{ + ui.label("15.83525 µKAS"); + ui.label(RichText::new("~0.00000264 USD").strong()); + ui.label("10 SOMPI/G"); + }) + .add_with_footer(FeeMode::Normal, i18n("Normal"), i18n("~30 minutes"), |ui|{ + ui.label("20.78334 µKAS"); + ui.label(RichText::new("~0.00000347 USD").strong()); + ui.label("10 SOMPI/G"); + }); + // .add_with_footer(FeeMode::Economic, i18n("Economic"), i18n("~2 hours"), |ui|{ + // ui.label("13.83525 µKAS"); + // ui.label(RichText::new("~608.83 USD").strong()); + // ui.label("10 SOMPI/G"); + // }) + // .add_with_footer(FeeMode::Normal, i18n("Normal"), i18n("~30 minutes"), |ui|{ + // ui.label("14.78334 µKAS"); + // ui.label(RichText::new("~650.56 USD").strong()); + // ui.label("10 SOMPI/G"); + // }); + + if fee_selection.render(ui, &mut self.context.fee_mode).clicked(){ + log_info!("clicked: self.fee_mode: {:?}", self.context.fee_mode); + runtime().toast(UserNotification::success(format!("selection: {:?}", self.context.fee_mode)).short()) + } + ui.add_space(8.); + } + + if ui .checkbox(&mut self.context.enable_priority_fees,i18n("Include QoS Priority Fees")) // .on_hover_text_at_pointer(i18n("Add priority fees to ensure faster confirmation.\nUseful only if the network is congested.")) diff --git a/core/src/modules/account_manager/mod.rs b/core/src/modules/account_manager/mod.rs index ce63242..952a6db 100644 --- a/core/src/modules/account_manager/mod.rs +++ b/core/src/modules/account_manager/mod.rs @@ -111,6 +111,15 @@ enum AddressStatus { Invalid(String), } +#[derive(PartialEq, Debug, Default)] +pub enum FeeMode{ + #[default] + None, + LowPriority, + Economic, + Normal, +} + #[derive(Default)] pub struct ManagerContext { transfer_to_account : Option, @@ -129,6 +138,7 @@ pub struct ManagerContext { wallet_secret : String, payment_secret : String, loading : bool, + fee_mode : FeeMode } impl ManagerContext { From 3704b77040fdc6a43a8219961177dfcfffdef878 Mon Sep 17 00:00:00 2001 From: Surinder Singh Matoo Date: Sun, 11 Aug 2024 03:04:28 +0530 Subject: [PATCH 4/6] fmt --- core/src/egui/selection_panels.rs | 44 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/core/src/egui/selection_panels.rs b/core/src/egui/selection_panels.rs index 8aa7a19..ec8f96c 100644 --- a/core/src/egui/selection_panels.rs +++ b/core/src/egui/selection_panels.rs @@ -1,7 +1,7 @@ use egui::*; use std::hash::Hash; -trait UILayoutExt{ +trait UILayoutExt { //fn layout_with_max_rect(&mut self, max_rect:Rect, layout:Layout, add_contents: impl FnOnce(&mut Ui) -> R)->InnerResponse; fn indent_with_size<'c, R>( &mut self, @@ -11,7 +11,7 @@ trait UILayoutExt{ ) -> InnerResponse; } -impl UILayoutExt for Ui{ +impl UILayoutExt for Ui { // fn layout_with_max_rect(&mut self, max_rect:Rect, layout:Layout, add_contents: impl FnOnce(&mut Ui) -> R)->InnerResponse { // let mut child_ui = self.child_ui(max_rect, layout); // let inner = add_contents(&mut child_ui); @@ -115,9 +115,9 @@ impl SelectionPanel { // let selected_bg = Color32::from_rgb(67, 76, 84); // let hover_stroke_color = Color32::WHITE; let selected_bg = visuals.selection.bg_fill; - let hover_stroke = visuals.window_stroke;//Color32::WHITE; - // let mut rect = ui.cursor(); - // rect.set_width(width); + let hover_stroke = visuals.window_stroke; //Color32::WHITE; + // let mut rect = ui.cursor(); + // rect.set_width(width); // ui.painter().rect( // rect, @@ -250,8 +250,8 @@ impl SelectionPanels { let visuals = ui.visuals(); //let frame_bg = visuals.extreme_bg_color;//Color32::from_rgb(17, 19, 24); let text_color = visuals.text_color(); - let even_panel_bg = visuals.extreme_bg_color;//Color32::from_rgb(44, 50, 59); - let odd_panel_bg = visuals.code_bg_color;//Color32::from_rgb(54, 63, 71); + let even_panel_bg = visuals.extreme_bg_color; //Color32::from_rgb(44, 50, 59); + let odd_panel_bg = visuals.code_bg_color; //Color32::from_rgb(54, 63, 71); // let frame = Frame::none() // .fill(frame_bg) @@ -265,9 +265,9 @@ impl SelectionPanels { .min(before_wrap_width / self.panels.len() as f32), ); let vertical = self.vertical || (before_wrap_width < (panel_width + 2.0) * 3.0); - let panels_width = if vertical{ + let panels_width = if vertical { panel_width - }else{ + } else { let mut width = 0.0; let mut available_width = ui.available_rect_before_wrap().width(); for _ in 0..self.panels.len() { @@ -288,13 +288,10 @@ impl SelectionPanels { //ui.button(text) { let available_width = ui.available_width() - indent; - let title = self.title.into_galley( - ui, - Some(true), - available_width, - TextStyle::Heading, - ); - let text_indent = (available_width - title.size().x)/2.0; + let title = + self.title + .into_galley(ui, Some(true), available_width, TextStyle::Heading); + let text_indent = (available_width - title.size().x) / 2.0; let rect = ui.cursor().translate(Vec2::new(text_indent, 10.0)); ui.allocate_exact_size( title.size() + Vec2::new(text_indent, 10.0), @@ -302,7 +299,6 @@ impl SelectionPanels { ); title.paint_with_fallback_color(ui.painter(), rect.min, text_color); } - // ui.label(format!("before_wrap_width: {before_wrap_width}")); // ui.label(format!("panel_width: {panel_width}")); @@ -333,7 +329,7 @@ impl SelectionPanels { // (self.build_footer)(ui, current_value) // } // ); - + // ui.label(format!("bottom width {}", b.response.rect.width())); // ui.label(format!("ui.min_rect().width() {}", ui.min_rect().width())); responce @@ -342,11 +338,13 @@ impl SelectionPanels { // let res = add_contents(&mut prepared.content_ui); // crate::imports::log_info!("min_width {min_width}, min: {}", prepared.content_ui.min_size().x); // res | prepared.end(ui) - - let mut response = ui.indent_with_size("selection-panels", indent, Box::new(add_contents)).response; - response |= ui.vertical_centered(|ui|{ - (self.build_footer)(ui, current_value) - }).response; + + let mut response = ui + .indent_with_size("selection-panels", indent, Box::new(add_contents)) + .response; + response |= ui + .vertical_centered(|ui| (self.build_footer)(ui, current_value)) + .response; ui.label(" "); // ui.label(format!(" vertical: {vertical}")); // ui.label(format!("panels_width {}", panels_width)); From 3afc234f9a7ec56a7a62e0298e2aa9404d7da4e4 Mon Sep 17 00:00:00 2001 From: Surinder Singh Matoo Date: Thu, 15 Aug 2024 02:24:22 +0530 Subject: [PATCH 5/6] Update i18n.json --- core/resources/i18n/i18n.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/resources/i18n/i18n.json b/core/resources/i18n/i18n.json index 0b16b11..1fca341 100644 --- a/core/resources/i18n/i18n.json +++ b/core/resources/i18n/i18n.json @@ -3178,6 +3178,7 @@ "Developer mode enables advanced and experimental features": "Developer mode enables advanced and experimental features", "Difficulty": "Difficulty", "Dimensions": "Dimensions", + "Disable Window Frame": "Disable Window Frame", "Disable password safety rules": "Disable password safety rules", "Disabled": "Disabled", "Disables node connectivity (Offline Mode).": "Disables node connectivity (Offline Mode).", @@ -3308,6 +3309,7 @@ "Open Data Folder": "Open Data Folder", "Opening wallet:": "Opening wallet:", "Optional": "Optional", + "Options": "Options", "Other operations": "Other operations", "Outbound": "Outbound", "Overview": "Overview", From f44372586140c32784ac43cd5795eb7385cba83a Mon Sep 17 00:00:00 2001 From: Surinder Singh Matoo Date: Thu, 15 Aug 2024 05:48:35 +0530 Subject: [PATCH 6/6] fee ux style changes --- core/src/egui/selection_panels.rs | 94 +++++++++---------- core/src/modules/account_manager/estimator.rs | 2 +- core/src/modules/testing.rs | 65 ++++++++++++- 3 files changed, 111 insertions(+), 50 deletions(-) diff --git a/core/src/egui/selection_panels.rs b/core/src/egui/selection_panels.rs index ec8f96c..56c597d 100644 --- a/core/src/egui/selection_panels.rs +++ b/core/src/egui/selection_panels.rs @@ -112,32 +112,11 @@ impl SelectionPanel { ) -> Response { let selected = *current_value == self.value; let visuals = ui.visuals(); - // let selected_bg = Color32::from_rgb(67, 76, 84); - // let hover_stroke_color = Color32::WHITE; let selected_bg = visuals.selection.bg_fill; - let hover_stroke = visuals.window_stroke; //Color32::WHITE; - // let mut rect = ui.cursor(); - // rect.set_width(width); - - // ui.painter().rect( - // rect, - // Rounding::ZERO, - // Color32::GRAY, - // Stroke::new(1.0, Color32::GREEN), - // ); - - // let res = ui.layout_with_max_rect(rect, Layout::top_down(Align::Center), |ui|{ - // ui.label(self.title); - // ui.label(self.sub); - // if let Some(build) = self.build_heading{ - // (build)(ui); - // } - // ui.checkbox(&mut selected, ""); - // if let Some(build) = self.build_footer{ - // (build)(ui); - // } - // }).response; - let frame = Frame::none().fill(if selected { selected_bg } else { bg_color }); + let hover_stroke = Stroke::new(1.0, visuals.text_color()); //visuals.window_stroke; + let frame = Frame::none() + .stroke(Stroke::new(1.0, Color32::TRANSPARENT)) + .fill(if selected { selected_bg } else { bg_color }); let mut prepared = frame.begin(ui); let add_contents = |ui: &mut Ui| { @@ -174,8 +153,9 @@ impl SelectionPanel { .frame .inner_margin .expand_rect(prepared.content_ui.min_rect()); - if ui.allocate_rect(rect, Sense::hover()).hovered() { - prepared.frame = prepared.frame.stroke(hover_stroke) + if !selected && ui.allocate_rect(rect, Sense::hover()).hovered() { + //prepared.frame = prepared.frame.stroke(hover_stroke); + prepared.frame = prepared.frame.fill(selected_bg).stroke(hover_stroke); } let res = prepared.end(ui); @@ -197,6 +177,7 @@ pub struct SelectionPanels { pub build_footer: FooterUiBuilderFn, pub panel_min_height: f32, pub vertical: bool, + pub sep_ratio: f32, } impl SelectionPanels { @@ -214,6 +195,7 @@ impl SelectionPanels { panels: vec![], panel_min_height: 0., vertical: false, + sep_ratio: 1.0, } } pub fn add( @@ -245,27 +227,26 @@ impl SelectionPanels { self.vertical = vertical; self } + pub fn sep_ratio(mut self, sep_ratio: f32) -> Self { + self.sep_ratio = sep_ratio; + self + } pub fn render(self, ui: &mut Ui, current_value: &mut Value) -> Response { let visuals = ui.visuals(); - //let frame_bg = visuals.extreme_bg_color;//Color32::from_rgb(17, 19, 24); + let sep_ratio = self.sep_ratio; let text_color = visuals.text_color(); - let even_panel_bg = visuals.extreme_bg_color; //Color32::from_rgb(44, 50, 59); - let odd_panel_bg = visuals.code_bg_color; //Color32::from_rgb(54, 63, 71); - - // let frame = Frame::none() - // .fill(frame_bg) - // .rounding(egui::Rounding::same(if visuals.widgets.hovered.rounding == Rounding::ZERO{0.0}else{10.0})); - - // let mut prepared = frame.begin(ui); - + let bg_color = visuals.code_bg_color; let before_wrap_width = ui.available_rect_before_wrap().width(); - let panel_width = self.panel_min_width.max( + let mut panel_width = self.panel_min_width.max( self.panel_max_width .min(before_wrap_width / self.panels.len() as f32), ); let vertical = self.vertical || (before_wrap_width < (panel_width + 2.0) * 3.0); let panels_width = if vertical { + panel_width = self + .panel_min_width + .max(self.panel_max_width.min(before_wrap_width - 10.0)); panel_width } else { let mut width = 0.0; @@ -285,7 +266,6 @@ impl SelectionPanels { let add_contents = |ui: &mut Ui| { let mut responce = ui.label(" "); //ui.visuals_mut().override_text_color = Some(Color32::WHITE); - //ui.button(text) { let available_width = ui.available_width() - indent; let title = @@ -305,16 +285,38 @@ impl SelectionPanels { let _panels_res = ui.horizontal_wrapped(|ui| { ui.spacing_mut().item_spacing = Vec2::ZERO; let mut min_height = self.panel_min_height; + let mut first_row = true; for (index, panel) in self.panels.into_iter().enumerate() { let rect = ui.available_rect_before_wrap(); + let mut row_first_item = index == 0; if (index > 0 && vertical) || rect.width() - 2.0 < panel_width { ui.end_row(); + row_first_item = true; + first_row = false; + } + // left separator + if !row_first_item { + let Pos2 { x, y } = ui.cursor().min; + let height = min_height * sep_ratio; + let m = (min_height - height) / 2.0; + ui.painter().vline( + x, + (y + m)..=(y + m + height), + Stroke::new(1.0, text_color), + ); + } + + // top seperator + if !first_row { + let Pos2 { x, y } = ui.cursor().min; + let width = panel_width * sep_ratio; + let m = (panel_width - width) / 2.0; + ui.painter().hline( + (x + m)..=(x + m + width), + y, + Stroke::new(1.0, text_color), + ); } - let bg_color = if index % 2 == 0 { - even_panel_bg - } else { - odd_panel_bg - }; responce |= panel.render(ui, bg_color, panel_width, &mut min_height, current_value); } @@ -335,10 +337,6 @@ impl SelectionPanels { responce }; - // let res = add_contents(&mut prepared.content_ui); - // crate::imports::log_info!("min_width {min_width}, min: {}", prepared.content_ui.min_size().x); - // res | prepared.end(ui) - let mut response = ui .indent_with_size("selection-panels", indent, Box::new(add_contents)) .response; diff --git a/core/src/modules/account_manager/estimator.rs b/core/src/modules/account_manager/estimator.rs index 8f5872e..fbd7a4a 100644 --- a/core/src/modules/account_manager/estimator.rs +++ b/core/src/modules/account_manager/estimator.rs @@ -73,8 +73,8 @@ impl<'context> Estimator<'context> { if core.settings.developer.enable{ //let child_ui = ui.child_ui(max_rect, Layout::top_down(Align::Center)); let fee_selection = SelectionPanels::new( + 120.0, 150.0, - 180.0, i18n("Miner Fee"), |ui, value|{ ui.label("1 in / 2 outputs, ~1.2 Kg"); diff --git a/core/src/modules/testing.rs b/core/src/modules/testing.rs index bd50da9..1ffdad1 100644 --- a/core/src/modules/testing.rs +++ b/core/src/modules/testing.rs @@ -108,7 +108,7 @@ impl ModuleT for Testing { let fee_selection = SelectionPanels::new( 100.0, - 150.0, + 130.0, i18n("Miner Fee"), |ui, value|{ ui.label("1 in / 2 outputs, ~1.2 Kg"); @@ -127,6 +127,21 @@ impl ModuleT for Testing { ui.label(RichText::new("~0.00000264 USD").strong()); ui.label("10 SOMPI/G"); }) + .add_with_footer(FeeMode::Normal, i18n("Normal"), i18n("~30 minutes"), |ui|{ + ui.label("20.78334 µKAS"); + ui.label(RichText::new("~0.00000347 USD").strong()); + ui.label("10 SOMPI/G"); + }) + .add_with_footer(FeeMode::LowPriority, i18n("Low-priority"), i18n("3 hours or more"), |ui|{ + ui.label("12.88716 µKAS"); + ui.label(RichText::new("~0.00000215 USD").strong()); + ui.label("9 SOMPI/G"); + }) + .add_with_footer(FeeMode::Economic, i18n("Economic"), i18n("~2 hours"), |ui|{ + ui.label("15.83525 µKAS"); + ui.label(RichText::new("~0.00000264 USD").strong()); + ui.label("10 SOMPI/G"); + }) .add_with_footer(FeeMode::Normal, i18n("Normal"), i18n("~30 minutes"), |ui|{ ui.label("20.78334 µKAS"); ui.label(RichText::new("~0.00000347 USD").strong()); @@ -143,10 +158,58 @@ impl ModuleT for Testing { // ui.label("10 SOMPI/G"); // }); + if fee_selection.render(ui, &mut self.fee_mode).clicked(){ log_info!("clicked: self.fee_mode: {:?}", self.fee_mode); runtime().toast(UserNotification::success(format!("selection: {:?}", self.fee_mode)).short()) } + + let fee_selection = SelectionPanels::new( + 100.0, + 150.0, + i18n("Miner Fee"), + |ui, value|{ + ui.label("1 in / 2 outputs, ~1.2 Kg"); + ui.label(format!("Fee Mode: {:?}", value)); + }) + //.panel_min_height(300.) + //.vertical(true) + //.add(FeeMode::LowPriority, i18n("Low-priority"), i18n("3 hours or more")) + .add_with_footer(FeeMode::LowPriority, i18n("Low-priority"), i18n("3 hours or more"), |ui|{ + ui.label("12.88716 µKAS"); + ui.label(RichText::new("~0.00000215 USD").strong()); + ui.label("9 SOMPI/G"); + }) + .add_with_footer(FeeMode::Economic, i18n("Economic"), i18n("~2 hours"), |ui|{ + ui.label("15.83525 µKAS"); + ui.label(RichText::new("~0.00000264 USD").strong()); + ui.label("10 SOMPI/G"); + }) + .add_with_footer(FeeMode::Normal, i18n("Normal"), i18n("~30 minutes"), |ui|{ + ui.label("20.78334 µKAS"); + ui.label(RichText::new("~0.00000347 USD").strong()); + ui.label("10 SOMPI/G"); + }) + .add_with_footer(FeeMode::LowPriority, i18n("Low-priority"), i18n("3 hours or more"), |ui|{ + ui.label("12.88716 µKAS"); + ui.label(RichText::new("~0.00000215 USD").strong()); + ui.label("9 SOMPI/G"); + }) + .add_with_footer(FeeMode::Economic, i18n("Economic"), i18n("~2 hours"), |ui|{ + ui.label("15.83525 µKAS"); + ui.label(RichText::new("~0.00000264 USD").strong()); + ui.label("10 SOMPI/G"); + }) + .add_with_footer(FeeMode::Normal, i18n("Normal"), i18n("~30 minutes"), |ui|{ + ui.label("20.78334 µKAS"); + ui.label(RichText::new("~0.00000347 USD").strong()); + ui.label("10 SOMPI/G"); + }); + + if fee_selection.sep_ratio(0.7).render(ui, &mut self.fee_mode).clicked(){ + log_info!("clicked: self.fee_mode: {:?}", self.fee_mode); + runtime().toast(UserNotification::success(format!("selection: {:?}", self.fee_mode)).short()) + } } }