diff --git a/crates/core/src/node/mod.rs b/crates/core/src/node/mod.rs index 2a971d9..450b60f 100644 --- a/crates/core/src/node/mod.rs +++ b/crates/core/src/node/mod.rs @@ -123,19 +123,19 @@ impl<'a> Controller<'a> { } /// Run the controller - pub fn run(&mut self, server: Server) { + pub fn run(&mut self, server: Server, chain_type: global::ChainTypes) { let stat_update_interval = 1; let mut next_stat_update = Utc::now().timestamp() + stat_update_interval; let delay = Duration::from_millis(50); - warn!("Running..."); + warn!("Running {:?}", chain_type); loop { if let Some(message) = self.controller_rx.try_iter().next() { match message { ControllerMessage::Shutdown => { - warn!("Shutdown in progress, please wait"); - //self.ui.stop(); + warn!("Shutdown {:?} in progress, please wait", chain_type); + // TODO this may hang on some errors server.stop(); return; } @@ -152,7 +152,6 @@ impl<'a> Controller<'a> { } thread::sleep(delay); } - } } @@ -216,21 +215,24 @@ impl NodeInterface { GlobalConfig::new(config_path.to_str().unwrap()).unwrap() } - pub fn shutdown_server(&mut self) { + pub fn shutdown_server(&mut self, join: bool) { if let Some(handle) = self.handle.take() { - if !handle.is_finished() { - self.controller_tx.clone().unwrap().send(ControllerMessage::Shutdown); + self.controller_tx + .clone() + .unwrap() + .send(ControllerMessage::Shutdown); + + if join { handle.join().expect("could not join spawned thread"); } + + self.node_started = false; + self.controller_tx = None; } } pub fn restart_server(&mut self, chain_type: global::ChainTypes) { - if let Some(tx) = self.controller_tx.clone() { - tx.send(ControllerMessage::Shutdown); - self.node_started = true; - self.controller_tx = None; - } + self.shutdown_server(false); self.start_server(chain_type); } @@ -310,7 +312,7 @@ impl NodeInterface { |serv: servers::Server, logs_rx: Option>| { let mut controller = Controller::new(logs_rx.unwrap(), ui_sender.clone(), &controller_rx); - controller.run(serv); + controller.run(serv, chain_type); }, None, api_chan, diff --git a/locale/de.json b/locale/de.json index d9a1334..e66039a 100644 --- a/locale/de.json +++ b/locale/de.json @@ -194,8 +194,9 @@ "status-line-title-main": "Status Mainnet", "status-line-title-test": "Status Testnet", "use-testnet": "This is a testnet wallet", - "exit-confirm-title": "Yes, exit now", - "exit-confirm-msg": "Are you sure you want to exit?", + "exit-confirm-title": "Quit application", + "exit-confirm-msg": "Are you sure you want to quit the application? This will shutdown any nodes that you have running.", "yes": "Yes", + "no": "No", "cancel":"Cancel" } \ No newline at end of file diff --git a/locale/en.json b/locale/en.json index d2e1e20..1f77268 100644 --- a/locale/en.json +++ b/locale/en.json @@ -195,6 +195,7 @@ "status-line-title-test": "Status Testnet", "wallet-list": "Wallets", "yes": "Yes", + "no": "No", "cancel":"Cancel", "display-name":"Display Name", "top-level-domain":"Wallet Directory", @@ -202,6 +203,6 @@ "load-wallet":"Load Wallet", "select-other":"Locate Wallet", "use-testnet": "This is a testnet wallet", - "exit-confirm-title": "Yes, exit now", - "exit-confirm-msg": "Are you sure you want to exit?" + "exit-confirm-title": "Quit application", + "exit-confirm-msg": "Are you sure you want to quit the application? This will shutdown any nodes that you have running." } diff --git a/src/gui/element/exit.rs b/src/gui/element/exit.rs deleted file mode 100644 index 32aba29..0000000 --- a/src/gui/element/exit.rs +++ /dev/null @@ -1,84 +0,0 @@ -use { - super::super::{DEFAULT_FONT_SIZE, DEFAULT_HEADER_FONT_SIZE}, - crate::gui::{style, Interaction, Message}, - crate::localization::localized_string, - grin_gui_core::theme::ColorPalette, - iced::{ - alignment, button, Alignment, Button, Column, Container, Element, Length, Row, Space, Text, - }, -}; - -pub struct StateContainer { - yes_button_state: button::State, - cancel_button_state: button::State, -} - -impl Default for StateContainer { - fn default() -> Self { - Self { - yes_button_state: Default::default(), - cancel_button_state: Default::default(), - } - } -} - -pub fn data_container<'a>( - color_palette: ColorPalette, - state: &'a mut StateContainer, -) -> Container<'a, Message> { - // Title row - let title = Text::new(localized_string("exit-confirm-msg")) - .size(DEFAULT_HEADER_FONT_SIZE) - .horizontal_alignment(alignment::Horizontal::Center); - - let title_container = - Container::new(title).style(style::BrightBackgroundContainer(color_palette)); - - let title_row = Row::new() - .push(title_container) - .align_items(Alignment::Center) - .padding(6) - .spacing(20); - - let yes_button_label = - Container::new(Text::new(localized_string("yes")).size(DEFAULT_FONT_SIZE)) - .center_x() - .align_x(alignment::Horizontal::Center); - - let cancel_button_label = - Container::new(Text::new(localized_string("cancel")).size(DEFAULT_FONT_SIZE)) - .center_x() - .align_x(alignment::Horizontal::Center); - - let yes_button: Element = - Button::new(&mut state.yes_button_state, yes_button_label) - .style(style::DefaultBoxedButton(color_palette)) - .on_press(Interaction::Exit) - .into(); - - let cancel_button: Element = - Button::new(&mut state.cancel_button_state, cancel_button_label) - .style(style::DefaultBoxedButton(color_palette)) - .on_press(Interaction::ExitCancel) - .into(); - - let unit_spacing = 15; - - let button_row = Row::new() - .push(yes_button.map(Message::Interaction)) - .push(Space::new(Length::Units(unit_spacing), Length::Units(0))) - .push(cancel_button.map(Message::Interaction)); - - let colum = Column::new() - .push(title_row) - .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) - .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) - .push(button_row) - .align_items(Alignment::Center); - - Container::new(colum) - .center_y() - .center_x() - .width(Length::Fill) - .height(Length::Fill) -} diff --git a/src/gui/element/mod.rs b/src/gui/element/mod.rs index 37bfde9..c7eb8b2 100644 --- a/src/gui/element/mod.rs +++ b/src/gui/element/mod.rs @@ -3,7 +3,7 @@ pub mod about; pub mod settings; pub mod wallet; pub mod node; -pub mod exit; +pub mod modal; // Default values used on multiple elements. pub static SMALLER_FONT_SIZE: u16 = 12; diff --git a/src/gui/element/modal.rs b/src/gui/element/modal.rs new file mode 100644 index 0000000..13535aa --- /dev/null +++ b/src/gui/element/modal.rs @@ -0,0 +1,121 @@ +use iced::Renderer; + +use { + super::super::{DEFAULT_FONT_SIZE, DEFAULT_HEADER_FONT_SIZE, SMALLER_FONT_SIZE}, + crate::gui::{style, Interaction, Message}, + crate::localization::localized_string, + grin_gui_core::theme::ColorPalette, + iced::{ + alignment, button, Alignment, Button, Column, Container, Element, Length, Row, Space, Text, + }, + iced_aw::{modal, native::card::Card, Modal}, +}; + +pub struct StateContainer { + ok_state: button::State, + cancel_state: button::State, +} + +impl Default for StateContainer { + fn default() -> Self { + Self { + ok_state: Default::default(), + cancel_state: Default::default(), + } + } +} + +pub fn exit_card<'a>( + color_palette: ColorPalette, + state: &'a mut StateContainer, +) -> Card { + let yes_button_label = + Container::new(Text::new(localized_string("yes")).size(DEFAULT_FONT_SIZE)) + .center_x() + .align_x(alignment::Horizontal::Center); + + let cancel_button_label = + Container::new(Text::new(localized_string("no")).size(DEFAULT_FONT_SIZE)) + .center_x() + .align_x(alignment::Horizontal::Center); + + let yes_button: Element = + Button::new(&mut state.ok_state, yes_button_label) + .style(style::DefaultBoxedButton(color_palette)) + .on_press(Interaction::Exit) + .into(); + + let cancel_button: Element = + Button::new(&mut state.cancel_state, cancel_button_label) + .style(style::DefaultBoxedButton(color_palette)) + .on_press(Interaction::ExitCancel) + .into(); + + let unit_spacing = 15; + + let button_row = Row::new() + .push(yes_button.map(Message::Interaction)) + .push(Space::new(Length::Units(unit_spacing), Length::Units(0))) + .push(cancel_button.map(Message::Interaction)); + + Card::new( + Text::new(localized_string("exit-confirm-title")) + .size(DEFAULT_HEADER_FONT_SIZE) + .horizontal_alignment(alignment::Horizontal::Center), + Text::new(localized_string("exit-confirm-msg")).size(DEFAULT_FONT_SIZE), + ) + .foot( + Column::new() + .spacing(10) + .padding(5) + .width(Length::Fill) + .align_items(Alignment::Center) + .push(button_row), + ) + .max_width(500) + .on_close(Message::Interaction(Interaction::CloseErrorModal)) + .style(style::NormalModalCardContainer(color_palette)) +} + +pub fn error_card<'a>( + color_palette: ColorPalette, + state: &'a mut StateContainer, + error_cause: String, +) -> Card { + Card::new( + Text::new(localized_string("error-detail")).size(DEFAULT_HEADER_FONT_SIZE), + Text::new(error_cause.clone()).size(DEFAULT_FONT_SIZE), + ) + .foot( + Column::new() + .spacing(10) + .padding(5) + .width(Length::Fill) + .align_items(Alignment::Center) + .push( + Button::new( + &mut state.cancel_state, + Text::new(localized_string("ok-caps")) + .size(DEFAULT_FONT_SIZE) + .horizontal_alignment(alignment::Horizontal::Center), + ) + .style(style::DefaultButton(color_palette)) + .on_press(Message::Interaction(Interaction::CloseErrorModal)), + ) + .push( + Button::new( + &mut state.ok_state, + Text::new(localized_string("copy-to-clipboard")) + .size(SMALLER_FONT_SIZE) + .horizontal_alignment(alignment::Horizontal::Center), + ) + .style(style::NormalTextButton(color_palette)) + .on_press(Message::Interaction(Interaction::WriteToClipboard( + error_cause, + ))), + ), + ) + .max_width(500) + .on_close(Message::Interaction(Interaction::CloseErrorModal)) + .style(style::NormalModalCardContainer(color_palette)) +} diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 4ffa725..79ecec7 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -19,7 +19,7 @@ use grin_gui_core::{ use iced::{ button, pick_list, text_input, Alignment, Application, Button, Column, Command, Container, Element, Length, Settings, Subscription, - Text, + Text, Row, Space, }; use iced_native::alignment; @@ -49,7 +49,7 @@ pub struct GrinGui { config: Config, /// Top-level error modal overlay - error_modal_state: modal::State, + modal_state: modal::State, /// Main menu state menu_state: element::menu::StateContainer, @@ -69,19 +69,19 @@ pub struct GrinGui { /// About screen state about_state: element::about::StateContainer, - exit_state: element::exit::StateContainer, - show_confirm: bool, + show_exit: bool, exit: bool, } impl GrinGui { pub fn show_exit (&mut self, show: bool) { - self.show_confirm = show; + self.modal_state.show(show); + self.show_exit = show; } pub fn safe_exit (&mut self) { let mut node = self.node_interface.write().unwrap(); - node.shutdown_server(); + node.shutdown_server(true); self.exit = true; } } @@ -100,7 +100,7 @@ impl<'a> Default for GrinGui { error: None, mode: Mode::Catalog, config: Config::default(), - error_modal_state: Default::default(), + modal_state: Default::default(), menu_state: Default::default(), wallet_state: Default::default(), node_state: Default::default(), @@ -109,8 +109,7 @@ impl<'a> Default for GrinGui { node_settings_state: Default::default(), general_settings_state: Default::default(), about_state: Default::default(), - exit_state: Default::default(), - show_confirm: false, + show_exit: false, exit: false, } } @@ -238,64 +237,51 @@ impl Application for GrinGui { let menu_state = self.menu_state.clone(); - let mut content = Column::new(); - - if self.show_confirm { - let exit_overlay = element::exit::data_container(color_palette, &mut self.exit_state); - content = content.push(exit_overlay) - } else { - content = content.push(element::menu::data_container( - &mut self.menu_state, - color_palette, - &mut self.error, - )); - - // Spacer between menu and content. - //content = content.push(Space::new(Length::Units(0), Length::Units(DEFAULT_PADDING))); - match menu_state.mode { - element::menu::Mode::Wallet => { - let setup_container = element::wallet::data_container( - color_palette, - &mut self.wallet_state, - &self.config - ); - content = content.push(setup_container) - } - element::menu::Mode::Node => { - let chain_type = self.node_interface.read().unwrap().chain_type.unwrap_or_else( || ChainTypes::Mainnet); - let node_container = element::node::data_container( - color_palette, - &mut self.node_state, - chain_type, - ); - content = content.push(node_container) - } - element::menu::Mode::About => { - let about_container = - element::about::data_container(color_palette, &None, &mut self.about_state); - content = content.push(about_container) - } - element::menu::Mode::Settings => { - content = content.push(element::settings::data_container( - &mut self.settings_state, - &mut self.config, - &mut self.wallet_settings_state, - &mut self.node_settings_state, - &mut self.general_settings_state, - color_palette, - )) - /*if let Some(settings_container) = views.get_mut(settings_view_index) { - content = content.push(settings_container.view.data_container(color_palette)) - }*/ - } + let mut content = Column::new().push(element::menu::data_container( + &mut self.menu_state, + color_palette, + &mut self.error, + )); + + // Spacer between menu and content. + //content = content.push(Space::new(Length::Units(0), Length::Units(DEFAULT_PADDING))); + match menu_state.mode { + element::menu::Mode::Wallet => { + let setup_container = element::wallet::data_container( + color_palette, + &mut self.wallet_state, + &self.config + ); + content = content.push(setup_container) + } + element::menu::Mode::Node => { + let chain_type = self.node_interface.read().unwrap().chain_type.unwrap_or_else( || ChainTypes::Mainnet); + let node_container = element::node::data_container( + color_palette, + &mut self.node_state, + chain_type, + ); + content = content.push(node_container) + } + element::menu::Mode::About => { + let about_container = + element::about::data_container(color_palette, &None, &mut self.about_state); + content = content.push(about_container) + } + element::menu::Mode::Settings => { + content = content.push(element::settings::data_container( + &mut self.settings_state, + &mut self.config, + &mut self.wallet_settings_state, + &mut self.node_settings_state, + &mut self.general_settings_state, + color_palette, + )) + /*if let Some(settings_container) = views.get_mut(settings_view_index) { + content = content.push(settings_container.view.data_container(color_palette)) + }*/ } } - - let error_cause = if let Some(e) = &self.error { - error_cause_string(&e) - } else { - "None".into() - }; let content: Element = // Wraps everything in a container. @@ -305,41 +291,22 @@ impl Application for GrinGui { .style(style::NormalBackgroundContainer(color_palette)) .into(); - Modal::new(&mut self.error_modal_state, content, move|state| { - Card::new( - Text::new(localized_string("error-detail")).size(DEFAULT_HEADER_FONT_SIZE), - Text::new(&error_cause).size(DEFAULT_FONT_SIZE) - ) - .foot( - Column::new() - .spacing(10) - .padding(5) - .width(Length::Fill) - .align_items(Alignment::Center) - .push( - Button::new( - &mut state.cancel_state, - Text::new(localized_string("ok-caps")).size(DEFAULT_FONT_SIZE).horizontal_alignment(alignment::Horizontal::Center), - ) - .style(style::DefaultButton(color_palette)) - .on_press(Message::Interaction(Interaction::CloseErrorModal)), - ) - .push( - Button::new( - &mut state.ok_state, - Text::new(localized_string("copy-to-clipboard")).size(SMALLER_FONT_SIZE).horizontal_alignment(alignment::Horizontal::Center), - ) - .style(style::NormalTextButton(color_palette)) - .on_press(Message::Interaction(Interaction::WriteToClipboard(error_cause.clone()))), - ) - ) - .max_width(500) - .on_close(Message::Interaction(Interaction::CloseErrorModal)) - .style(style::NormalModalCardContainer(color_palette)) - .into() + let show_exit = self.show_exit; + let error_cause = if let Some(e) = &self.error { + error_cause_string(&e) + } else { + "".into() + }; + + Modal::new(&mut self.modal_state, content, move|state| { + if show_exit { + element::modal::exit_card(color_palette, state).into() + } else { + element::modal::error_card(color_palette, state, error_cause.clone()).into() + } }) - .backdrop(Message::Interaction(Interaction::CloseErrorModal)) - .on_esc(Message::Interaction(Interaction::CloseErrorModal)) + //.backdrop(Message::Interaction(Interaction::CloseErrorModal)) + //.on_esc(Message::Interaction(Interaction::CloseErrorModal)) .style(style::NormalModalContainer(color_palette)) .into() @@ -503,12 +470,6 @@ pub enum Interaction { ExitCancel } -#[derive(Default)] -struct ModalState { - cancel_state: button::State, - ok_state: button::State, -} - pub struct ThemeState { themes: Vec<(String, Theme)>, current_theme_name: String, diff --git a/src/gui/style.rs b/src/gui/style.rs index dbf950c..187bf24 100644 --- a/src/gui/style.rs +++ b/src/gui/style.rs @@ -11,7 +11,7 @@ impl modal::StyleSheet for NormalModalContainer { fn active(&self) -> modal::Style { modal::Style { background: Background::Color(Color { - a: 0.6, + a: 0.9, ..self.0.base.foreground }), } diff --git a/src/gui/update.rs b/src/gui/update.rs index bb71607..4741c1d 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -113,9 +113,9 @@ pub fn handle_message(grin_gui: &mut GrinGui, message: Message) -> Result grin_gui.error_modal_state.show(true), + Message::Interaction(Interaction::OpenErrorModal) => grin_gui.modal_state.show(true), Message::Interaction(Interaction::CloseErrorModal) => { - grin_gui.error_modal_state.show(false) + grin_gui.modal_state.show(false) } // Clipboard messages Message::Interaction(Interaction::WriteToClipboard(contents)) => { diff --git a/src/main.rs b/src/main.rs index 62e7aec..88e73ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ #![allow(dead_code)] #![allow(unused_variables)] #![allow(unused_mut)] +#![allow(unused_imports)] mod cli; mod gui;