diff --git a/src/gui/platform/desktop/mod.rs b/src/gui/platform/desktop/mod.rs index becab93..20968dd 100644 --- a/src/gui/platform/desktop/mod.rs +++ b/src/gui/platform/desktop/mod.rs @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +use eye::hal::traits::{Context, Device, Stream}; +use eye::hal::PlatformContext; use lazy_static::lazy_static; -use std::sync::Arc; use parking_lot::RwLock; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; +use std::sync::Arc; use std::thread; -use eye::hal::PlatformContext; -use eye::hal::traits::{Context, Device, Stream}; use crate::gui::platform::PlatformCallbacks; @@ -27,14 +27,14 @@ pub struct Desktop { /// Camera index. camera_index: AtomicI32, /// Flag to check if camera stop is needed. - stop_camera: Arc + stop_camera: Arc, } impl Default for Desktop { fn default() -> Self { Self { camera_index: AtomicI32::new(0), - stop_camera: Arc::new(AtomicBool::new(false)) + stop_camera: Arc::new(AtomicBool::new(false)), } } } @@ -61,8 +61,19 @@ impl PlatformCallbacks for Desktop { *w_image = None; } + // Setup stop camera flag. + let stop_camera = self.stop_camera.clone(); + stop_camera.store(false, Ordering::Relaxed); + + // Create a context + let ctx = if let Some(ctx) = PlatformContext::all().next() { + ctx + } else { + PlatformContext::default() + }; + // Query for available devices. - let devices = PlatformContext::default().devices(); + let devices = ctx.devices(); if devices.is_err() { return; } @@ -77,12 +88,6 @@ impl PlatformCallbacks for Desktop { saved_index }; - // Setup stop camera flag. - let stop_camera = self.stop_camera.clone(); - stop_camera.store(false, Ordering::Relaxed); - - let devices = devices.clone(); - // Capture images at separate thread. thread::spawn(move || { tokio::runtime::Builder::new_multi_thread() @@ -91,8 +96,7 @@ impl PlatformCallbacks for Desktop { .unwrap() .block_on(async { // Open camera. - let context = PlatformContext::default(); - if let Ok(dev) = context.open_device(&devices[camera_index as usize].uri) { + if let Ok(dev) = ctx.open_device(&devices[camera_index as usize].uri) { let streams = dev.streams().unwrap(); let stream_desc = streams[0].clone(); println!("Camera stream: {:?}", stream_desc); diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 2e4c095..f065cd0 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -13,6 +13,7 @@ // limitations under the License. use egui::{Margin, RichText, ScrollArea, Stroke}; +use egui::scroll_area::ScrollBarVisibility; use crate::AppConfig; use crate::gui::Colors; @@ -130,6 +131,7 @@ impl NetworkContent { } ScrollArea::vertical() .id_source("connections_content") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(1.0); diff --git a/src/gui/views/network/metrics.rs b/src/gui/views/network/metrics.rs index 1d3ed29..3bb37f2 100644 --- a/src/gui/views/network/metrics.rs +++ b/src/gui/views/network/metrics.rs @@ -13,6 +13,7 @@ // limitations under the License. use egui::{RichText, Rounding, ScrollArea, vec2}; +use egui::scroll_area::ScrollBarVisibility; use grin_servers::{DiffBlock, ServerStats}; use crate::gui::Colors; @@ -141,6 +142,7 @@ fn blocks_ui(ui: &mut egui::Ui, stats: &ServerStats) { ui.add_space(4.0); ScrollArea::vertical() .id_source("difficulty_scroll") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .stick_to_bottom(true) .show_rows( diff --git a/src/gui/views/network/mining.rs b/src/gui/views/network/mining.rs index dd6f1db..36f884f 100644 --- a/src/gui/views/network/mining.rs +++ b/src/gui/views/network/mining.rs @@ -13,6 +13,7 @@ // limitations under the License. use egui::{RichText, Rounding, ScrollArea}; +use egui::scroll_area::ScrollBarVisibility; use grin_chain::SyncStatus; use grin_servers::WorkerStats; @@ -76,6 +77,7 @@ impl NetworkTab for NetworkMining { if !stratum_stats.is_running { ScrollArea::vertical() .id_source("stratum_setup_scroll") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(1.0); @@ -176,8 +178,9 @@ impl NetworkTab for NetworkMining { View::horizontal_line(ui, Colors::ITEM_STROKE); ui.add_space(4.0); ScrollArea::vertical() - .auto_shrink([false; 2]) .id_source("stratum_workers_scroll") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .auto_shrink([false; 2]) .show_rows( ui, WORKER_ITEM_HEIGHT, diff --git a/src/gui/views/network/node.rs b/src/gui/views/network/node.rs index 3809d71..325cbc3 100644 --- a/src/gui/views/network/node.rs +++ b/src/gui/views/network/node.rs @@ -13,6 +13,7 @@ // limitations under the License. use egui::{RichText, Rounding, ScrollArea}; +use egui::scroll_area::ScrollBarVisibility; use grin_servers::PeerStats; use crate::gui::Colors; @@ -54,6 +55,7 @@ impl NetworkTab for NetworkNode { ScrollArea::vertical() .id_source("integrated_node") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(2.0); diff --git a/src/gui/views/network/settings.rs b/src/gui/views/network/settings.rs index 01cd485..ff6ca36 100644 --- a/src/gui/views/network/settings.rs +++ b/src/gui/views/network/settings.rs @@ -13,6 +13,7 @@ // limitations under the License. use egui::{RichText, ScrollArea}; +use egui::scroll_area::ScrollBarVisibility; use crate::gui::Colors; use crate::gui::icons::ARROW_COUNTER_CLOCKWISE; @@ -86,6 +87,7 @@ impl NetworkTab for NetworkSettings { ScrollArea::vertical() .id_source("network_settings") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(1.0); diff --git a/src/gui/views/title_panel.rs b/src/gui/views/title_panel.rs index a9cfee5..9bc47a8 100644 --- a/src/gui/views/title_panel.rs +++ b/src/gui/views/title_panel.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Margin, Color32, Id, lerp, Rgba}; +use egui::{Margin, Id}; use egui_extras::{Size, Strip, StripBuilder}; use crate::gui::Colors; @@ -154,23 +154,7 @@ impl TitlePanel { }); strip.cell(|ui| { ui.centered_and_justified(|ui| { - // Setup text color animation if needed. - let (dark, bright) = (0.3, 1.0); - let color_factor = if animate_sub { - lerp(dark..=bright, ui.input(|i| i.time).cos().abs()) as f32 - } else { - bright as f32 - }; - - // Draw subtitle text. - let sub_color_rgba = Rgba::from(Colors::TEXT) * color_factor; - let sub_color = Color32::from(sub_color_rgba); - View::ellipsize_text(ui, subtitle, 15.0, sub_color); - - // Repaint delay based on animation status. - if animate_sub { - ui.ctx().request_repaint(); - } + View::animate_text(ui, subtitle, 15.0, Colors::TEXT, animate_sub); }); }); }); diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 51bd21d..e868b2f 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -17,7 +17,7 @@ use std::sync::Arc; use parking_lot::RwLock; use lazy_static::lazy_static; -use egui::{Align, Button, CursorIcon, Layout, PointerState, Rect, Response, RichText, Sense, Spinner, TextBuffer, TextStyle, Widget}; +use egui::{Align, Button, CursorIcon, Layout, lerp, PointerState, Rect, Response, Rgba, RichText, Sense, Spinner, TextBuffer, TextStyle, Widget}; use egui::epaint::{Color32, FontId, RectShape, Rounding, Stroke}; use egui::epaint::text::TextWrapping; use egui::os::OperatingSystem; @@ -116,11 +116,32 @@ impl View { job } - /// Show ellipsized text. + /// Draw ellipsized text. pub fn ellipsize_text(ui: &mut egui::Ui, text: String, size: f32, color: Color32) { ui.label(Self::ellipsize(text, size, color)); } + /// Draw animated ellipsized text. + pub fn animate_text(ui: &mut egui::Ui, text: String, size: f32, color: Color32, animate: bool) { + // Setup text color animation if needed. + let (dark, bright) = (0.3, 1.0); + let color_factor = if animate { + lerp(dark..=bright, ui.input(|i| i.time).cos().abs()) as f32 + } else { + bright as f32 + }; + + // Draw subtitle text. + let sub_color_rgba = Rgba::from(color) * color_factor; + let sub_color = Color32::from(sub_color_rgba); + View::ellipsize_text(ui, text, size, sub_color); + + // Repaint delay based on animation status. + if animate { + ui.ctx().request_repaint(); + } + } + /// Draw horizontally centered sub-title with space below. pub fn sub_title(ui: &mut egui::Ui, text: String) { ui.vertical_centered_justified(|ui| { @@ -555,12 +576,17 @@ impl View { /// Show a [`RadioButton`]. It is selected if `*current_value == selected_value`. /// If clicked, `selected_value` is assigned to `*current_value`. pub fn radio_value(ui: &mut egui::Ui, current: &mut T, value: T, text: String) { - let mut response = ui.radio(*current == value, text) - .on_hover_cursor(CursorIcon::PointingHand); - if Self::touched(ui, response.clone()) && *current != value { - *current = value; - response.mark_changed(); - } + ui.scope(|ui| { + // Setup background color. + ui.visuals_mut().widgets.inactive.bg_fill = Colors::FILL_DARK; + // Draw radio button. + let mut response = ui.radio(*current == value, text) + .on_hover_cursor(CursorIcon::PointingHand); + if Self::touched(ui, response.clone()) && *current != value { + *current = value; + response.mark_changed(); + } + }); } /// Draw horizontal line. diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 7fef942..6e88e1e 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -14,6 +14,7 @@ use std::time::Duration; use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea}; +use egui::scroll_area::ScrollBarVisibility; use crate::AppConfig; use crate::gui::Colors; @@ -343,6 +344,7 @@ impl WalletsContent { // Draw list of wallets. ScrollArea::vertical() .id_source("wallet_list") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { ui.vertical_centered(|ui| { diff --git a/src/gui/views/wallets/creation/creation.rs b/src/gui/views/wallets/creation/creation.rs index 3975306..00f0be3 100644 --- a/src/gui/views/wallets/creation/creation.rs +++ b/src/gui/views/wallets/creation/creation.rs @@ -13,6 +13,7 @@ // limitations under the License. use egui::{Id, Margin, RichText, ScrollArea, vec2}; +use egui::scroll_area::ScrollBarVisibility; use grin_util::ZeroingString; use crate::built_info; @@ -115,6 +116,7 @@ impl WalletCreation { }; ScrollArea::vertical() .id_source(id) + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { ui.vertical_centered(|ui| { diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index e388501..b3039d1 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -14,6 +14,7 @@ use std::time::Duration; use egui::{Align, Id, Layout, Margin, RichText, ScrollArea}; +use egui::scroll_area::ScrollBarVisibility; use grin_chain::SyncStatus; use grin_core::core::amount_to_hr_string; @@ -239,7 +240,7 @@ impl WalletContent { // Show confirmed height. let height_text = format!("{} {}", PACKAGE, data.info.last_confirmed_height); - ui.label(RichText::new(height_text).size(15.0).color(Colors::GRAY)); + View::animate_text(ui, height_text, 15.0, Colors::GRAY, wallet.syncing()); }) }); }); @@ -316,8 +317,9 @@ impl WalletContent { // Show list of accounts. let size = self.accounts.len(); ScrollArea::vertical() - .max_height(266.0) .id_source("account_list_modal_scroll") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .max_height(266.0) .auto_shrink([true; 2]) .show_rows(ui, ACCOUNT_ITEM_HEIGHT, size, |ui, row_range| { for index in row_range { @@ -374,8 +376,9 @@ impl WalletContent { View::horizontal_line(ui, Colors::ITEM_STROKE); ui.add_space(3.0); ScrollArea::vertical() - .max_height(128.0) .id_source(Id::from("qr_scan_result_input").with(wallet.get_config().id)) + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .max_height(128.0) .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(7.0); diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index 9a45a05..68e1cfa 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -112,7 +112,7 @@ impl WalletTab for WalletMessages { }) .show_inside(ui, |ui| { ScrollArea::vertical() - .scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible) + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .id_source(Id::from("wallet_messages").with(wallet.get_config().id)) .auto_shrink([false; 2]) .show(ui, |ui| { @@ -305,8 +305,9 @@ impl WalletMessages { "response_input" }).with(wallet.get_config().id); ScrollArea::vertical() - .max_height(128.0) .id_source(scroll_id) + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .max_height(128.0) .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(7.0); @@ -767,8 +768,9 @@ impl WalletMessages { Id::from("receive_request").with(wallet.get_config().id) }; ScrollArea::vertical() - .max_height(128.0) .id_source(scroll_id) + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .max_height(128.0) .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(7.0); diff --git a/src/gui/views/wallets/wallet/settings.rs b/src/gui/views/wallets/wallet/settings.rs index 23d79c9..cddf7d1 100644 --- a/src/gui/views/wallets/wallet/settings.rs +++ b/src/gui/views/wallets/wallet/settings.rs @@ -13,6 +13,7 @@ // limitations under the License. use egui::{Id, Margin, ScrollArea}; +use egui::scroll_area::ScrollBarVisibility; use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; @@ -77,6 +78,7 @@ impl WalletTab for WalletSettings { ScrollArea::vertical() .id_source(Id::from("wallet_settings_scroll").with(wallet.get_config().id)) .auto_shrink([false; 2]) + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .show(ui, |ui| { ui.vertical_centered(|ui| { View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { diff --git a/src/gui/views/wallets/wallet/transport.rs b/src/gui/views/wallets/wallet/transport.rs index 4d3191d..fd5cd5a 100644 --- a/src/gui/views/wallets/wallet/transport.rs +++ b/src/gui/views/wallets/wallet/transport.rs @@ -94,8 +94,8 @@ impl WalletTab for WalletTransport { }) .show_inside(ui, |ui| { ScrollArea::vertical() - .scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible) .id_source(Id::from("wallet_transport").with(wallet.get_config().id)) + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { ui.vertical_centered(|ui| { diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index c2812f1..c17cfdd 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -46,7 +46,10 @@ pub struct WalletTransactions { tx_info_finalize: bool, /// Transaction identifier to use at confirmation[`Modal`]. - confirm_cancel_tx_id: Option + confirm_cancel_tx_id: Option, + + /// Flag to check if sync of wallet was initiated manually. + manual_sync: bool } impl Default for WalletTransactions { @@ -59,6 +62,7 @@ impl Default for WalletTransactions { tx_info_finalize_error: false, tx_info_finalize: false, confirm_cancel_tx_id: None, + manual_sync: false, } } } @@ -118,17 +122,17 @@ impl WalletTransactions { wallet: &mut Wallet, data: &WalletData, cb: &dyn PlatformCallbacks) { - let amount_awaiting_conf = data.info.amount_awaiting_confirmation; - let amount_awaiting_fin = data.info.amount_awaiting_finalization; + let amount_conf = data.info.amount_awaiting_confirmation; + let amount_fin = data.info.amount_awaiting_finalization; let amount_locked = data.info.amount_locked; // Show transactions info. View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { // Show non-zero awaiting confirmation amount. - if amount_awaiting_conf != 0 { - let awaiting_conf = amount_to_hr_string(amount_awaiting_conf, true); - let rounding = if amount_awaiting_fin != 0 || amount_locked != 0 { + if amount_conf != 0 { + let awaiting_conf = amount_to_hr_string(amount_conf, true); + let rounding = if amount_fin != 0 || amount_locked != 0 { [false, false, false, false] } else { [false, false, true, true] @@ -140,8 +144,8 @@ impl WalletTransactions { } // Show non-zero awaiting finalization amount. - if amount_awaiting_fin != 0 { - let awaiting_conf = amount_to_hr_string(amount_awaiting_fin, true); + if amount_fin != 0 { + let awaiting_conf = amount_to_hr_string(amount_fin, true); let rounding = if amount_locked != 0 { [false, false, false, false] } else { @@ -177,32 +181,40 @@ impl WalletTransactions { } }); - // Show list of transactions. ui.add_space(4.0); - let refresh_resp = PullToRefresh::new(wallet.syncing()).scroll_area_ui(ui, |ui| { - ScrollArea::vertical() - .scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible) - .id_source(Id::from("txs_content").with(wallet.get_config().id)) - .auto_shrink([false; 2]) - .show_rows(ui, TX_ITEM_HEIGHT, data.txs.len(), |ui, row_range| { - ui.add_space(3.0); - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { - let extra_padding = amount_awaiting_conf != 0 || amount_awaiting_fin != 0 || - amount_locked != 0; - for index in row_range { - // Show transaction item. - let tx = data.txs.get(index).unwrap(); - let rounding = View::item_rounding(index, data.txs.len(), false); - self.tx_item_ui(ui, tx, rounding, extra_padding, true, &data, wallet, cb); - } - }); - }) - }); + // Show list of transactions. + let syncing = wallet.syncing(); + // Reset manual sync flag when wallet is not syncing. + if !syncing { + self.manual_sync = false; + } + let refresh_resp = PullToRefresh::new(syncing && self.manual_sync) + .min_refresh_distance(70.0) + .can_refresh(!wallet.syncing()) + .scroll_area_ui(ui, |ui| { + ScrollArea::vertical() + .id_source(Id::from("txs_content").with(wallet.get_config().id)) + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .auto_shrink([false; 2]) + .show_rows(ui, TX_ITEM_HEIGHT, data.txs.len(), |ui, row_range| { + ui.add_space(3.0); + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + let padding = amount_conf != 0 || amount_fin != 0 || amount_locked != 0; + for index in row_range { + // Show transaction item. + let tx = data.txs.get(index).unwrap(); + let rounding = View::item_rounding(index, data.txs.len(), false); + self.tx_item_ui(ui, tx, rounding, padding, true, &data, wallet, cb); + } + }); + }) + }); // Sync wallet on refresh. if refresh_resp.should_refresh() { wallet.sync(); + self.manual_sync = true; } } @@ -640,8 +652,9 @@ impl WalletTransactions { View::horizontal_line(ui, Colors::ITEM_STROKE); ui.add_space(3.0); ScrollArea::vertical() - .max_height(128.0) .id_source(input_id) + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .max_height(128.0) .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(7.0); diff --git a/src/lib.rs b/src/lib.rs index 2eb5772..b1a056d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,6 +142,9 @@ pub fn setup_visuals(ctx: &Context) { visuals.widgets.noninteractive.bg_stroke = Stroke::NONE; // Setup stroke around inactive widgets. visuals.widgets.inactive.bg_stroke = View::DEFAULT_STROKE; + // Setup background and foreground stroke color for widgets like pull-to-refresher. + visuals.widgets.inactive.bg_fill = Colors::YELLOW; + visuals.widgets.inactive.fg_stroke.color = Colors::ITEM_BUTTON; // Setup visuals ctx.set_visuals(visuals); }