diff --git a/hejmorris.json b/hejmorris.json deleted file mode 100644 index 0b50148..0000000 --- a/hejmorris.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "plan": [ - { - "area_key": "1_1_1", - "notes": "" - }, - { - "area_key": "1_1_4_0", - "notes": "" - }, - { - "area_key": "1_1_2", - "notes": "" - }, - { - "area_key": "1_1_4_1", - "notes": "" - }, - { - "area_key": "1_1_6", - "notes": "" - }, - { - "area_key": "1_1_9", - "notes": "" - }, - { - "area_key": "1_1_3", - "notes": "" - }, - { - "area_key": "1_1_4_0", - "notes": "" - }, - { - "area_key": "1_2_4", - "notes": "" - }, - { - "area_key": "1_2_7", - "notes": "" - }, - { - "area_key": "1_2_9", - "notes": "{#ff00ff}(Hej morris)\n![The San Juan Mountains are beautiful!](https://mdg.imgix.net/assets/images/san-juan-mountains.jpg?auto=format&fit=clip&q=40&w=1080 \"San Juan Mountains\")\n\n{#ff00ff}(Hej morris)\n![The San Juan Mountains are beautiful!](https://mdg.imgix.net/assets/images/san-juan-mountains.jpg?auto=format&fit=clip&q=40&w=1080 \"San Juan Mountains\")" - }, - { - "area_key": "1_3_town", - "notes": "" - }, - { - "area_key": "1_3_3_1", - "notes": "" - }, - { - "area_key": "1_3_7", - "notes": "" - }, - { - "area_key": "1_3_9", - "notes": "" - }, - { - "area_key": "1_3_10_1", - "notes": "" - }, - { - "area_key": "1_3_9", - "notes": "" - }, - { - "area_key": "1_3_13", - "notes": "" - }, - { - "area_key": "1_3_14_1", - "notes": "" - }, - { - "area_key": "1_3_14_2", - "notes": "" - }, - { - "area_key": "1_3_8_2", - "notes": "" - }, - { - "area_key": "1_3_5", - "notes": "" - }, - { - "area_key": "1_3_town", - "notes": "" - }, - { - "area_key": "1_2_14_2", - "notes": "" - } - ], - "current": 0 -} \ No newline at end of file diff --git a/imageplan.json b/imageplan.json deleted file mode 100644 index 74a3fc2..0000000 --- a/imageplan.json +++ /dev/null @@ -1 +0,0 @@ -{"plan":[{"area_key":"1_1_town","notes":"{magenta}(Hey)\n![The San Juan Mountains are beautiful!](https://mdg.imgix.net/assets/images/san-juan-mountains.jpg?auto=format&fit=clip&q=40&w=1080 \"San Juan Mountains\")"},{"area_key":"1_1_1","notes":""},{"area_key":"1_1_2","notes":""}],"current":0} \ No newline at end of file diff --git a/licenses/material-icon b/licenses/material-icon new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/licenses/material-icon @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/nice.json b/nice.json deleted file mode 100644 index 3a7c97a..0000000 --- a/nice.json +++ /dev/null @@ -1 +0,0 @@ -{"plan":[{"area_key":"1_1_town","notes":""},{"area_key":"1_1_1","notes":"{#ff00ff}(hej how)\ntest\n![The San Juan Mountains are beautiful!](https://mdg.imgix.net/assets/images/san-juan-mountains.jpg?auto=format&fit=clip&q=40&w=1080 \"San Juan Mountains\")"},{"area_key":"1_1_2","notes":""}],"current":0} \ No newline at end of file diff --git a/package.json b/package.json index 0330387..8572ff7 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "fuzzr": "github:isark2/fuzzr#v0.3.1", "markdown-it": "^13.0.1", "markdown-it-color": "^2.1.1", + "material-icons": "^1.13.9", "natural-compare": "^1.4.0", "ngx-moveable": "^0.48.1", "rxjs": "~7.8.1", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 3cffd8b..175c5d6 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1834,12 +1834,46 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libappindicator" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2d3cb96d092b4824cb306c9e544c856a4cb6210c1081945187f7f1924b47e8" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b3b6681973cea8cc3bce7391e6d7d5502720b80a581c9a95c9cbaf592826aa" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "line-wrap" version = "0.1.1" @@ -3442,6 +3476,7 @@ dependencies = [ "core-foundation", "core-graphics", "crossbeam-channel", + "dirs-next", "dispatch", "gdk", "gdk-pixbuf", @@ -3456,6 +3491,7 @@ dependencies = [ "instant", "jni", "lazy_static", + "libappindicator", "libc", "log", "ndk", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index d8e94d2..18ba89f 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -19,7 +19,7 @@ ts-rs = "6.2.1" [dependencies] -tauri = { version = "1.2", features = [ "api-all"] } +tauri = { version = "1.2", features = [ "system-tray", "api-all"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" Underlayer = { git = "https://git.isark.me/isark/Underlay.git" } diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png deleted file mode 100644 index 6be5e50..0000000 Binary files a/src-tauri/icons/128x128.png and /dev/null differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png deleted file mode 100644 index e81bece..0000000 Binary files a/src-tauri/icons/128x128@2x.png and /dev/null differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png index a437dd5..2d06108 100644 Binary files a/src-tauri/icons/32x32.png and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/NothingIcon.png b/src-tauri/icons/NothingIcon.png new file mode 100644 index 0000000..2d06108 Binary files /dev/null and b/src-tauri/icons/NothingIcon.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png deleted file mode 100644 index 0ca4f27..0000000 Binary files a/src-tauri/icons/Square107x107Logo.png and /dev/null differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png deleted file mode 100644 index b81f820..0000000 Binary files a/src-tauri/icons/Square142x142Logo.png and /dev/null differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png deleted file mode 100644 index 624c7bf..0000000 Binary files a/src-tauri/icons/Square150x150Logo.png and /dev/null differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png deleted file mode 100644 index c021d2b..0000000 Binary files a/src-tauri/icons/Square284x284Logo.png and /dev/null differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png deleted file mode 100644 index 6219700..0000000 Binary files a/src-tauri/icons/Square30x30Logo.png and /dev/null differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png deleted file mode 100644 index f9bc048..0000000 Binary files a/src-tauri/icons/Square310x310Logo.png and /dev/null differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png deleted file mode 100644 index d5fbfb2..0000000 Binary files a/src-tauri/icons/Square44x44Logo.png and /dev/null differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png deleted file mode 100644 index 63440d7..0000000 Binary files a/src-tauri/icons/Square71x71Logo.png and /dev/null differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png deleted file mode 100644 index f3f705a..0000000 Binary files a/src-tauri/icons/Square89x89Logo.png and /dev/null differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png deleted file mode 100644 index 4556388..0000000 Binary files a/src-tauri/icons/StoreLogo.png and /dev/null differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns deleted file mode 100644 index 12a5bce..0000000 Binary files a/src-tauri/icons/icon.icns and /dev/null differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico deleted file mode 100644 index b3636e4..0000000 Binary files a/src-tauri/icons/icon.ico and /dev/null differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png deleted file mode 100644 index e1cd261..0000000 Binary files a/src-tauri/icons/icon.png and /dev/null differ diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 7a2c1b7..4c6cbd2 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -19,9 +19,11 @@ pub struct Rect { #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct Config { - initial_plan_window_position: Rect, - hide_on_unfocus: bool, - toggle_overlay: String, + pub initial_plan_window_position: Rect, + pub hide_on_unfocus: bool, + pub toggle_overlay: String, + pub plan_bg: String, + pub backdrop_bg: String, } impl Default for Config { @@ -30,6 +32,8 @@ impl Default for Config { initial_plan_window_position: Default::default(), hide_on_unfocus: true, toggle_overlay: "F6".into(), + plan_bg: "#00000010".to_string(), + backdrop_bg: "#00000030".to_string(), } } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 9371ac1..5a9a955 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -11,8 +11,15 @@ use plan::Plan; use poe_data::world_area::WorldAreasMap; use simple_logger::SimpleLogger; use storage::Storage; +use tauri::AppHandle; +use tauri::CustomMenuItem; use tauri::Manager; +use tauri::SystemTray; +use tauri::SystemTrayEvent; +use tauri::SystemTrayMenu; +use tauri::SystemTrayMenuItem; + mod config; mod overlay; mod plan; @@ -28,14 +35,6 @@ fn set_interactable(interactable: bool, state: tauri::State>) { } } -#[tauri::command] -fn set_auto_hide(auto_hide: bool, state: tauri::State) { - log::info!("set_auto_hide: {auto_hide:?}"); - if let Ok(mut settings) = state.lock() { - settings.auto_hide = auto_hide; - } -} - #[tauri::command] fn load_world_areas() -> WorldAreasMap { log::info!("Loading world areas"); @@ -50,11 +49,24 @@ fn load_config(state: tauri::State>) -> Option { } #[tauri::command] -fn set_config(config: Config, state: tauri::State>) { +fn set_config( + config: Config, + state: tauri::State>, + window: tauri::Window, + app: AppHandle, +) { log::info!("Saved config: {:?}", config); if let Ok(mut storage) = state.lock() { - storage.config = config; + storage.config = config.clone(); storage.save(); + + let other_window = if window.label() == "Overlay" { + "Normal" + } else { + "Overlay" + }; + //Ignore failures! woohoo + // app.emit_to(other_window, "config", config).ok(); } } @@ -72,11 +84,9 @@ fn load_plan(path: PathBuf, state: tauri::State>) -> Option } #[tauri::command] -fn save_plan(path: PathBuf, plan: Plan, state: tauri::State>) -> bool { - if let Ok(storage) = state.lock() { - if let Some(path_string) = path.with_extension("json").to_str() { - return Storage::save_plan(path_string, plan).is_ok() - } +fn save_plan(path: PathBuf, plan: Plan) -> bool { + if let Some(path_string) = path.with_extension("json").to_str() { + return Storage::save_plan(path_string, plan).is_ok(); } false @@ -94,6 +104,16 @@ fn main() { .init() .expect("Could not init logger"); + let settings = CustomMenuItem::new("settings".to_string(), "Settings"); + let editor = CustomMenuItem::new("editor".to_string(), "Plan Editor"); + let exit = CustomMenuItem::new("exit".to_string(), "Exit"); + let tray_menu = SystemTrayMenu::new() + .add_item(settings) + .add_item(editor) + .add_native_item(SystemTrayMenuItem::Separator) + .add_item(exit); + let system_tray = SystemTray::new().with_menu(tray_menu); + tauri::Builder::default() .setup(|app| { let tx = Overlay::initialize( @@ -112,7 +132,6 @@ fn main() { }) .invoke_handler(tauri::generate_handler![ set_interactable, - set_auto_hide, load_world_areas, load_config, set_config, @@ -120,6 +139,21 @@ fn main() { load_stored_plans, save_plan ]) + .system_tray(system_tray) + .on_system_tray_event(|app, event| match event { + SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() { + "exit" => { + std::process::exit(0); + } + "editor" | "settings" => { + if let Some(window) = app.get_window("Normal") { + window.show().ok(); + } + } + _ => {} + }, + _ => {} + }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/src/overlay.rs b/src-tauri/src/overlay.rs index 2616987..8beeedf 100644 --- a/src-tauri/src/overlay.rs +++ b/src-tauri/src/overlay.rs @@ -8,6 +8,8 @@ use std::sync::{mpsc::Receiver as MpscReceiver, Mutex}; use tauri::{Manager, PhysicalPosition, PhysicalSize, Window}; use underlayer::{Bounds, UnderlayEvent}; +use crate::storage::Storage; + impl From for Event { fn from(value: State) -> Self { Self::State(value) @@ -35,7 +37,7 @@ pub struct Overlay { } pub struct OverlayData { - pub auto_hide: bool, + // pub auto_hide: bool, } pub type LockedOverlayData = Mutex; @@ -57,7 +59,7 @@ impl Overlay { previous: State::hidden(), }; - window.manage(Mutex::new(OverlayData { auto_hide: true })); + window.manage(Mutex::new(OverlayData { })); let mut fsm = Overlay::uninitialized_state_machine(overlay).init(); @@ -133,9 +135,9 @@ impl Overlay { if !fsm.window.is_focused().unwrap() && fsm .window - .state::() + .state::>() .lock() - .is_ok_and(|s| s.auto_hide) + .is_ok_and(|s| s.config.hide_on_unfocus) { fsm.handle(&State::Hidden {}.into()) } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index deec0d4..6ea9912 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -11,6 +11,10 @@ "version": "0.0.0" }, "tauri": { + "systemTray": { + "iconPath": "icons/NothingIcon.png", + "iconAsTemplate": false + }, "allowlist": { "all": true, "shell": { @@ -22,10 +26,7 @@ "active": true, "icon": [ "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" + "icons/NothingIcon.png" ], "identifier": "me.isark.poe.Nothing", "targets": "all" @@ -38,14 +39,23 @@ }, "windows": [ { - "title": "Nothing", "label": "Overlay", + "title": "Nothing", "transparent": true, "visible": false, "decorations": false, "alwaysOnTop": true, "resizable": false, "focus": false + }, + { + "label": "Normal", + "title": "Nothing", + "transparent": false, + "visible": false, + "decorations": true, + "resizable": true, + "focus": false } ] } diff --git a/src/app/app.component.html b/src/app/app.component.html index 6b5908a..9109b1b 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,15 +1,23 @@ - -
- Plan window background - Overlay backdrop color -
- -
- - -
+
+ + + + Settings + + + + + + Editor + + + +
+ + -
- +
+
- \ No newline at end of file + \ No newline at end of file diff --git a/src/app/app.component.scss b/src/app/app.component.scss index eb2147f..7860465 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -7,9 +7,37 @@ plan-display { display: flex; max-height: 100vh; max-width: 100vw; + height: 100vh; + width: 100vw; } .editor { max-height: 100%; overflow-y: hidden; +} + +plan-display { + position: absolute; + width: 100vw; + height: 100vh; + pointer-events: none; +} + +.main-container { + flex: 1 1 auto; +} + +.standalone { + color: black; + +} + +.tab-label { + color: black; +} + +.content { + display: flex; + flex-direction: column; + justify-content: center; } \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 8f2d2ab..41096ee 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from "@angular/core"; +import { Component, NgZone, OnDestroy, OnInit } from "@angular/core"; import { invoke } from "@tauri-apps/api/tauri"; import { ShortcutService } from "./services/shortcut.service"; import { EventsService } from "./services/events.service"; @@ -7,8 +7,8 @@ import { Color } from "./color-picker/color-picker.component"; import { OverlayService } from "./services/overlay.service"; import { ConfigService } from "./services/config.service"; import { PlanService } from "./services/plan.service"; -import { open } from '@tauri-apps/api/dialog'; import { from } from "rxjs"; +import { appWindow } from "@tauri-apps/api/window" @Component({ selector: "app-root", @@ -19,37 +19,20 @@ export class AppComponent implements OnInit { auto_hide: boolean = true; interactable: boolean = false; isBinding: boolean = false; - planColor?: Color; - backdrop?: Color; - + isOverlay: boolean; + overlayShowSettings: boolean = false; constructor( public overlayService: OverlayService, public worldAreas: WorldAreaService, public planService: PlanService, public configService: ConfigService, - ) { } - - openDialog() { - from(open({ - multiple: false, - filters: [ - // { - // name: "Plan Files", - // extensions: ['PPF'] - // } - ] - })).subscribe(file => { - if (file) { - this.planService.loadPlan(file as string); - } - }); + private zone: NgZone, + ) { + this.isOverlay = appWindow.label === "Overlay"; } - ngOnInit(): void { } - - } \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 36ddcf4..e47c9c4 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -7,10 +7,12 @@ import { FormsModule } from "@angular/forms"; import { RecordKeyChord } from "./directives/record-key-chord.directive"; import { PlanDisplayModule } from "./plan-display/plan-display.module"; import { ConfigService } from "./services/config.service"; -import { ColorPickerComponent } from './color-picker/color-picker.component'; import { MatButtonModule } from "@angular/material/button"; import { EditorComponent } from "./editor/editor.component"; -import { NotesComponent } from './editor/notes/notes.component'; +import { AngularResizeEventModule } from "angular-resize-event"; +import { OverlayModule } from "@angular/cdk/overlay"; +import { SettingsComponent } from "./settings/settings.component"; +import {MatTabsModule} from '@angular/material/tabs'; export function initializeApp(configService: ConfigService) { return (): Promise => { @@ -21,7 +23,7 @@ export function initializeApp(configService: ConfigService) { @NgModule({ declarations: [ AppComponent, - RecordKeyChord, ColorPickerComponent, + RecordKeyChord, ], imports: [ BrowserModule, @@ -30,6 +32,10 @@ export function initializeApp(configService: ConfigService) { MatButtonModule, BrowserAnimationsModule, EditorComponent, + AngularResizeEventModule, + OverlayModule, + SettingsComponent, + MatTabsModule ], providers: [ { diff --git a/src/app/color-picker/color-picker.component.ts b/src/app/color-picker/color-picker.component.ts index 6b7e76c..130a829 100644 --- a/src/app/color-picker/color-picker.component.ts +++ b/src/app/color-picker/color-picker.component.ts @@ -2,6 +2,7 @@ import { AfterContentInit, AfterViewInit, Component, ElementRef, EventEmitter, I import Picker from 'vanilla-picker'; import { MatButtonModule } from '@angular/material/button'; import { Subject, delay, startWith } from 'rxjs'; +import { CommonModule } from '@angular/common'; export interface Color { rgba: number[]; @@ -17,6 +18,11 @@ export interface Color { selector: 'color-picker', templateUrl: './color-picker.component.html', styleUrls: ['./color-picker.component.scss'], + imports: [ + CommonModule, + MatButtonModule + ], + standalone: true }) export class ColorPickerComponent implements AfterViewInit { @@ -26,7 +32,16 @@ export class ColorPickerComponent implements AfterViewInit { @Output('color') outColor = new EventEmitter; - @Input('initialColor') + justSet: boolean = false; + + @Input('setColor') set color(value: string) { + this.justSet = true; + if(this.picker) { + this.picker.setColor(value, false); + } + this.initialColor = value; + } + initialColor?: string; loaded: Subject = new Subject(); @@ -34,10 +49,18 @@ export class ColorPickerComponent implements AfterViewInit { picker?: Picker; showPicker: boolean = false; + first: boolean = true; + constructor() { this.loaded.pipe(delay(0)).subscribe(() => { this.picker = new Picker({ - onChange: this.outColor.next.bind(this.outColor), + onChange: (color: Color) => { + if (this.justSet) { + this.justSet = false; + return; + } + return this.outColor.next(color); + }, parent: this.clickColorPickerElement?.nativeElement, color: this.initialColor }); diff --git a/src/app/editor/editor.component.html b/src/app/editor/editor.component.html index 0afbdb3..8349b2c 100644 --- a/src/app/editor/editor.component.html +++ b/src/app/editor/editor.component.html @@ -1,9 +1,9 @@ +
- - CHOSEN FILTER ACT {{filterAct.name}} - + Act filter

Campaign zones

@@ -20,8 +20,8 @@ Auto scroll to end on add to end Reverse display: - - + Act filter

Plan

@@ -32,6 +32,7 @@
Act {{areasMap?.get(item.area_key)?.act}}
#{{planIndexOf(item)}}
+
+
diff --git a/src/app/editor/editor.component.scss b/src/app/editor/editor.component.scss index ee2cdc4..cab2401 100644 --- a/src/app/editor/editor.component.scss +++ b/src/app/editor/editor.component.scss @@ -48,6 +48,26 @@ } } +.delete { + display: flex; + justify-content: center; + align-items: center; + + transform: rotate(45deg); + font-size: 2em; + color: darken(red, 10%); + position: absolute; + top: 0px; + right: 0px; + width: 15px; + height: 15px; + overflow: visible; + + &:hover { + cursor: pointer; + } +} + .cdk-drag-preview { box-sizing: border-box; diff --git a/src/app/editor/editor.component.ts b/src/app/editor/editor.component.ts index 9b12478..a2140c1 100644 --- a/src/app/editor/editor.component.ts +++ b/src/app/editor/editor.component.ts @@ -19,6 +19,7 @@ import { save } from '@tauri-apps/api/dialog'; import { PlanService } from '../services/plan.service'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { EditNotesComponentDialog } from './notes/notes.component'; +import { open } from '@tauri-apps/api/dialog'; interface Act { value: number; @@ -40,6 +41,7 @@ interface Act { ], }) export class EditorComponent implements OnInit { + areas?: WorldArea[]; planAreas: WorldArea[]; plan: Plan; @@ -119,6 +121,11 @@ export class EditorComponent implements OnInit { } } + remove(item: PlanElement) { + console.log("delete"); + this.plan.plan.splice(this.planIndexOf(item), 1); + } + canDrop = () => { return !this.disabledPlanDD; } @@ -162,7 +169,7 @@ export class EditorComponent implements OnInit { if (this.planSearchString !== "" || this.planFilterAct.value != 0) { this.disabledPlanDD = true; return this.planFuzzer.search(this.planSearchString).map(({ item }) => item).filter(item => { - return this.areasMap?.get(item.area_key)?.act == this.planFilterAct.value || this.planFilterAct.value == 0; + return this.areasMap?.get(item.area_key)?.act === this.planFilterAct.value || this.planFilterAct.value === 0; }); } else { this.disabledPlanDD = false; @@ -194,10 +201,29 @@ export class EditorComponent implements OnInit { extensions: ['json'] }] })).subscribe(file => { - if (file) { - this.planService.savePlan(file as string, this.plan); + if (file) { + this.planService.savePlan(file as string, this.plan); + } + }); + } + + openPlan() { + from(open({ + multiple: false, + filters: [ + { + name: "JSON (.json)", + extensions: ['json'] } - }); + ] + })).subscribe(file => { + if (file) { + this.planService.loadPlanNoSave(file as string).subscribe(plan => { + this.plan.plan.length = 0; + plan.plan.forEach(p => this.plan.plan.push(p)); + }); + } + }); } addNote(event: MouseEvent, item: PlanElement) { @@ -211,7 +237,9 @@ export class EditorComponent implements OnInit { }) dialogRef.afterClosed().subscribe(note => { - item.notes = note; + if(note) { + item.notes = note; + } }) } diff --git a/src/app/plan-display/plan-display.component.html b/src/app/plan-display/plan-display.component.html index 5a7637d..91b891d 100644 --- a/src/app/plan-display/plan-display.component.html +++ b/src/app/plan-display/plan-display.component.html @@ -1,28 +1,43 @@ - -
- + +
+ +
+ - - -
{{worldAreaMap!.get(slide.area_key)!.name}}
-
-
- - - - - + + +
{{worldAreaMap!.get(slide.area_key)!.name}}
+
+
+ + + + + -
-
+
+ +
-
- - -
+ - +
+ + +
+
+
+ +
+
+ + +
+
\ No newline at end of file diff --git a/src/app/plan-display/plan-display.component.scss b/src/app/plan-display/plan-display.component.scss index 69ee46c..d878431 100644 --- a/src/app/plan-display/plan-display.component.scss +++ b/src/app/plan-display/plan-display.component.scss @@ -1,6 +1,10 @@ :host { height: 0; width: 0; + + & * { + pointer-events: auto; + } } .target { @@ -8,8 +12,11 @@ min-height: 50px; display: flex; flex-direction: column; - & > * { + user-select: none; + + &>* { flex: 1 1 auto; + &:first-child { flex: 0 0 auto; max-height: 60px; @@ -17,9 +24,7 @@ } } -.zone-slide { - -} +.zone-slide {} .controls { position: absolute; @@ -30,5 +35,47 @@ notes { display: block; height: 100%; max-height: 100%; - width: 100%;; + width: 100%; + ; +} + +.overlay { + display: grid; + grid-template-columns: 1fr 2fr; + width: 100vw; + height: 100vh; +} + +.settings-button { + position: absolute; + top: 0; + right: 0; +} + + +.exit { + transform: rotate(45deg); + position: absolute; + top: 10px; + right: 10px; + background-color: none; + display: flex; + align-items: center; + justify-content: center; + align-content: center; + + + & span { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + } +} + +.overlay { + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.5); } \ No newline at end of file diff --git a/src/app/plan-display/plan-display.component.ts b/src/app/plan-display/plan-display.component.ts index 3f497ee..e777d80 100644 --- a/src/app/plan-display/plan-display.component.ts +++ b/src/app/plan-display/plan-display.component.ts @@ -12,6 +12,10 @@ import { WorldAreaService } from '../services/world-area.service'; import { WorldArea } from '../models/world-area'; import { NotesComponent } from '../editor/notes/notes.component'; import { ResizedEvent } from 'angular-resize-event'; +import { from } from 'rxjs'; +import { open } from '@tauri-apps/api/dialog'; +import { OverlayRef } from '@angular/cdk/overlay'; +import { OverlayService } from '../services/overlay.service'; @Component({ selector: 'plan-display', @@ -19,27 +23,37 @@ import { ResizedEvent } from 'angular-resize-event'; styleUrls: ['./plan-display.component.scss'] }) export class PlanDisplayComponent implements AfterViewInit, OnInit { - @Input() backgroundColor?: Color; + @Input() backgroundColor?: String; draggable: boolean = true; rect?: Rect; + bounds: any = { "left": 0, "top": 0, "right": 0, "bottom": 0, "position": "css" }; // slides: PlanElement[] = []; - plan?: Plan; slideIndex: number = 0; zoneSlides?: CarouselComponent; currentSlides?: CarouselComponent; worldAreaMap?: Map; + settingsOpen: boolean = false; + init: boolean = false; - - constructor(private configService: ConfigService, private cdr: ChangeDetectorRef, private shortcut: ShortcutService, private planService: PlanService, public worldAreaService: WorldAreaService) { + constructor(private configService: ConfigService, private cdr: ChangeDetectorRef, private shortcut: ShortcutService, public planService: PlanService, public worldAreaService: WorldAreaService, public overlayService: OverlayService) { // for (let i = 0; i < 100; i++) { // this.slides.push(i); // } + window.addEventListener("resize", this.windowInitHandler.bind(this)); + overlayService.setInteractable(); + } + + windowInitHandler() { + if (window.innerWidth > 0) { + this.ngAfterViewInit(); + window.removeEventListener("resize", this.windowInitHandler.bind(this)); + } } ngOnInit() { - this.plan = this.planService.currentPlan; this.worldAreaService.getWorldAreas().subscribe(a => this.worldAreaMap = a); + } abs(v: number) { @@ -47,6 +61,7 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { } transform() { + console.log("transform:", `translate(${this.rect!.x}px, ${this.rect!.y}px)`, "rect", this.rect); return `translate(${this.rect!.x}px, ${this.rect!.y}px)`; } @@ -59,15 +74,22 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { } ngAfterViewInit(): void { - const cfgRect = this.configService.config.initialPlanWindowPosition; - this.rect = { - x: cfgRect.x * window.innerWidth, - y: cfgRect.y * window.innerHeight, - width: cfgRect.width * window.innerWidth, - height: cfgRect.height * window.innerHeight, + if (window.innerWidth > 0) { + const cfgRect = this.configService.config.initialPlanWindowPosition; + console.log("cfgrect", cfgRect); + console.log("window res", window.innerWidth); + console.log("window res", window.innerHeight); + this.rect = { + x: cfgRect.x * window.innerWidth, + y: cfgRect.y * window.innerHeight, + width: cfgRect.width * window.innerWidth, + height: cfgRect.height * window.innerHeight, + } + console.log("rect", this.rect); + + setTimeout(() => this.cdr.detectChanges(), 0); + this.init = true; } - - setTimeout(() => this.cdr.detectChanges(), 0); } onDrag(e: OnDrag) { @@ -127,7 +149,7 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { let bounds = noteSlide.ref.nativeElement.getBoundingClientRect(); const children = noteSlide.ref.nativeElement.children; - + let sumWidth = 0; let sumHeight = 0; for (let child of children) { @@ -143,4 +165,24 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { noteSlide.ref.nativeElement.style.transform = `scale(1, ${scale})`; } + + openDialog() { + from(open({ + multiple: false, + filters: [ + { + name: "JSON (.json)", + extensions: ['json'] + } + ] + })).subscribe(file => { + if (file) { + this.planService.loadPlan(file as string).subscribe(plan => { + if (plan) { + this.settingsOpen = false; + } + }); + } + }); + } } diff --git a/src/app/plan-display/plan-display.module.ts b/src/app/plan-display/plan-display.module.ts index bc51fd7..7d63013 100644 --- a/src/app/plan-display/plan-display.module.ts +++ b/src/app/plan-display/plan-display.module.ts @@ -6,7 +6,10 @@ import { CarouselComponent } from '../carousel/carousel.component'; import { FormsModule } from '@angular/forms'; import { NotesComponent } from '../editor/notes/notes.component'; import { AngularResizeEventModule } from 'angular-resize-event'; - +import { SettingsComponent } from '../settings/settings.component'; +import { OverlayModule } from '@angular/cdk/overlay'; +import {MatIconModule} from '@angular/material/icon'; +import { MatButtonModule } from '@angular/material/button'; @NgModule({ declarations: [ @@ -19,6 +22,11 @@ import { AngularResizeEventModule } from 'angular-resize-event'; FormsModule, NotesComponent, AngularResizeEventModule, + SettingsComponent, + OverlayModule, + MatIconModule, + MatButtonModule, + MatIconModule ], exports: [ PlanDisplayComponent diff --git a/src/app/services/config.service.ts b/src/app/services/config.service.ts index 00b0bf7..ec10215 100644 --- a/src/app/services/config.service.ts +++ b/src/app/services/config.service.ts @@ -1,35 +1,48 @@ import { Injectable } from '@angular/core'; import { Config } from '../models/generated/Config'; -import { invoke } from '@tauri-apps/api'; -import { Subject, debounceTime } from 'rxjs'; +import { app, invoke } from '@tauri-apps/api'; +import { Subject, debounceTime, from } from 'rxjs'; +import { EventsService } from './events.service'; +import { appWindow, WebviewWindow } from '@tauri-apps/api/window' @Injectable({ providedIn: 'root' }) export class ConfigService { //We're making sure this is loaded initially so let's not indicate it being nullable. - config!: Config; + private cfg!: Config; + private proxied!: Config; private syncSubject: Subject = new Subject(); - - constructor() { + + constructor(private events: EventsService) { // Let's do some premature optimization! Now we can do lots of config changes quickly from FE without as many sync calls :D - this.syncSubject.pipe(debounceTime(500)).subscribe(() => this.underlyingSync()); + this.syncSubject.pipe(debounceTime(100)).subscribe(() => this.underlyingSync()); + events.listen("config").subscribe(cfg => { + console.log("Got new config"); + Object.assign(this.cfg, cfg.payload); + // this.cfg = cfg.payload; + } + ); + } + + get config() { + return this.proxied; } init() { return invoke('load_config').then(cfg => { - if(!cfg) { + if (!cfg) { console.error("Could not load config, should generally not happen :')"); return; } const _this = this; + this.cfg = cfg; // Mostly to wrap setters so we can run the (debounced) sync function on any config changes! - this.config = new Proxy(cfg, { + this.proxied = new Proxy(this.cfg, { get(target: any, property) { return target[property as keyof Config]; }, - set(target: Config, property:any , value) { - // target[property as keyof Config] = value; + set(target: Config, property: any, value) { setConfig(target, property, value); _this.sync(); return true; @@ -40,6 +53,20 @@ export class ConfigService { } sync() { + let target = "Normal" + + if(appWindow.label == "Normal") { + target = "Overlay"; + } + + const targetWindow = WebviewWindow.getByLabel(target); + if (targetWindow) { + from(targetWindow.isVisible()).subscribe(visible => { + if (visible) { + targetWindow?.emit("config", this.cfg); + } + }); + } this.syncSubject.next(); } diff --git a/src/app/services/overlay.service.ts b/src/app/services/overlay.service.ts index 797febb..a03f0c4 100644 --- a/src/app/services/overlay.service.ts +++ b/src/app/services/overlay.service.ts @@ -20,7 +20,6 @@ export class OverlayService { isBinding: boolean = false; visible: boolean = false; - constructor(private shortcuts: ShortcutService, private events: EventsService, private configService: ConfigService) { this.shortcuts.register(this.configService.config.toggleOverlay, this.onToggleOverlay.bind(this)); this.events.listen("OverlayStateChange").subscribe(this.onOverlayStateChange.bind(this)); @@ -29,8 +28,10 @@ export class OverlayService { onOverlayStateChange(event: Event) { this.interactable = event.payload.Interactable != null; if (event.payload.Hidden) {this.visible = false} else {this.visible = true}; - invoke("set_interactable", { interactable: true }).then(); + } + setInteractable() { + invoke("set_interactable", { interactable: true }).then(); } onToggleOverlay() { diff --git a/src/app/services/plan.service.ts b/src/app/services/plan.service.ts index 01b2d11..b8590d5 100644 --- a/src/app/services/plan.service.ts +++ b/src/app/services/plan.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { invoke } from '@tauri-apps/api'; -import { from } from 'rxjs'; +import { from, map } from 'rxjs'; import { Plan } from '../models/plan'; @Injectable({ @@ -16,17 +16,24 @@ export class PlanService { } loadPlan(path: string) { - from(invoke('load_plan', {path})).subscribe(plan => { - console.log("got plan: ", plan); - this.currentPlan = plan; - }); + return from(invoke('load_plan', { path })).pipe( + map(plan => { + this.currentPlan = plan; + return plan + }) + ); + } + + loadPlanNoSave(path: string) { + return from(invoke('load_plan', { path })).pipe( + ); } savePlan(path: string, plan: Plan) { plan.plan.forEach(elem => { - if(!elem.notes) {elem.notes = ""} + if (!elem.notes) { elem.notes = "" } }) - return from(invoke('save_plan', {path, plan})).subscribe(status => { + return from(invoke('save_plan', { path, plan })).subscribe(status => { console.log("save plan", status); }); } diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html new file mode 100644 index 0000000..a8e793f --- /dev/null +++ b/src/app/settings/settings.component.html @@ -0,0 +1,13 @@ +
+
+ Plan window + background + Overlay + backdrop color +
+
+ + Auto hide on unfocus +
+ +
\ No newline at end of file diff --git a/src/app/settings/settings.component.scss b/src/app/settings/settings.component.scss new file mode 100644 index 0000000..29bdce3 --- /dev/null +++ b/src/app/settings/settings.component.scss @@ -0,0 +1,9 @@ +:host { + display: block; + width: fit-content; +} + +.checkboxes { + display: grid; + grid-template-columns: 1fr 10fr; +} \ No newline at end of file diff --git a/src/app/settings/settings.component.spec.ts b/src/app/settings/settings.component.spec.ts new file mode 100644 index 0000000..0529051 --- /dev/null +++ b/src/app/settings/settings.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SettingsComponent } from './settings.component'; + +describe('SettingsComponent', () => { + let component: SettingsComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [SettingsComponent] + }); + fixture = TestBed.createComponent(SettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts new file mode 100644 index 0000000..e397eb1 --- /dev/null +++ b/src/app/settings/settings.component.ts @@ -0,0 +1,34 @@ +import { Component, Input, NgZone } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { ConfigService } from '../services/config.service'; +import { Color, ColorPickerComponent } from '../color-picker/color-picker.component'; +import {MatSlideToggleModule} from '@angular/material/slide-toggle'; + +@Component({ + selector: 'settings', + standalone: true, + imports: [CommonModule, FormsModule, ColorPickerComponent, MatSlideToggleModule], + templateUrl: './settings.component.html', + styleUrls: ['./settings.component.scss'] +}) +export class SettingsComponent { + @Input() + overlay: boolean = false; + + constructor( + public configService: ConfigService, + private zone: NgZone + ) { + + } + + onPlanColorChange(color: Color) { + this.zone.run(() => this.configService.config.planBg = color.rgbaString); + + } + + onBackdropColorChange(color: Color) { + this.zone.run(() => this.configService.config.backdropBg = color.rgbaString); + } +} diff --git a/src/assets/material-settings.svg b/src/assets/material-settings.svg new file mode 100644 index 0000000..a292720 --- /dev/null +++ b/src/assets/material-settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 9efdaaa..d2c9bdf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5166,6 +5166,11 @@ markdown-it@^13.0.1: mdurl "^1.0.1" uc.micro "^1.0.5" +material-icons@^1.13.9: + version "1.13.9" + resolved "https://registry.yarnpkg.com/material-icons/-/material-icons-1.13.9.tgz#15dbe8895111ae2cca9a55871cdcd30a6783e06a" + integrity sha512-dwuf2C8LR07FcrjRDx0vWnDEcHiDuamXOmtAck8bdONUv5Fi6wR/ubQwPfG1NbcqgPg+y0fHN8Unj5DO+5fV4w== + mdurl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"