Fiddle around with UI

This commit is contained in:
Sofia 2020-08-30 00:53:15 +03:00
parent 87caba2b33
commit 68b12d553b
11 changed files with 187 additions and 51 deletions

56
Cargo.lock generated
View File

@ -79,6 +79,12 @@ dependencies = [
"byteorder", "byteorder",
] ]
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.4.0" version = "3.4.0"
@ -187,6 +193,12 @@ version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
[[package]]
name = "libm"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.11" version = "0.4.11"
@ -219,6 +231,30 @@ dependencies = [
"webpki-roots", "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]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.43" version = "0.1.43"
@ -355,6 +391,16 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 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]] [[package]]
name = "syn" name = "syn"
version = "1.0.39" version = "1.0.39"
@ -373,7 +419,10 @@ dependencies = [
"argh", "argh",
"geo-types", "geo-types",
"gpx", "gpx",
"native-windows-derive",
"native-windows-gui",
"thingy-lib", "thingy-lib",
"toml",
] ]
[[package]] [[package]]
@ -383,7 +432,6 @@ dependencies = [
"chrono", "chrono",
"minreq", "minreq",
"serde", "serde",
"toml",
] ]
[[package]] [[package]]
@ -516,6 +564,12 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu", "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]] [[package]]
name = "winapi-i686-pc-windows-gnu" name = "winapi-i686-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"

View File

@ -9,6 +9,11 @@ argh = "0.1"
gpx = "0.8" gpx = "0.8"
geo-types = "*" geo-types = "*"
thingy-lib = { path = "./thingy_lib/" } 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] [target.x86_64-pc-windows-msvc]
rustflags = ["-Ctarget-feature=+crt-static"] rustflags = ["-Ctarget-feature=+crt-static"]

View File

