From 65bec5a4a1ec360cfe6484785957a6098069e625 Mon Sep 17 00:00:00 2001 From: Ralf Zerres Date: Wed, 22 Jul 2020 18:11:39 +0200 Subject: [PATCH] callbacks: introduce theming, i18n truning, introduce menu * global_state: use global "lang", support env * policycheck_state: use global "lang", new menu function, ron theming Signed-off-by: Ralf Zerres --- advotracker/src/callbacks/global_state.rs | 13 +- .../src/callbacks/policycheck_state.rs | 384 ++++++++++++------ 2 files changed, 266 insertions(+), 131 deletions(-) diff --git a/advotracker/src/callbacks/global_state.rs b/advotracker/src/callbacks/global_state.rs index 5818830..d0139ad 100644 --- a/advotracker/src/callbacks/global_state.rs +++ b/advotracker/src/callbacks/global_state.rs @@ -5,6 +5,8 @@ * SPDX-License-Identifier: (0BSD or MIT) */ +use serde::Deserialize; + use orbtk::prelude::*; use crate::{ @@ -12,6 +14,15 @@ use crate::{ data::structures::PolicyList }; +/// define valid environment variables provided via .env files +/// located in the current call directory +/// this is primarily used in testing scenarios (eg. debugging) +#[derive(Debug, Deserialize)] +struct Environment { + test_lang: String, + rust_log: String, +} + /// Provides generic methods to handle states of datatypes (e.g. used in `PolicyList`). pub trait GlobalState { /// Navigates to the given entity. @@ -19,7 +30,7 @@ pub trait GlobalState { if let Some(old_focused_element) = ctx.window().get::("global").focused_widget { let mut old_focused_element = ctx.get_widget(old_focused_element); old_focused_element.set("focused", false); - old_focused_element.update_theme_by_state(false); + //old_focused_element.update_theme_by_state(false); } ctx.window().get_mut::("global").focused_widget = None; ctx.widget().set("visibility", Visibility::Collapsed); diff --git a/advotracker/src/callbacks/policycheck_state.rs b/advotracker/src/callbacks/policycheck_state.rs index 7cef10b..ca76689 100644 --- a/advotracker/src/callbacks/policycheck_state.rs +++ b/advotracker/src/callbacks/policycheck_state.rs @@ -1,8 +1,12 @@ //use chrono::{Local, DateTime, Duration}; use dotenv::dotenv; use locales::t; -use orbtk::prelude::*; -use orbtk::shell::WindowRequest; +use orbtk::{ + prelude::*, + shell::WindowRequest, + theme::{COLORS_RON, DARK_THEME_RON, FONTS_RON}, + theming::config::ThemeConfig, +}; use serde::Deserialize; use std::{env, process}; use std::collections::HashMap; @@ -28,15 +32,20 @@ pub enum Action { ParseEntry(Entity), AddProgress(f64), ResetProgress, + SetMenu(Entity), SetProgress(f64), SetProgressPopup(Entity), RemoveFocus(Entity), - RemoveProgressPopup(Entity), + RemovePopup(Entity), SetEntry(Entity), SetVisibility(Entity), TextChanged(Entity, usize), + ToggleTheme(Entity), } +/// define valid environment variables provided via .env files +/// located in the current call directory +/// this is primarily used in testing scenarios (eg. debugging) #[derive(Debug, Deserialize)] struct Environment { test_lang: String, @@ -49,15 +58,20 @@ pub struct PolicyCheckState { action: Option, duration: Duration, label_result: Entity, + lang: String, last_focused: Option, - menu_button: Entity, + button_menu: Entity, + menu: Entity, + popup_progress: Entity, policy_data_count: usize, policy_numbers: HashMap, progress_bar: Entity, progress_count: f64, - progress_popup: Entity + theme: String } +static DARK_EXT: &'static str = include_str!("../../resources/stylesheets/advotracker_dark.ron"); + impl GlobalState for PolicyCheckState {} /// method definitions, that react on any given state change inside the view @@ -71,22 +85,13 @@ impl PolicyCheckState { pub fn create_hashmap(&mut self, _ctx: &mut Context<'_>) -> Result<(), Box> { trace!(target: "advotracker", create_hashmap = "started"); - // WIP: redundant lang selection (already in main!) - let mut lang = env::var("lang").unwrap_or("en".to_string()); - // testing environment: read from .env file - dotenv().ok(); - match envy::from_env::() { - Ok(environment) => { - if environment.test_lang != lang { lang = environment.test_lang; } - }, - Err(e) => { debug!(target: "advotracker", "{}", e); } - } - - let policy_list = PolicyList::new("Allianz Versicherungsnummen-List"); + let res = t!("policy.string.label_policy_list", self.lang); + let policy_list = PolicyList::new(res); trace!(target: "advotracker", policy_list = ?policy_list); // create vector to hold imported data - let mut policy_data = PolicyDataList::new("Allianz-Import latest"); + let res = t!("policy.string.label_policy_data", self.lang); + let mut policy_data = PolicyDataList::new(res); trace!(target: "advotracker", policy_data = ?policy_data); let mut policy_numbers : HashMap = HashMap::new(); @@ -96,7 +101,7 @@ impl PolicyCheckState { let mut csv_import_path = String::from("POLLFNR_WOECHENTLICH.txt"); match import(&mut csv_import_path, &mut policy_data, &mut policy_numbers, &mut self.policy_data_count, - &lang) { + &self.lang) { Ok((count, duration)) => { self.policy_data_count = count; self.duration = duration; @@ -123,8 +128,24 @@ impl PolicyCheckState { let _text = text_box.text_mut(); } + /// Get the active language environment. + pub fn get_lang() -> String { + // get system environment + let mut lang = env::var("LANG").unwrap_or("en".to_string()); + + // testing environment: read from .env file + dotenv().ok(); + match envy::from_env::() { + Ok(environment) => { + if environment.test_lang != lang { lang = environment.test_lang; } + }, + Err(e) => { info!(target: "advotracker", "{}", e) } + } + lang + } + /// Import policy numbers into hashmap - fn import_data(&mut self, lang: &String, ctx: &mut Context<'_>) + fn import_data(&mut self, ctx: &mut Context<'_>) -> Result<(), Box> { // WIP: for now, only import once per session if self.policy_data_count == 0 { @@ -134,9 +155,9 @@ impl PolicyCheckState { if self.policy_numbers.len() == 0 { // initialize popup widget let sender = ctx.window_sender(); - self.set_progress_popup(&lang, ctx); + self.set_popup_progress(ctx); self.progress_count += 0.33; - self.update_progress_bar(&lang, ctx); + self.update_progress_bar(ctx); sender.send(WindowRequest::Redraw).unwrap(); // for _ in 1..4 { @@ -148,27 +169,23 @@ impl PolicyCheckState { // importing policy code elements from csv-file match self.create_hashmap(ctx) { Ok(()) => { - let res = t!("policy.hashmap.success", lang); + let res = t!("policy.hashmap.success", self.lang); info!("hashmap has: {:?} entries", self.policy_data_count); trace!(target: "advotracker", hashmap_status = ?res, hashmap_entries = ?self.policy_data_count); self.progress_count = 1.; - self.update_progress_bar(&lang, ctx); + self.update_progress_bar(ctx); sender.send(WindowRequest::Redraw).unwrap(); } _ => { - let res = t!("policy.hashmap.failed", lang); + let res = t!("policy.hashmap.failed", self.lang); error!("{:?}", res); trace!(target: "advotracker", hashmap_status = ?res); } } - - - // remove popup widget now, that we are done - //self.remove_progress_popup(ctx); } } else { trace!(target: "advotracker", @@ -180,8 +197,11 @@ impl PolicyCheckState { } /// Open menu. - pub fn open_menu(&mut self, text_block: Entity, ctx: &mut Context<'_>) { - let _menu_string = ctx.get_widget(text_block).get::("text"); + pub fn open_menu(&mut self, _entity: Entity, ctx: &mut Context<'_>) { + //let menu_string = ctx.get_widget(entity).get::("text"); + //.child(policycheck_menu); + //self.set_popup_menu(ctx); + self.set_menu(ctx); } /// Parse validity of the given policy number. @@ -192,19 +212,19 @@ impl PolicyCheckState { let policy_number_string = ctx.get_widget(policy_check_policy_number).get::("text").as_string(); let policy_number_length = policy_number_string.len(); - // WIP: redundant lang selection (already in main!) - let mut lang = env::var("lang").unwrap_or("en".to_string()); - // testing environment: read from .env file - dotenv().ok(); - match envy::from_env::() { - Ok(environment) => { - if environment.test_lang != lang { lang = environment.test_lang; } - }, - Err(e) => { debug!(target: "advotracker", "{}", e); } - } + // // WIP: redundant lang selection (already in main!) + // let mut lang = env::var("lang").unwrap_or("en".to_string()); + // // testing environment: read from .env file + // dotenv().ok(); + // match envy::from_env::() { + // Ok(environment) => { + // if environment.test_lang != lang { lang = environment.test_lang; } + // }, + // Err(e) => { debug!(target: "advotracker", "{}", e); } + // } // Load data into hashmap - match self.import_data(&lang, ctx) { + match self.import_data(ctx) { Ok(()) => trace!(target: "advotracker", policycheck_state = "init", import_data = "success"), Err(e) => trace!(target: "advotracker", policycheck_state = "init", import_data = ?e), } @@ -236,7 +256,7 @@ impl PolicyCheckState { result_wrapper.set_text(string_result); button(ctx.child("policy_check_button_result")).set_icon(material_icons_font::MD_CHECK); button(ctx.child("policy_check_button_result")).set_icon_brush("#008000"); - let res = t!("policy.validation.button_success", lang); + let res = t!("policy.validation.button_success", self.lang); button(ctx.child("policy_check_button_result")).set_text(res); button(ctx.child("policy_check_button_result")).set_visibility(Visibility::Visible); button(ctx.child("policy_check_button_result")).set_foreground("#008000"); @@ -245,16 +265,16 @@ impl PolicyCheckState { } _ => { // no matching key - let res = t!("policy.validation.failed", lang); + let res = t!("policy.validation.failed", self.lang); trace!(target: "advotracker", state = ?res, policy_number = ?p); button(ctx.child("policy_check_button_result")).set_icon(material_icons_font::MD_CLEAR); button(ctx.child("policy_check_button_result")).set_icon_brush("#FF0000"); - let res = t!("policy.validation.button_failed", lang); + let res = t!("policy.validation.button_failed", self.lang); button(ctx.child("policy_check_button_result")).set_text(res); button(ctx.child("policy_check_button_result")).set_visibility(Visibility::Visible); button(ctx.child("policy_check_button_result")).set_foreground("#FF0000"); let mut text_block_wrapper: TextBlockCtx<'_> = text_block(ctx.child("policy_check_result")); - let res = t!("policy.validation.not_found", lang); + let res = t!("policy.validation.not_found", self.lang); text_block_wrapper.set_text(res); text_block(ctx.child("policy_check_label_result")).set_visibility(Visibility::Visible); } @@ -268,7 +288,7 @@ impl PolicyCheckState { button(ctx.child("policy_check_button_result")).set_foreground("#FF0000"); text_block(ctx.child("policy_check_label_result")).set_visibility(Visibility::Visible); let mut text_block_wrapper: TextBlockCtx<'_> = text_block(ctx.child("policy_check_result")); - let res = t!("policy.validation.invalid_input", lang); + let res = t!("policy.validation.invalid_input", self.lang); text_block_wrapper.set_text(res); text_block_wrapper.set_enabled(true); } @@ -277,7 +297,7 @@ impl PolicyCheckState { if policy_number_length < 10 { let mut text_block_wrapper: TextBlockCtx<'_> = text_block(ctx.child("policy_check_result")); text_block_wrapper.set_enabled(true); - let res = t!("policy.validation.to_short", lang); + let res = t!("policy.validation.to_short", self.lang); text_block_wrapper.set_text(res); text_block(ctx.child("policy_check_label_result")).set_visibility(Visibility::Visible); //self.set_visibility(policy_check_policy_number, ctx); @@ -285,7 +305,7 @@ impl PolicyCheckState { if policy_number_length > 10 { let mut text_block_wrapper: TextBlockCtx<'_> = text_block(ctx.child("policy_check_result")); text_block_wrapper.set_enabled(true); - let res = t!("policy.validation.to_long", lang); + let res = t!("policy.validation.to_long", self.lang); text_block_wrapper.set_text(res); text_block(ctx.child("policy_check_label_result")).set_visibility(Visibility::Visible); } @@ -293,13 +313,11 @@ impl PolicyCheckState { trace!(target: "advotracker", parse_entry = "finished"); } - /// Remove a popup box showing the data import status in a progress bar - fn remove_progress_popup(&mut self, ctx: &mut Context<'_>) { - let progress_popup = self.progress_popup; - //let sleep = time::Duration::from_secs(3); - //thread::sleep(sleep); - ctx.remove_child(progress_popup); - println!("ProgressPopup {:?} removed !", progress_popup); + /// Remove the menu popup box + fn remove_popup(&mut self, id: Entity, ctx: &mut Context<'_>) { + ctx.remove_child(self.menu); + ctx.remove_child(self.popup_progress); + println!("Popup {:?} removed !", id); } // /// If TextBox 'policy_check_policy_number' is empty, disable button "clear" @@ -318,11 +336,7 @@ impl PolicyCheckState { /// Change status of given text box to edit mode. fn set_entry(&mut self, policy_check_policy_number: Entity, ctx: &mut Context<'_>) { if *ctx.get_widget(policy_check_policy_number).get::("focused") { - //let mut my_ctx: WidgetContainer<'_> = ctx.widget(); - //let mut child: WidgetContainer<'_> = ctx.get_widget(policy_check_policy_number); - //ctx.get_widget(policy_check_policy_number).set("enabled", true); let mut text_box_wrapper: TextBoxCtx<'_> = text_box(ctx.child("policy_check_policy_number")); - //ctx.push_event_by_window(FocusEvent::RemoveFocus(policy_check_policy_number)); text_box_wrapper.set_visibility( Visibility::Visible); text_box_wrapper.set_enabled(true); text_box_wrapper.set_text(""); @@ -332,31 +346,46 @@ impl PolicyCheckState { if let Some(old_focused_element) = ctx.window().get::("global").focused_widget { ctx.push_event_by_window(FocusEvent::RemoveFocus(old_focused_element)); } - } - /// Set a progress popup that updates the import status in a progress bar - fn set_progress_popup(&mut self, lang: &String, ctx: &mut Context<'_>) { - //println!("Set up Progress popup: {:?}", text_box); - let stack = ctx - .entity_of_child(ID_POLICY_CHECK_RESULT) - .expect("PolicyCheckState.init: Can't find entity of resource 'ID_POLICY_CHECK_RESULT'."); + /// Set a menu + fn set_menu(&mut self, ctx: &mut Context<'_>) { let current_entity = ctx.entity; let build_context = &mut ctx.build_context(); - let res = t!("policy.string.progress_text", lang); - let progress_popup = create_progress_popup(current_entity, &res, build_context); + // create a menu overlay + self.menu = create_menu(current_entity, build_context); + let _menu = build_context.append_child_to_overlay(self.menu) + .expect("PolicyCheckState: Can't create overlay as child of entity"); + + let label_account = t!("policy.menu.label_account", self.lang); + button(ctx.child(ID_POLICY_CHECK_MENU_LABEL_ACCOUNT)).set_text(label_account); + let label_quit = t!("policy.menu.label_quit", self.lang); + button(ctx.child(ID_POLICY_CHECK_MENU_LABEL_QUIT)).set_text(label_quit); + let label_toggle_theme = t!("policy.menu.label_toggle_theme", self.lang); + button(ctx.child(ID_POLICY_CHECK_MENU_LABEL_TOGGLE_THEME)).set_text(label_toggle_theme); + } + + /// Set a progress popup that updates the import status in a progress bar + fn set_popup_progress(&mut self, ctx: &mut Context<'_>) { + //println!("Set up Progress popup: {:?}", text_box); + let stack = ctx + .entity_of_child(ID_POLICY_CHECK_RESULT) + .expect("PolicyCheckState: Can't find entity of resource 'ID_POLICY_CHECK_RESULT'."); + let current_entity = ctx.entity; + let build_context = &mut ctx.build_context(); + + let res = t!("policy.string.progress_text", self.lang); + self.popup_progress = create_popup_progress(current_entity, &res, build_context); // create a progress_popup widget as a child of entity "ID_POLICY_CHECK_POLICY_NUMBER" - build_context.append_child(stack, progress_popup); - self.progress_popup = progress_popup; + build_context.append_child(stack, self.popup_progress); - let progress_bar = ctx + self.progress_bar = ctx .entity_of_child(ID_POLICY_CHECK_PROGRESS_BAR) .expect("PolicyCheckState.init: Can't find entity of resource 'ID_POLICY_CHECK_PROGRESS_BAR'."); - self.progress_bar = progress_bar; - println!("ProgressPopup created: {:?}", progress_popup); + println!("PopupProgress created: {:?}", self.popup_progress); } /// Change visibility of the result label. @@ -369,7 +398,19 @@ impl PolicyCheckState { text_block(ctx.child(ID_POLICY_CHECK_BUTTON_RESULT)).set_visibility(Visibility::Visible); } - ctx.get_widget(self.label_result).update_theme_by_state(true); + //ctx.get_widget(self.label_result).update_theme_by_state(true); + } + + + /// Set and activate the theme attributes. + pub fn theme() -> Theme { + Theme::from_config( + // sourcing: crates/theme/assets/dark/dark.ron + ThemeConfig::from(DARK_THEME_RON) + .extend(ThemeConfig::from(DARK_EXT)) + .extend(ThemeConfig::from(COLORS_RON)) + .extend(ThemeConfig::from(FONTS_RON)), + ) } /// Update count of elements in the policy data list. @@ -378,8 +419,8 @@ impl PolicyCheckState { ctx.widget().set(PROP_POLICY_DATA_COUNT, data_list_count); } - fn update_progress_bar(&self, lang: &String, ctx: &mut Context<'_>) { - let res = t!("policy.string.progress_time", lang); + fn update_progress_bar(&self, ctx: &mut Context<'_>) { + let res = t!("policy.string.progress_time", self.lang); let string_duration = format!("{}: {:?}", res, self.duration); text_block(ctx.child(ID_POLICY_CHECK_PROGRESS_TIME)).set_text(string_duration); @@ -397,7 +438,7 @@ impl State for PolicyCheckState { trace!(target: "advotracker", policycheck_state = "init", status = "started"); // Entities - self.menu_button = ctx + self.button_menu = ctx .entity_of_child(ID_POLICY_CHECK_BUTTON_MENU) .expect("PolicyCheckState.init: Can't find resource entity 'ID_POLICY_CHECK_BUTTON_MENU'."); @@ -405,14 +446,6 @@ impl State for PolicyCheckState { .entity_of_child(ID_POLICY_CHECK_LABEL_RESULT) .expect("PolicyCheckState.init: Can't find resource entity 'ID_POLICY_CHECK_LABEL_RESULT'."); - // self.progress_bar = ctx - // .entity_of_child(ID_POLICY_CHECK_PROGRESS_BAR) - // .expect("PolicyCheckState.init: Can't find resource entity 'ID_POLICY_CHECK_PROGRESS_BAR'."); - - // self.progress_popup = ctx - // .entity_of_child(ID_POLICY_CHECK_PROGRESS_POPUP) - // .expect("PolicyCheckState.init: Can't find resource entity 'ID_POLICY_CHECK_PROGRESS_POPUP'."); - // WIP: redundant lang selection (already in main!) let mut lang = env::var("lang").unwrap_or("en".to_string()); // testing environment: read from .env file @@ -423,24 +456,21 @@ impl State for PolicyCheckState { }, Err(e) => { debug!(target: "advotracker", "{}", e); } } + self.lang = lang; // Constants - let res = t!("policy.string.header", lang); + let res = t!("policy.string.header", self.lang); text_block(ctx.child(ID_POLICY_CHECK_HEADER)).set_text(res); - let res = t!("policy.string.label_policy_number", lang); + let res = t!("policy.string.label_policy_number", self.lang); let string_label_policy_number = format!("{}:", res); text_block(ctx.child(ID_POLICY_CHECK_LABEL_POLICY_NUMBER)).set_text(string_label_policy_number); - let res = t!("policy.string.label_result", lang); + let res = t!("policy.string.label_result", self.lang); let string_label_result = format!("{}:", res); text_block(ctx.child(ID_POLICY_CHECK_LABEL_RESULT)).set_text(string_label_result); text_block(ctx.child(ID_POLICY_CHECK_LABEL_RESULT)).set_visibility(Visibility::Collapsed); - // self.progress_popup = ctx - // .entity_of_child(ID_POLICY_CHECK_PROGRESS_POPUP) - // .expect("PolicyCheckState.init: Can't find entity of resource 'ID_POLICY_CHECK_PROGRESS_POPUP'."); - // // Load the saved data from a file in 'ron' format into our data structure. // // The cargo package identifier (default: 'nwx.advotracker') is used as the // // app directory name. The directory location is OS dependant @@ -453,10 +483,6 @@ impl State for PolicyCheckState { // ctx.widget().set(PROP_ADVOTRACKER, policy_data); // } - // number of elements in the restored policy data - //policy_data_count = policy_data.len().clone; - //self.update_data_count(ctx); - let time_end = SystemTime::now(); let duration = time_end.duration_since(time_start); @@ -482,7 +508,7 @@ impl State for PolicyCheckState { let new_width = old_width + increment; // Set the ProgressBar's val property to the calculated percentage // (whereas 0.0 means 0 %, and 1.0 means 100 %) to increment the progress - if new_width <= 1.0 { + if new_width <= 1. { progress_bar(ctx.child(ID_POLICY_CHECK_PROGRESS_BAR)).set_val(new_width); } else { progress_bar(ctx.child(ID_POLICY_CHECK_PROGRESS_BAR)).set_val(1.); @@ -495,8 +521,7 @@ impl State for PolicyCheckState { println!("entry changed: {}", text_box(ctx.get_widget(entity)).text()); } Action::ImportData => { - let lang = "en".to_string(); - match self.import_data(&lang, ctx) { + match self.import_data(ctx) { Ok(()) => { trace!(target: "advotracker", import_data = "success"); } @@ -506,8 +531,8 @@ impl State for PolicyCheckState { } } } - Action::OpenMenu(text_block) => { - self.open_menu(text_block, ctx); + Action::OpenMenu(entity) => { + self.open_menu(entity, ctx); } Action::ParseEntry(text_box) => { self.parse_entry(text_box, ctx); @@ -516,8 +541,8 @@ impl State for PolicyCheckState { ctx.get_widget(policy_check_policy_number).set("enabled", false); ctx.push_event_by_window(FocusEvent::RemoveFocus(policy_check_policy_number)); } - Action::RemoveProgressPopup(_entity) => { - self.remove_progress_popup(ctx); + Action::RemovePopup(entity) => { + self.remove_popup(entity, ctx); } Action::ResetProgress => { progress_bar(ctx.child(ID_POLICY_CHECK_PROGRESS_BAR)).set_val(0.); @@ -526,6 +551,9 @@ impl State for PolicyCheckState { //self.last_focused = Some(); self.set_entry(policy_check_policy_number, ctx); } + Action::SetMenu(_entity) => { + self.set_menu(ctx); + } Action::SetProgress(value) => { if value >= 0. || value <= 1. { progress_bar(ctx.child(ID_POLICY_CHECK_PROGRESS_BAR)).set_val(value); @@ -534,8 +562,7 @@ impl State for PolicyCheckState { } } Action::SetProgressPopup(_entity) => { - let lang = "en".to_string(); - self.set_progress_popup(&lang, ctx); + self.set_popup_progress(ctx); } Action::SetVisibility(_entity) => { text_block(ctx.child(ID_POLICY_CHECK_LABEL_RESULT)).set_visibility(Visibility::Collapsed); @@ -543,6 +570,21 @@ impl State for PolicyCheckState { Action::TextChanged(entity, _index) => { self.set_entry(entity, ctx); } + Action::ToggleTheme(_entity) => { + if self.theme == "light" { + self.theme = "dark_theme".to_string(); + } else { + self.theme = "light_theme".to_string(); + } + //match &self.theme { + // light => { self.theme = "dark_theme".to_string(); } + // dark => { self.theme = "light_theme".to_string(); } + // _ => { debug!(target: "advotracker", "theme not supported!"); } + // } + println!{"Switch theme to {:?}", self.theme} + let theme = dark_theme(); + ctx.switch_theme(theme); + } } } // Reset action @@ -568,53 +610,135 @@ impl State for PolicyCheckState { } } - /// Create a progress popup with update status of an onging data import - fn create_progress_popup(popup: Entity, text: &str, ctx: &mut BuildContext<'_>) -> Entity { - Popup::new() - .id(ID_POLICY_CHECK_PROGRESS_POPUP) - .target(popup) - .open(true) + /// Create a menu popup + fn create_menu(menu: Entity, ctx: &mut BuildContext<'_>) -> Entity { + Container::new() + .style("container_menu") .width(280.0) .height(140.0) - .h_align("left") + .position((100.0, 100.0)) + .child( + Grid::new() + .id(ID_POLICY_CHECK_MENU) + .columns( + Columns::new() + // Menu Button + .add("80") + // Seperator + .add("1") + // Keyboard Shortcut + .add("*") + .build(), + ) + .rows( + Rows::new() + .add("auto") + .add("auto") + .add("auto") + .build(), + ) + .child( + Button::new() + .id(ID_POLICY_CHECK_MENU_LABEL_ACCOUNT) + .style("button_menu") + .attach(Grid::row(0)) + .attach(Grid::column(0)) + .attach(Grid::column_span(2)) + .icon(material_icons_font::MD_PERSON) + .build(ctx), + ) + .child( + Button::new() + .id(ID_POLICY_CHECK_MENU_LABEL_TOGGLE_THEME) + //.style("body") + .style("button_menu") + .attach(Grid::row(1)) + .attach(Grid::column(0)) + .attach(Grid::column_span(2)) + .icon(material_icons_font::MD_EDIT) + .on_click(move |states, _| { + states.get_mut::(menu) + .action(Action::ToggleTheme(menu)); + true + }) + .build(ctx), + ) + .child( + Button::new() + .id(ID_POLICY_CHECK_MENU_LABEL_QUIT) + //.style("body") + .style("button_menu") + .attach(Grid::row(2)) + .attach(Grid::column(0)) + .attach(Grid::column_span(2)) + .icon(material_icons_font::MD_SETTINGS_POWER) + .on_mouse_down(move |_states, _| { + process::exit(0); + }) + // .on_key_down(move | ctx, "Ctlr+Q"| { + // process::exit(0); + // }) + .build(ctx), + ) + .child( + TextBlock::new() + .id(ID_POLICY_CHECK_MENU_SHORTCUT_QUIT) + .style("button_menu") + //.style("body") + .attach(Grid::row(2)) + .attach(Grid::column(2)) + .margin((0, 0, 16, 0)) + .h_align("end") + .v_align("center") + .text("CTRL+Q") + .build(ctx), + ) + .build(ctx), + ) + .build(ctx) + } + + /// Create a progress popup with update status of an onging data import + fn create_popup_progress(target: Entity, text: &str, ctx: &mut BuildContext<'_>) -> Entity { + Popup::new() + .id(ID_POLICY_CHECK_POPUP_PROGRESS) + .target(target) + .open(true) + //.style("popup_progress") + .width(280) + .height(100) .on_mouse_down(move |ctx, _| { - println!("on_click -> remove_progress_popup()"); - ctx.get_mut::(popup) - .action(Action::RemoveProgressPopup(popup)); + println!("on_click -> remove_popup_progress()"); + ctx.get_mut::(target) + .action(Action::RemovePopup(target)); true }) .child( Container::new() - //.background("transparent") - .border_radius(3.) - .border_width(2.) - .padding(16.) + .style("container_progress") .child( Stack::new() - .h_align("center") - .margin((16., 16., 16., 16.)) - .spacing(16.) - // default: .orientation("vertical") + .style("stack_progress") .child( TextBlock::new() .id(ID_POLICY_CHECK_PROGRESS_TEXT) - .h_align("center") - .v_align("top") + //.style("textblock_progress") + .font_size(12) .text(text) .build(ctx) ) .child( ProgressBar::new() .id(ID_POLICY_CHECK_PROGRESS_BAR) - .val(0.) - .width(250.) + .val(0) + //.width(250) .build(ctx) ) .child( TextBlock::new() .id(ID_POLICY_CHECK_PROGRESS_TIME) + //.style("textblock_progress") .h_align("end") - .v_align("bottom") - //.text() + .font_size(12) .build(ctx) ) .build(ctx)