Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update iced gui library 0.9 -> 0.10 #1232

Merged
merged 5 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,201 changes: 649 additions & 552 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,8 @@ hex-literal = "0.4"
hmac = "0.12"
http = "0.2"
hyper = "0.14"
iced = "0.9"
iced_aw = "0.5"
iced_lazy = "0.6"
iced = "0.10"
iced_aw = "0.7"
itertools = "0.11"
jsonrpsee = "0.17"
lazy_static = "1.4"
Expand Down
4 changes: 2 additions & 2 deletions node-gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ wallet-types = { path = "../wallet/types" }
anyhow.workspace = true
chrono.workspace = true
futures.workspace = true
iced = { workspace = true, features = ["canvas", "debug", "tokio"] }
iced = { workspace = true, features = ["canvas", "debug", "tokio", "lazy"] }
iced_aw = { workspace = true, features = ["cupertino", "icons"] }
iced_lazy = { workspace = true }
rfd = { workspace = true, features = ["gtk3"] } # revert to "xdg-portal" once https://github.com/PolyMeilex/rfd/issues/126 is fixed
thiserror.workspace = true
tokio.workspace = true
variant_count.workspace = true
20 changes: 12 additions & 8 deletions node-gui/src/backend/backend_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ pub struct Backend {
/// The bounded sender is used so that the UI is not overloaded with messages.
/// With an unbounded sender, high latency was experienced when wallet scan was enabled.
event_tx: UnboundedSender<BackendEvent>,
/// Low priority event_tx for sending wallet updates when new blocks are scanned
/// without this the queue can get filled up with updates when the wallet is far behind
/// and user events interacting with the wallet can start lagging
low_priority_event_tx: UnboundedSender<BackendEvent>,

wallet_updated_tx: UnboundedSender<WalletId>,

Expand All @@ -89,13 +93,15 @@ impl Backend {
pub fn new(
chain_config: Arc<ChainConfig>,
event_tx: UnboundedSender<BackendEvent>,
low_priority_event_tx: UnboundedSender<BackendEvent>,
wallet_updated_tx: UnboundedSender<WalletId>,
controller: NodeController,
manager_join_handle: JoinHandle<()>,
) -> Self {
Self {
chain_config,
event_tx,
low_priority_event_tx,
wallet_updated_tx,
controller,
manager_join_handle,
Expand Down Expand Up @@ -191,14 +197,12 @@ impl Backend {

let wallet_events = GuiWalletEvents::new(wallet_id, self.wallet_updated_tx.clone());

let controller = HandlesController::new(
let controller = HandlesController::new_unsynced(
Arc::clone(&self.chain_config),
handles_client,
wallet,
wallet_events,
)
.await
.map_err(|e| BackendError::WalletError(e.to_string()))?;
);

let best_block = controller.best_block();

Expand Down Expand Up @@ -522,7 +526,7 @@ impl Backend {
let best_block = wallet_data.controller.best_block();
if wallet_data.best_block != best_block {
Self::send_event(
&self.event_tx,
&self.low_priority_event_tx,
BackendEvent::WalletBestBlock(*wallet_id, best_block),
);
wallet_data.best_block = best_block;
Expand All @@ -535,7 +539,7 @@ impl Backend {
// (when a wallet transaction is added/updated/removed)
let balance = Self::get_account_balance(&controller);
Self::send_event(
&self.event_tx,
&self.low_priority_event_tx,
BackendEvent::Balance(*wallet_id, *account_id, balance),
);

Expand All @@ -552,7 +556,7 @@ impl Backend {
match transaction_list_res {
Ok(transaction_list) => {
Self::send_event(
&self.event_tx,
&self.low_priority_event_tx,
BackendEvent::TransactionList(
*wallet_id,
*account_id,
Expand Down Expand Up @@ -581,7 +585,7 @@ impl Backend {
match staking_balance_res {
Ok(staking_balance) => {
Self::send_event(
&self.event_tx,
&self.low_priority_event_tx,
BackendEvent::StakingBalance(
*wallet_id,
*account_id,
Expand Down
4 changes: 4 additions & 0 deletions node-gui/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub struct BackendControls {
pub initialized_node: InitializedNode,
pub backend_sender: BackendSender,
pub backend_receiver: UnboundedReceiver<BackendEvent>,
pub low_priority_backend_receiver: UnboundedReceiver<BackendEvent>,
}

/// `UnboundedSender` wrapper, used to make sure there is only one instance and it doesn't get cloned
Expand Down Expand Up @@ -94,6 +95,7 @@ pub async fn node_initialize(_time_getter: TimeGetter) -> anyhow::Result<Backend

let (request_tx, request_rx) = unbounded_channel();
let (event_tx, event_rx) = unbounded_channel();
let (low_priority_event_tx, low_priority_event_rx) = unbounded_channel();
let (wallet_updated_tx, wallet_updated_rx) = unbounded_channel();

// Subscribe to chainstate before getting the current chain_info!
Expand All @@ -115,11 +117,13 @@ pub async fn node_initialize(_time_getter: TimeGetter) -> anyhow::Result<Backend
initialized_node,
backend_sender: BackendSender::new(request_tx),
backend_receiver: event_rx,
low_priority_backend_receiver: low_priority_event_rx,
};

let backend = backend_impl::Backend::new(
chain_config,
event_tx,
low_priority_event_tx,
wallet_updated_tx,
controller,
manager_join_handle,
Expand Down
67 changes: 54 additions & 13 deletions node-gui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use backend::messages::{BackendEvent, BackendRequest};
use backend::{node_initialize, BackendControls, BackendSender};
use common::time_getter::TimeGetter;
use iced::widget::{column, container, text};
use iced::Subscription;
use iced::{executor, Application, Command, Element, Length, Settings, Theme};
use iced::{font, Subscription};
use iced_aw::native::cupertino::cupertino_spinner::CupertinoSpinner;
use main_window::{MainWindow, MainWindowMessage};
use tokio::sync::mpsc::UnboundedReceiver;
Expand All @@ -36,7 +36,6 @@ pub fn main() -> iced::Result {
id: Some("mintlayer-gui".to_owned()),
antialiasing: true,
exit_on_close_request: false,
try_opengles_first: true,
..Settings::default()
})
}
Expand All @@ -49,8 +48,13 @@ enum MintlayerNodeGUI {

#[derive(Debug)]
pub enum Message {
FromBackend(UnboundedReceiver<BackendEvent>, BackendEvent),
FromBackend(
UnboundedReceiver<BackendEvent>,
UnboundedReceiver<BackendEvent>,
BackendEvent,
),
Loaded(anyhow::Result<BackendControls>),
FontLoaded(Result<(), font::Error>),
EventOccurred(iced::Event),
ShuttingDownFinished,
MainWindowMessage(MainWindowMessage),
Expand All @@ -65,7 +69,10 @@ impl Application for MintlayerNodeGUI {
fn new(_flags: ()) -> (Self, Command<Message>) {
(
MintlayerNodeGUI::Loading,
Command::perform(node_initialize(TimeGetter::default()), Message::Loaded),
Command::batch(vec![
font::load(iced_aw::graphics::icons::ICON_FONT_BYTES).map(Message::FontLoaded),
Command::perform(node_initialize(TimeGetter::default()), Message::Loaded),
]),
)
}

Expand All @@ -85,21 +92,28 @@ impl Application for MintlayerNodeGUI {
fn update(&mut self, message: Message) -> Command<Message> {
match self {
MintlayerNodeGUI::Loading => match message {
Message::FromBackend(_, _) => unreachable!(),
Message::FromBackend(_, _, _) => unreachable!(),
Message::Loaded(Ok(backend_controls)) => {
let BackendControls {
initialized_node,
backend_sender,
backend_receiver,
low_priority_backend_receiver,
} = backend_controls;
*self =
MintlayerNodeGUI::Loaded(backend_sender, MainWindow::new(initialized_node));
recv_backend_command(backend_receiver)
recv_backend_command(backend_receiver, low_priority_backend_receiver)
}
Message::Loaded(Err(e)) => {
*self = MintlayerNodeGUI::IntializationError(e.to_string());
Command::none()
}
Message::FontLoaded(status) => {
if status.is_err() {
*self = MintlayerNodeGUI::IntializationError("Failed to load font".into());
}
Command::none()
}
Message::EventOccurred(event) => {
if let iced::Event::Window(iced::window::Event::CloseRequested) = event {
panic!("Attempted shutdown during initialization")
Expand All @@ -112,15 +126,25 @@ impl Application for MintlayerNodeGUI {
Message::MainWindowMessage(_) => Command::none(),
},
MintlayerNodeGUI::Loaded(backend_sender, w) => match message {
Message::FromBackend(backend_receiver, backend_event) => Command::batch([
Message::FromBackend(
backend_receiver,
low_priority_backend_receiver,
backend_event,
) => Command::batch([
w.update(
MainWindowMessage::FromBackend(backend_event),
backend_sender,
)
.map(Message::MainWindowMessage),
recv_backend_command(backend_receiver),
recv_backend_command(backend_receiver, low_priority_backend_receiver),
]),
Message::Loaded(_) => unreachable!("Already loaded"),
Message::FontLoaded(status) => {
if status.is_err() {
*self = MintlayerNodeGUI::IntializationError("Failed to load font".into());
}
Command::none()
}
Message::EventOccurred(event) => {
if let iced::Event::Window(iced::window::Event::CloseRequested) = event {
// TODO: this event doesn't cover the case of closing the Window through Cmd+Q in MacOS
Expand All @@ -136,8 +160,9 @@ impl Application for MintlayerNodeGUI {
}
},
MintlayerNodeGUI::IntializationError(_) => match message {
Message::FromBackend(_, _) => unreachable!(),
Message::FromBackend(_, _, _) => unreachable!(),
Message::Loaded(_) => Command::none(),
Message::FontLoaded(_) => Command::none(),
Message::EventOccurred(event) => {
if let iced::Event::Window(iced::window::Event::CloseRequested) = event {
iced::window::close()
Expand Down Expand Up @@ -192,12 +217,28 @@ impl Application for MintlayerNodeGUI {
}
}

fn recv_backend_command(mut backend_receiver: UnboundedReceiver<BackendEvent>) -> Command<Message> {
fn recv_backend_command(
mut backend_receiver: UnboundedReceiver<BackendEvent>,
mut low_priority_backend_receiver: UnboundedReceiver<BackendEvent>,
) -> Command<Message> {
Command::perform(
async move {
match backend_receiver.recv().await {
Some(msg) => Message::FromBackend(backend_receiver, msg),
None => Message::ShuttingDownFinished,
tokio::select! {
// Make sure we process low priority events at the end
biased;

msg_opt = backend_receiver.recv() => {
match msg_opt {
Some(msg) => Message::FromBackend(backend_receiver, low_priority_backend_receiver, msg),
None => Message::ShuttingDownFinished,
}
}
msg_opt = low_priority_backend_receiver.recv() => {
match msg_opt {
Some(msg) => Message::FromBackend(backend_receiver, low_priority_backend_receiver, msg),
None => Message::ShuttingDownFinished,
}
}
}
},
identity,
Expand Down
2 changes: 1 addition & 1 deletion node-gui/src/main_window/main_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl button::StyleSheet for ButtonStyle {
fn active(&self, style: &Self::Style) -> button::Appearance {
button::Appearance {
text_color: style.extended_palette().background.base.text,
border_radius: 4.0,
border_radius: 4.0.into(),
background: Some(Color::TRANSPARENT.into()),
..Default::default()
}
Expand Down
50 changes: 38 additions & 12 deletions node-gui/src/main_window/main_widget/tabs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use iced::{Command, Element};
use iced::{Command, Element, Length};
use iced_aw::{TabLabel, Tabs};
use variant_count::VariantCount;

use crate::{
backend::{messages::WalletId, BackendSender},
Expand Down Expand Up @@ -44,6 +45,21 @@ pub enum TabsMessage {
WalletMessage(WalletId, WalletMessage),
}

#[derive(VariantCount, Debug, Clone, Default)]
enum TabIndex {
#[default]
Summary = 0,
Networking = 1,
Settings = 2,
// Notice that the wallet tabs are added dynamically to these variants, so a higher index may be valid
}

impl TabIndex {
fn into_usize(self) -> usize {
self as usize
}
}

pub struct TabsWidget {
active_tab: usize,
summary_tab: SummaryTab,
Expand All @@ -55,7 +71,7 @@ pub struct TabsWidget {
impl TabsWidget {
pub fn new() -> Self {
TabsWidget {
active_tab: 0,
active_tab: TabIndex::default().into_usize(),
summary_tab: SummaryTab::new(),
networking_tab: NetworkingTab::new(),
settings_tab: SettingsTab::new(),
Expand All @@ -64,36 +80,46 @@ impl TabsWidget {
}

pub fn last_wallet_tab_index(&self) -> usize {
2 + self.wallets.len()
TabIndex::VARIANT_COUNT + self.wallets.len() - 1
}

pub fn view(&self, node_state: &NodeState) -> Element<TabsMessage> {
let position = self.settings_tab.settings().tab_bar_position.unwrap_or_default();

let mut tabs = Tabs::new(self.active_tab, TabsMessage::TabSelected)
let mut tabs = Tabs::new(TabsMessage::TabSelected)
.icon_font(iced_aw::ICON_FONT)
.push(
TabIndex::Summary as usize,
self.summary_tab.tab_label(),
self.summary_tab.view(node_state),
)
.push(
TabIndex::Networking as usize,
self.networking_tab.tab_label(),
self.networking_tab.view(node_state),
)
.push(
TabIndex::Settings as usize,
self.settings_tab.tab_label(),
self.settings_tab.view(node_state),
);

for wallet in self.wallets.iter() {
tabs = tabs.push(wallet.tab_label(), wallet.view(node_state))
for (idx, wallet) in self.wallets.iter().enumerate() {
tabs = tabs.push(
idx + TabIndex::VARIANT_COUNT,
wallet.tab_label(),
wallet.view(node_state),
)
}

tabs.icon_font(iced_aw::ICON_FONT)
.tab_bar_position(match position {
TabBarPosition::Top => iced_aw::TabBarPosition::Top,
TabBarPosition::Bottom => iced_aw::TabBarPosition::Bottom,
})
.into()
tabs.tab_bar_position(match position {
TabBarPosition::Top => iced_aw::TabBarPosition::Top,
TabBarPosition::Bottom => iced_aw::TabBarPosition::Bottom,
})
.set_active_tab(&self.active_tab)
.height(Length::Fill)
.tab_bar_max_height(70.0)
.into()
}

pub fn update(
Expand Down
Loading
Loading