@ -1,14 +1,16 @@
use super::args::*; pub mod args;
use super::errors::GenericError; 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::api::API;
use thingy_lib::chrono::NaiveDateTime; 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> { pub fn from_env(env: EnvOpt) -> Result<(), GenericError> {
match env.subcommand.unwrap() { match env.subcommand.unwrap() {
Subcommand::Between(opt) => { 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 mut api = API::new(config.clone());
let since = let since =
@ -33,11 +35,12 @@ pub fn from_env(env: EnvOpt) -> Result<(), GenericError> {
Ok(()) Ok(())
} }
Subcommand::Init(opt) => { 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(()) Ok(())
} }
Subcommand::Api(_) => { Subcommand::Api(_) => {
let config = Config::from_path(&env.config)?; let config = config_from_path(&env.config)?;
let api = API::new(config.clone()); let api = API::new(config.clone());
match thingy_lib::check_api(&api) { match thingy_lib::check_api(&api) {
Ok(_) => { Ok(_) => {
@ -51,13 +54,13 @@ pub fn from_env(env: EnvOpt) -> Result<(), GenericError> {
} }
} }
Subcommand::ShareToken(opt) => { Subcommand::ShareToken(opt) => {
let config = Config::from_path(&env.config)?; let config = config_from_path(&env.config)?;
let api = API::new(config.clone()); let api = API::new(config.clone());
println!("{}", thingy_lib::sharetoken(&api, opt.device)?); println!("{}", thingy_lib::sharetoken(&api, opt.device)?);
Ok(()) Ok(())
} }
Subcommand::Nick(opt) => { Subcommand::Nick(opt) => {
let config = Config::from_path(&env.config)?; let config = config_from_path(&env.config)?;
let mut api = API::new(config.clone()); let mut api = API::new(config.clone());
match opt.subcommand { match opt.subcommand {
@ -68,7 +71,7 @@ pub fn from_env(env: EnvOpt) -> Result<(), GenericError> {
} }
NickSub::Set(opt) => { NickSub::Set(opt) => {
thingy_lib::nick_set(&mut api, opt.device, opt.nickname)?; thingy_lib::nick_set(&mut api, opt.device, opt.nickname)?;
api.config.write_to(&env.config)?; write_to(&api.config, &env.config)?;
} }
} }
Ok(()) Ok(())
@ -76,7 +79,7 @@ pub fn from_env(env: EnvOpt) -> Result<(), GenericError> {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
Subcommand::Get(opt) => { Subcommand::Get(opt) => {
let config = Config::from_path(&env.config)?; let config = config_from_path(&env.config)?;
let api = API::new(config.clone()); let api = API::new(config.clone());
match opt.subcommand { match opt.subcommand {
GetSub::Tag(opt) => { GetSub::Tag(opt) => {

View File

@ -4,6 +4,9 @@ use std::fmt::{Display, Formatter};
pub enum GenericError { pub enum GenericError {
ThingyLibError(thingy_lib::Error), ThingyLibError(thingy_lib::Error),
GPXError(gpx::errors::Error), GPXError(gpx::errors::Error),
NWGError(nwg::NwgError),
IOError(std::io::Error),
TOMLError(toml::de::Error),
MessagedError(String, Option<Box<GenericError>>), MessagedError(String, Option<Box<GenericError>>),
} }
@ -19,11 +22,32 @@ impl From<gpx::errors::Error> for GenericError {
} }
} }
impl From<nwg::NwgError> for GenericError {
fn from(error: nwg::NwgError) -> Self {
GenericError::NWGError(error)
}
}
impl From<std::io::Error> for GenericError {
fn from(error: std::io::Error) -> Self {
GenericError::IOError(error)
}
}
impl From<toml::de::Error> for GenericError {
fn from(error: toml::de::Error) -> Self {
GenericError::TOMLError(error)
}
}
impl Display for GenericError { impl Display for GenericError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
let err = match self { let err = match self {
GenericError::ThingyLibError(e) => e.to_string(), GenericError::ThingyLibError(e) => e.to_string(),
GenericError::GPXError(e) => format!("GPX error: {}", e), 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!( GenericError::MessagedError(msg, err) => format!(
"{}\n {}", "{}\n {}",
msg, msg,

View File

@ -1,12 +1,22 @@
#![windows_subsystem = "windows"] #![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 cmd;
mod errors; mod errors;
mod gpx; mod gpx;
use args::*; #[cfg(target_os = "windows")]
use errors::GenericError; 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::api::{LocationModel, TagModel};
use thingy_lib::Config; use thingy_lib::Config;
@ -19,10 +29,28 @@ fn main() {
} }
} else { } else {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ if let Err(e) = ui::start_windows_ui(env) {
println!("Open nwg UI instead"); eprintln!("Critical Error: {}", e);
std::process::exit(1);
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
println!("non-windows ui not supported. Please run with --help"); println!("non-windows ui not supported. Please run with --help");
} }
} }
pub fn config_from_path(path: &PathBuf) -> Result<Config, GenericError> {
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(())
}

55
src/ui/mod.rs Normal file
View File

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

View File

@ -6,6 +6,5 @@ edition = "2018"
[dependencies] [dependencies]
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
toml = "0.5"
minreq = { version = "2.2.0", features = ["https", "json-using-serde"] } minreq = { version = "2.2.0", features = ["https", "json-using-serde"] }
chrono = "0.4" chrono = "0.4"

View File

@ -1,9 +1,5 @@
use super::errors::{LibError, MessagedError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config { pub struct Config {
@ -26,25 +22,6 @@ pub struct Config {
pub nicknames: HashMap<String, String>, pub nicknames: HashMap<String, String>,
} }
impl Config {
pub fn from_path(path: &PathBuf) -> Result<Config, LibError> {
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 { impl Default for Config {
fn default() -> Config { fn default() -> Config {
Config { Config {

View File

@ -4,7 +4,6 @@ use std::io;
#[derive(Debug)] #[derive(Debug)]
pub enum LibError { pub enum LibError {
TomlError(toml::de::Error),
YepzonServerError(ErrorModel), YepzonServerError(ErrorModel),
MinreqError(minreq::Error), MinreqError(minreq::Error),
ChronoParseError(chrono::ParseError), ChronoParseError(chrono::ParseError),
@ -13,12 +12,6 @@ pub enum LibError {
MessagedError(String, Option<Box<LibError>>), MessagedError(String, Option<Box<LibError>>),
} }
impl From<toml::de::Error> for LibError {
fn from(error: toml::de::Error) -> Self {
LibError::TomlError(error)
}
}
impl From<minreq::Error> for LibError { impl From<minreq::Error> for LibError {
fn from(error: minreq::Error) -> Self { fn from(error: minreq::Error) -> Self {
LibError::MinreqError(error) LibError::MinreqError(error)
@ -56,7 +49,6 @@ impl Display for LibError {
LibError::FromUTF8Error(e) => format!("UTF-8 error: {}", e), LibError::FromUTF8Error(e) => format!("UTF-8 error: {}", e),
LibError::IOError(e) => format!("IO error: {}", e), LibError::IOError(e) => format!("IO error: {}", e),
LibError::MinreqError(e) => format!("Network error: {}", e), LibError::MinreqError(e) => format!("Network error: {}", e),
LibError::TomlError(e) => format!("Toml error: {}", e),
LibError::YepzonServerError(e) => format!( LibError::YepzonServerError(e) => format!(
"Yepzon server error: {}", "Yepzon server error: {}",
e.message.as_ref().unwrap_or(&String::new()) e.message.as_ref().unwrap_or(&String::new())

View File

@ -7,7 +7,6 @@ use chrono::offset::Local;
use chrono::{NaiveDate, NaiveDateTime, NaiveTime, ParseError}; use chrono::{NaiveDate, NaiveDateTime, NaiveTime, ParseError};
use errors::LibError; use errors::LibError;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::PathBuf;
use std::sync::mpsc::TryRecvError; use std::sync::mpsc::TryRecvError;
use std::time::Duration; use std::time::Duration;
@ -16,12 +15,12 @@ pub use config::Config;
pub use errors::LibError as Error; pub use errors::LibError as Error;
pub use errors::MessagedError; pub use errors::MessagedError;
pub fn init(path: &PathBuf, api_key: Option<String>) -> Result<(), LibError> { pub fn init(api_key: Option<String>) -> Result<Config, LibError> {
let mut config = Config::default(); let mut config = Config::default();
if let Some(api_key) = api_key { if let Some(api_key) = api_key {
config.api_key = api_key; config.api_key = api_key;
} }
Ok(config.write_to(path)?) Ok(config)
} }
pub fn between( pub fn between(