ui: hidden scrollbar, wallet sync indicator

This commit is contained in:
ardocrat 2024-05-17 21:37:29 +03:00
parent f118ad7d07
commit 098d0a9611
16 changed files with 129 additions and 77 deletions

View file

@ -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<AtomicBool>
stop_camera: Arc<AtomicBool>,
}
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);

View file

@ -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);

View file

@ -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(

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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);
});
});
});

View file

@ -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<T: PartialEq>(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.

View file

@ -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| {

View file

@ -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| {

View file

@ -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);

View file

@ -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);

View file

@ -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| {

View file

@ -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| {

View file

@ -46,7 +46,10 @@ pub struct WalletTransactions {
tx_info_finalize: bool,
/// Transaction identifier to use at confirmation[`Modal`].
confirm_cancel_tx_id: Option<u32>
confirm_cancel_tx_id: Option<u32>,
/// 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);

View file

@ -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);
}