From 68b12d553b0cf887c13259ab6a7d9450a4fa2767 Mon Sep 17 00:00:00 2001 From: Teascade Date: Sun, 30 Aug 2020 00:53:15 +0300 Subject: [PATCH] Fiddle around with UI --- Cargo.lock | 56 +++++++++++++++++++++++++++++++++++++- Cargo.toml | 5 ++++ src/{ => cmd}/args.rs | 0 src/{cmd.rs => cmd/mod.rs} | 23 +++++++++------- src/errors.rs | 24 ++++++++++++++++ src/main.rs | 38 ++++++++++++++++++++++---- src/ui/mod.rs | 55 +++++++++++++++++++++++++++++++++++++ thingy_lib/Cargo.toml | 1 - thingy_lib/src/config.rs | 23 ---------------- thingy_lib/src/errors.rs | 8 ------ thingy_lib/src/lib.rs | 5 ++-- 11 files changed, 187 insertions(+), 51 deletions(-) rename src/{ => cmd}/args.rs (100%) rename src/{cmd.rs => cmd/mod.rs} (84%) create mode 100644 src/ui/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 73680c6..c2daa22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + [[package]] name = "bumpalo" version = "3.4.0" @@ -187,6 +193,12 @@ version = "0.2.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + [[package]] name = "log" version = "0.4.11" @@ -219,6 +231,30 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "native-windows-derive" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39357a029a532c887fb1596e0f0498613d8dd119549d400569d61c048145ac6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "native-windows-gui" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6212fbf8ec1f512dffe75155513c017e1d05b5ecd08e03d3ebf4be07bd9f28b1" +dependencies = [ + "bitflags", + "lazy_static", + "stretch", + "winapi", + "winapi-build", +] + [[package]] name = "num-integer" version = "0.1.43" @@ -355,6 +391,16 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "stretch" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0dc6d20ce137f302edf90f9cd3d278866fd7fb139efca6f246161222ad6d87" +dependencies = [ + "lazy_static", + "libm", +] + [[package]] name = "syn" version = "1.0.39" @@ -373,7 +419,10 @@ dependencies = [ "argh", "geo-types", "gpx", + "native-windows-derive", + "native-windows-gui", "thingy-lib", + "toml", ] [[package]] @@ -383,7 +432,6 @@ dependencies = [ "chrono", "minreq", "serde", - "toml", ] [[package]] @@ -516,6 +564,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index c53afbb..0c984f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,11 @@ argh = "0.1" gpx = "0.8" geo-types = "*" thingy-lib = { path = "./thingy_lib/" } +toml = "0.5" + +[target.'cfg(windows)'.dependencies] +native-windows-gui = "1.0" +native-windows-derive = "1.0" [target.x86_64-pc-windows-msvc] rustflags = ["-Ctarget-feature=+crt-static"] diff --git a/src/args.rs b/src/cmd/args.rs similarity index 100% rename from src/args.rs rename to src/cmd/args.rs diff --git a/src/cmd.rs b/src/cmd/mod.rs similarity index 84% rename from src/cmd.rs rename to src/cmd/mod.rs index d15d43b..b3cad83 100644 --- a/src/cmd.rs +++ b/src/cmd/mod.rs @@ -1,14 +1,16 @@ -use super::args::*; +pub mod args; + use super::errors::GenericError; -use super::gpx; +use super::{config_from_path, gpx, write_to}; +use args::*; use thingy_lib::api::API; use thingy_lib::chrono::NaiveDateTime; -use thingy_lib::{try_get_datetime, Config, MessagedError}; +use thingy_lib::{try_get_datetime, MessagedError}; pub fn from_env(env: EnvOpt) -> Result<(), GenericError> { match env.subcommand.unwrap() { Subcommand::Between(opt) => { - let config = Config::from_path(&env.config)?; + let config = config_from_path(&env.config)?; let mut api = API::new(config.clone()); let since = @@ -33,11 +35,12 @@ pub fn from_env(env: EnvOpt) -> Result<(), GenericError> { Ok(()) } Subcommand::Init(opt) => { - thingy_lib::init(&env.config, opt.api_key)?; + let config = thingy_lib::init(opt.api_key)?; + write_to(&config, &env.config)?; Ok(()) } Subcommand::Api(_) => { - let config = Config::from_path(&env.config)?; + let config = config_from_path(&env.config)?; let api = API::new(config.clone()); match thingy_lib::check_api(&api) { Ok(_) => { @@ -51,13 +54,13 @@ pub fn from_env(env: EnvOpt) -> Result<(), GenericError> { } } Subcommand::ShareToken(opt) => { - let config = Config::from_path(&env.config)?; + let config = config_from_path(&env.config)?; let api = API::new(config.clone()); println!("{}", thingy_lib::sharetoken(&api, opt.device)?); Ok(()) } Subcommand::Nick(opt) => { - let config = Config::from_path(&env.config)?; + let config = config_from_path(&env.config)?; let mut api = API::new(config.clone()); match opt.subcommand { @@ -68,7 +71,7 @@ pub fn from_env(env: EnvOpt) -> Result<(), GenericError> { } NickSub::Set(opt) => { thingy_lib::nick_set(&mut api, opt.device, opt.nickname)?; - api.config.write_to(&env.config)?; + write_to(&api.config, &env.config)?; } } Ok(()) @@ -76,7 +79,7 @@ pub fn from_env(env: EnvOpt) -> Result<(), GenericError> { #[cfg(debug_assertions)] Subcommand::Get(opt) => { - let config = Config::from_path(&env.config)?; + let config = config_from_path(&env.config)?; let api = API::new(config.clone()); match opt.subcommand { GetSub::Tag(opt) => { diff --git a/src/errors.rs b/src/errors.rs index 9d5bfb9..220dbaf 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -4,6 +4,9 @@ use std::fmt::{Display, Formatter}; pub enum GenericError { ThingyLibError(thingy_lib::Error), GPXError(gpx::errors::Error), + NWGError(nwg::NwgError), + IOError(std::io::Error), + TOMLError(toml::de::Error), MessagedError(String, Option>), } @@ -19,11 +22,32 @@ impl From for GenericError { } } +impl From for GenericError { + fn from(error: nwg::NwgError) -> Self { + GenericError::NWGError(error) + } +} + +impl From for GenericError { + fn from(error: std::io::Error) -> Self { + GenericError::IOError(error) + } +} + +impl From for GenericError { + fn from(error: toml::de::Error) -> Self { + GenericError::TOMLError(error) + } +} + impl Display for GenericError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { let err = match self { GenericError::ThingyLibError(e) => e.to_string(), GenericError::GPXError(e) => format!("GPX error: {}", e), + GenericError::NWGError(e) => format!("UI error: {}", e), + GenericError::IOError(e) => format!("IO error: {}", e), + GenericError::TOMLError(e) => format!("toml error: {}", e), GenericError::MessagedError(msg, err) => format!( "{}\n {}", msg, diff --git a/src/main.rs b/src/main.rs index 3a9c5d6..885a484 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,22 @@ #![windows_subsystem = "windows"] -mod args; +#[cfg(target_os = "windows")] +extern crate native_windows_derive as nwd; +#[cfg(target_os = "windows")] +extern crate native_windows_gui as nwg; + mod cmd; mod errors; mod gpx; -use args::*; -use errors::GenericError; +#[cfg(target_os = "windows")] +mod ui; + +use cmd::args::EnvOpt; +use errors::{GenericError, MessagedError}; +use std::fs::File; +use std::io::prelude::*; +use std::path::PathBuf; use thingy_lib::api::{LocationModel, TagModel}; use thingy_lib::Config; @@ -19,10 +29,28 @@ fn main() { } } else { #[cfg(target_os = "windows")] - { - println!("Open nwg UI instead"); + if let Err(e) = ui::start_windows_ui(env) { + eprintln!("Critical Error: {}", e); + std::process::exit(1); } #[cfg(not(target_os = "windows"))] println!("non-windows ui not supported. Please run with --help"); } } + +pub fn config_from_path(path: &PathBuf) -> Result { + let mut file = + File::open(&path).with_msg(format!("Could not find {}", path.to_str().unwrap_or("")))?; + let mut string = String::new(); + file.read_to_string(&mut string) + .with_msg("config file is not valid UTF-8")?; + + Ok(toml::from_str(&string).with_msg("given config file is not a valid config file")?) +} + +pub fn write_to(config: &Config, path: &PathBuf) -> Result<(), GenericError> { + let mut file = File::create(&path).unwrap(); + file.write_all(&toml::to_vec(config).unwrap()) + .with_msg("Could not write config.toml, make sure you have correct permissions.")?; + Ok(()) +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs new file mode 100644 index 0000000..0226561 --- /dev/null +++ b/src/ui/mod.rs @@ -0,0 +1,55 @@ +use super::{Config, EnvOpt, GenericError}; + +use nwd::NwgUi; +use nwg::{Font, NativeUi}; + +#[derive(Default, NwgUi)] +pub struct APIKeyPrompt { + #[nwg_control(size: (500, 100), position: (300, 300), title: "API-key prompt", flags: "WINDOW|VISIBLE")] + #[nwg_events( OnWindowClose: [APIKeyPrompt::say_goodbye] )] + window: nwg::Window, + + #[nwg_layout(parent: window, spacing: 5)] + grid: nwg::GridLayout, + + #[nwg_control(text: "API-key")] + #[nwg_layout_item(layout: grid, col: 0, row: 0, col_span: 3)] + label: nwg::Label, + + #[nwg_control(text: "", focus: true)] + #[nwg_layout_item(layout: grid, col: 0, row: 1, col_span: 3)] + name_edit: nwg::TextInput, + + #[nwg_control(text: "Submit")] + #[nwg_layout_item(layout: grid, col: 1, row: 2)] + button: nwg::Button, +} + +impl APIKeyPrompt { + fn say_goodbye(&self) { + nwg::modal_info_message( + &self.window, + "Goodbye", + &format!("Goodbye {}", self.name_edit.text()), + ); + nwg::stop_thread_dispatch(); + } +} + +pub fn start_windows_ui(env: EnvOpt) -> Result<(), GenericError> { + nwg::init().expect("Failed to init Native Windows GUI"); + + let mut font = Font::default(); + + Font::builder() + .family("Segoe UI") + .size(20) + .build(&mut font)?; + + Font::set_global_default(Some(font)); + + let mut _app = APIKeyPrompt::build_ui(Default::default()).expect("Failed to build UI"); + nwg::dispatch_thread_events(); + + Ok(()) +} diff --git a/thingy_lib/Cargo.toml b/thingy_lib/Cargo.toml index 78567d8..39e2a46 100644 --- a/thingy_lib/Cargo.toml +++ b/thingy_lib/Cargo.toml @@ -6,6 +6,5 @@ edition = "2018" [dependencies] serde = { version = "1.0", features = ["derive"] } -toml = "0.5" minreq = { version = "2.2.0", features = ["https", "json-using-serde"] } chrono = "0.4" \ No newline at end of file diff --git a/thingy_lib/src/config.rs b/thingy_lib/src/config.rs index 0a50cb1..6091acb 100644 --- a/thingy_lib/src/config.rs +++ b/thingy_lib/src/config.rs @@ -1,9 +1,5 @@ -use super::errors::{LibError, MessagedError}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::fs::File; -use std::io::prelude::*; -use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { @@ -26,25 +22,6 @@ pub struct Config { pub nicknames: HashMap, } -impl Config { - pub fn from_path(path: &PathBuf) -> Result { - let mut file = File::open(&path) - .with_msg(format!("Could not find {}", path.to_str().unwrap_or("")))?; - let mut string = String::new(); - file.read_to_string(&mut string) - .with_msg("config file is not valid UTF-8")?; - - Ok(toml::from_str(&string).with_msg("given config file is not a valid config file")?) - } - - pub fn write_to(&self, path: &PathBuf) -> Result<(), LibError> { - let mut file = File::create(&path).unwrap(); - file.write_all(&toml::to_vec(self).unwrap()) - .with_msg("Could not write config.toml, make sure you have correct permissions.")?; - Ok(()) - } -} - impl Default for Config { fn default() -> Config { Config { diff --git a/thingy_lib/src/errors.rs b/thingy_lib/src/errors.rs index f3f2612..7518f19 100644 --- a/thingy_lib/src/errors.rs +++ b/thingy_lib/src/errors.rs @@ -4,7 +4,6 @@ use std::io; #[derive(Debug)] pub enum LibError { - TomlError(toml::de::Error), YepzonServerError(ErrorModel), MinreqError(minreq::Error), ChronoParseError(chrono::ParseError), @@ -13,12 +12,6 @@ pub enum LibError { MessagedError(String, Option>), } -impl From for LibError { - fn from(error: toml::de::Error) -> Self { - LibError::TomlError(error) - } -} - impl From for LibError { fn from(error: minreq::Error) -> Self { LibError::MinreqError(error) @@ -56,7 +49,6 @@ impl Display for LibError { LibError::FromUTF8Error(e) => format!("UTF-8 error: {}", e), LibError::IOError(e) => format!("IO error: {}", e), LibError::MinreqError(e) => format!("Network error: {}", e), - LibError::TomlError(e) => format!("Toml error: {}", e), LibError::YepzonServerError(e) => format!( "Yepzon server error: {}", e.message.as_ref().unwrap_or(&String::new()) diff --git a/thingy_lib/src/lib.rs b/thingy_lib/src/lib.rs index e3b31b8..8c34a69 100644 --- a/thingy_lib/src/lib.rs +++ b/thingy_lib/src/lib.rs @@ -7,7 +7,6 @@ use chrono::offset::Local; use chrono::{NaiveDate, NaiveDateTime, NaiveTime, ParseError}; use errors::LibError; use std::io::prelude::*; -use std::path::PathBuf; use std::sync::mpsc::TryRecvError; use std::time::Duration; @@ -16,12 +15,12 @@ pub use config::Config; pub use errors::LibError as Error; pub use errors::MessagedError; -pub fn init(path: &PathBuf, api_key: Option) -> Result<(), LibError> { +pub fn init(api_key: Option) -> Result { let mut config = Config::default(); if let Some(api_key) = api_key { config.api_key = api_key; } - Ok(config.write_to(path)?) + Ok(config) } pub fn between(