From 3f3547a8fb24cdd8a661db1c4d80b50705161bc1 Mon Sep 17 00:00:00 2001 From: "Bruno St. John" Date: Sun, 23 Jul 2023 20:40:07 +0100 Subject: [PATCH] feat: redesign --- .../src/rendering/devices/capellix/device.rs | 2 + src-tauri/src/rendering/devices/device.rs | 10 ++ .../rendering/devices/thermaltake/device.rs | 2 + src-tauri/src/rendering/threading.rs | 138 +++++++++--------- src-tauri/src/rendering/ultralight/engine.rs | 6 +- src/components/FeaturedThemePanel.svelte | 12 +- src/components/ThemeCard.svelte | 55 +++++-- src/components/ThemeDownload.svelte | 3 +- src/components/parameters/Boolean.svelte | 43 +++++- src/components/parameters/Colour.svelte | 94 ++++++++++-- src/components/parameters/File.svelte | 31 ++-- src/components/parameters/Range.svelte | 111 ++++++++++---- src/components/parameters/Sensor.svelte | 120 +++++++++++---- src/components/parameters/Text.svelte | 75 ++++++++-- src/helpers/clickOutside.js | 18 +++ src/routes/+layout.scss | 13 +- src/routes/+layout.svelte | 20 ++- src/routes/background.css | 41 +++++- src/routes/renderer/+page.scss | 13 +- src/routes/renderer/+page.svelte | 34 +++-- src/routes/themes/+page.scss | 17 ++- src/routes/themes/fsName/+page.svelte | 2 +- 22 files changed, 640 insertions(+), 220 deletions(-) create mode 100644 src/helpers/clickOutside.js diff --git a/src-tauri/src/rendering/devices/capellix/device.rs b/src-tauri/src/rendering/devices/capellix/device.rs index 42158a4..a3918ea 100644 --- a/src-tauri/src/rendering/devices/capellix/device.rs +++ b/src-tauri/src/rendering/devices/capellix/device.rs @@ -77,6 +77,8 @@ impl<'a> DeviceCreator for Capellix<'a> { name: "Corsair iCUE Capellix LCD Cooler".to_string(), manufacturer: "Corsair".to_string(), conflicting_processes: vec!["iCUE.exe".to_string()], + width: 480, + height: 480, } } } diff --git a/src-tauri/src/rendering/devices/device.rs b/src-tauri/src/rendering/devices/device.rs index 8d1f1ce..87c0baf 100644 --- a/src-tauri/src/rendering/devices/device.rs +++ b/src-tauri/src/rendering/devices/device.rs @@ -8,6 +8,7 @@ mod thermaltake; pub struct DeviceContainer { device: Box, + device_info: DeviceInfo, } impl DeviceContainer { @@ -16,6 +17,7 @@ impl DeviceContainer { Ok(device) => { return Ok(Self { device: Box::new(device), + device_info: TTUltra::device_info(), }) } Err(_) => None, @@ -25,6 +27,7 @@ impl DeviceContainer { Ok(device) => { return Ok(Self { device: Box::new(device), + device_info: Capellix::device_info(), }); } Err(_) => None, @@ -52,6 +55,10 @@ impl DeviceContainer { pub fn send_image(&mut self, img: &[u8]) -> Result<(), &'static str> { self.device.send_image(img) } + + pub fn device_info(&self) -> DeviceInfo { + self.device_info.clone() + } } pub trait DeviceCreator { @@ -63,10 +70,13 @@ pub trait DeviceCreator { Self: Sized; } +#[derive(Clone, Debug)] pub struct DeviceInfo { pub name: String, pub manufacturer: String, pub conflicting_processes: Vec, + pub width: u32, + pub height: u32, } pub trait Device { diff --git a/src-tauri/src/rendering/devices/thermaltake/device.rs b/src-tauri/src/rendering/devices/thermaltake/device.rs index 212dad1..b2eb525 100644 --- a/src-tauri/src/rendering/devices/thermaltake/device.rs +++ b/src-tauri/src/rendering/devices/thermaltake/device.rs @@ -29,6 +29,8 @@ impl DeviceCreator for TTUltra { name: "Thermaltake Toughliquid LCD Cooler".to_string(), manufacturer: "Thermaltake".to_string(), conflicting_processes: vec![], + width: 480, + height: 480, } } } diff --git a/src-tauri/src/rendering/threading.rs b/src-tauri/src/rendering/threading.rs index 13faff9..72806c5 100644 --- a/src-tauri/src/rendering/threading.rs +++ b/src-tauri/src/rendering/threading.rs @@ -20,7 +20,7 @@ use std::fs::{self}; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::thread::{self, JoinHandle}; -use std::time::{Duration, SystemTime}; +use std::time::Duration; use std::vec; use crate::rendering::device::DeviceContainer; @@ -60,13 +60,6 @@ impl Renderer { let (tx_port, rx_port) = kanal::unbounded(); let render = thread::spawn(move || { - let mut engine = Ultralight::new(app_folder); - - let mut frame_time = EventTicker::new(1000 / fps); - let mut sensor_time = EventTicker::new(3000); - let mut channel_scan = EventTicker::new(250); - let mut gc_time = EventTicker::new(1000 * 30); - let mut device = match DeviceContainer::new() { Err(error) => { println!("{:?}", error); @@ -75,6 +68,14 @@ impl Renderer { Ok(result) => result, }; + let info = device.device_info(); + + let mut engine = Ultralight::new(app_folder, info.width, info.height); + + let mut frame_time = EventTicker::new(1000 / fps); + let mut sensor_time = EventTicker::new(3000); + let mut gc_time = EventTicker::new(1000 * 30); + let _ = device .init() .map_err(|_| println!("Failed to initialise device.")); @@ -121,79 +122,76 @@ impl Renderer { engine.render(); - if channel_scan.check_time() { - if gc_time.check_time() { - engine.garbage_collect(); - } + if gc_time.check_time() { + engine.garbage_collect(); + } - if let Ok(Some(port)) = rx_port.try_recv() { - let _ = engine - .load_url(&format!("http://127.0.0.1:{port}")) - .map_err(|_| println!("Failed to reload theme!")); - } + if let Ok(Some(port)) = rx_port.try_recv() { + let _ = engine + .load_url(&format!("http://127.0.0.1:{port}")) + .map_err(|_| println!("Failed to reload theme!")); + } - if receive_flag(&rx_reload, false) { - let server = server.lock().unwrap(); - let now_serving = server.now_serving(); - drop(server); - let mut theme_path = themes_path.clone(); - theme_path.push(now_serving); - theme_path.push("config.json"); - - if theme_path.exists() { - let theme_config_unparsed = - fs::read_to_string(theme_path).unwrap_or("".to_owned()); - - let theme_config_parsed: Vec = - serde_json::from_str(&theme_config_unparsed) - .unwrap_or(Vec::new()); - - let sensors_only: Vec = theme_config_parsed - .iter() - .filter(|x| x.r#type == "sensor") - .map(|x| x.to_owned()) - .collect::>(); - - if !sensors_only.is_empty() { - sensor_flag = true; - let sensor_paths: Vec = - sensors_only.iter().map(|x| x.value.clone()).collect(); - - let sensor_names: Vec = - sensors_only.iter().map(|x| x.name.clone()).collect(); - - let mut sensors = sensors.lock().unwrap(); - - if let Ok(vals) = sensors - .subscribe(sensor_paths.clone(), sensor_names.clone()) - { - sensor_values = vals; - } - - drop(sensors); - } else { - sensor_flag = false; + if receive_flag(&rx_reload, false) { + let server = server.lock().unwrap(); + let now_serving = server.now_serving(); + drop(server); + let mut theme_path = themes_path.clone(); + theme_path.push(now_serving); + theme_path.push("config.json"); + + if theme_path.exists() { + let theme_config_unparsed = + fs::read_to_string(theme_path).unwrap_or("".to_owned()); + + let theme_config_parsed: Vec = + serde_json::from_str(&theme_config_unparsed).unwrap_or(Vec::new()); + + let sensors_only: Vec = theme_config_parsed + .iter() + .filter(|x| x.r#type == "sensor") + .map(|x| x.to_owned()) + .collect::>(); + + if !sensors_only.is_empty() { + sensor_flag = true; + let sensor_paths: Vec = + sensors_only.iter().map(|x| x.value.clone()).collect(); + + let sensor_names: Vec = + sensors_only.iter().map(|x| x.name.clone()).collect(); + + let mut sensors = sensors.lock().unwrap(); + + if let Ok(vals) = + sensors.subscribe(sensor_paths.clone(), sensor_names.clone()) + { + sensor_values = vals; } - let serialised = theme_config_parsed.custom_serialise(); + drop(sensors); + } else { + sensor_flag = false; + } + + let serialised = theme_config_parsed.custom_serialise(); - engine.call_js_script( + engine.call_js_script( format!("document.dispatchEvent(new CustomEvent('configLoaded', {{ detail: JSON.parse('{}') }}))", &serialised), ); - } } - if receive_flag(&rx_end, false) { - println!("Received end signal. Thread: renderer."); + } + if receive_flag(&rx_end, false) { + println!("Received end signal. Thread: renderer."); - let _ = device - .close() - .map_err(|_| println!("Failed to close device!")); + let _ = device + .close() + .map_err(|_| println!("Failed to close device!")); - break; - } - - frame_time.change_frequency(&rx_fps); + break; } + + frame_time.change_frequency(&rx_fps); } else { thread::sleep(Duration::from_millis(15)); } diff --git a/src-tauri/src/rendering/ultralight/engine.rs b/src-tauri/src/rendering/ultralight/engine.rs index 9a6ab97..a4ba768 100644 --- a/src-tauri/src/rendering/ultralight/engine.rs +++ b/src-tauri/src/rendering/ultralight/engine.rs @@ -38,7 +38,7 @@ pub struct Ultralight { impl Ultralight { #[allow(unused_mut)] - pub fn new(app_folder: PathBuf) -> Ultralight { + pub fn new(app_folder: PathBuf, width: u32, height: u32) -> Ultralight { let mut renderer; let mut view; @@ -94,7 +94,7 @@ impl Ultralight { let view_config = ulCreateViewConfig(); ulViewConfigSetIsAccelerated(view_config, true); - view = ulCreateView(renderer, 480, 480, view_config, null_mut()); + view = ulCreateView(renderer, width, height, view_config, null_mut()); ulViewSetFinishLoadingCallback(view, Some(finished_callback), null_mut()); }; @@ -186,7 +186,7 @@ impl Ultralight { unsafe { let context = ulViewLockJSContext(self.view); JSGarbageCollect(context); - ulViewLockJSContext(self.view); + ulViewUnlockJSContext(self.view); } } diff --git a/src/components/FeaturedThemePanel.svelte b/src/components/FeaturedThemePanel.svelte index 4919e81..27e2687 100644 --- a/src/components/FeaturedThemePanel.svelte +++ b/src/components/FeaturedThemePanel.svelte @@ -10,10 +10,10 @@ const onClickPanel = () => { panel.classList.remove("clicked-panel"); + goto(`/themes/fsName?fsName=${encodeURIComponent(theme.fs_name)}`); panel.classList.add("clicked-panel"); setTimeout(() => { panel.classList.remove("clicked-panel"); - goto(`/themes/fsName?fsName=${encodeURIComponent(theme.fs_name)}`); }, 150); }; @@ -91,11 +91,13 @@ object-fit: cover; z-index: -5; - filter: blur(20px) brightness(90%); + filter: blur(20px) brightness(50%); // background-color: var(--bs-primary); } .short-info { + color: white; + z-index: 200; top: 0; left: 3%; @@ -113,7 +115,9 @@ white-space: nowrap; } - h4 { + h4, + h3 { + color: white; } .preview-img { @@ -152,6 +156,8 @@ -webkit-line-clamp: 9; line-clamp: 9; -webkit-box-orient: vertical; + + color: white; } } diff --git a/src/components/ThemeCard.svelte b/src/components/ThemeCard.svelte index 4a745c7..eb89810 100644 --- a/src/components/ThemeCard.svelte +++ b/src/components/ThemeCard.svelte @@ -1,20 +1,51 @@
onClick(fsName)}> - {themeName -
+ {themeName +
{themeName}
@@ -39,6 +70,8 @@ margin-left: 0; margin-right: 0; display: inline-block; + + transition: all ease-in-out 100ms; } .theme-title { @@ -48,23 +81,25 @@ -webkit-line-clamp: 1; overflow: hidden; position: absolute; - bottom: 4px; + bottom: 6px; left: 1px; transform: translate(5px, 9px); - transition: opacity ease-in-out 100ms; + transition: all ease-in-out 100ms; } &:hover { .theme-img { - filter: blur(5px); + filter: blur(5px) brightness(0.5); overflow: hidden; object-fit: cover; - width: 110%; - height: 110%; - top: -5%; - left: -5%; + // width: 105%; + // height: 105%; + // top: -2.5%; + // left: -2.5%; + + transform: scale(1.05, 1.05); } .theme-title { diff --git a/src/components/ThemeDownload.svelte b/src/components/ThemeDownload.svelte index 2eddc57..6b20a3b 100644 --- a/src/components/ThemeDownload.svelte +++ b/src/components/ThemeDownload.svelte @@ -71,7 +71,8 @@ @include flex-center; flex-direction: column; - background-color: rgba(0, 0, 0, 0.7); + // background-color: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(20px) brightness(0.5); .progress { width: 60%; diff --git a/src/components/parameters/Boolean.svelte b/src/components/parameters/Boolean.svelte index b1af368..9cb839f 100644 --- a/src/components/parameters/Boolean.svelte +++ b/src/components/parameters/Boolean.svelte @@ -20,8 +20,22 @@ }; -
{display_as}
-
+ +
{ + checked = !checked; + updateConfig(); + }} + style={`background-color: ${checked ? "rgba(5, 109, 24, 0.5)" : "rgba(5, 109, 24, 0.2)"}`} +> +
{display_as}
+
+ {checked ? "Yes" : "No"} +
+
+ + diff --git a/src/components/parameters/Colour.svelte b/src/components/parameters/Colour.svelte index 7cdacf0..d5fef54 100644 --- a/src/components/parameters/Colour.svelte +++ b/src/components/parameters/Colour.svelte @@ -6,8 +6,11 @@ export let parameter: Parameter; import "@melloware/coloris/dist/coloris.css"; import * as Coloris from "@melloware/coloris"; + import { crossfade, fade } from "svelte/transition"; + import { clickOutside } from "../../helpers/clickOutside"; let hex: string; + let rgb = { r: 255, g: 255, b: 255 }; const { name, display_as, default: def } = parameter; @@ -25,6 +28,7 @@ }); hex = parameterCurrent.value.toLowerCase(); + rgb = hexToRgb(hex); }); const updateConfig = async () => { @@ -38,22 +42,90 @@ clearTimeout(timeout); timeout = setTimeout(updateConfig, 300); }; + + function hexToRgb(hex: string): number { + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, function (m, r, g, b) { + return r + r + g + g + b + b; + }); + + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : null; + } + + let expand = false; + const [send, recieve] = crossfade({ fallback: fade }); -
-
{display_as}
-
- +{#if expand} +
+
(expand = false)} + in:recieve={{ duration: 300 }} + out:send={{ duration: 300 }} + > +
{display_as}
+
+ +
+
-
+{:else} + +
(expand = true)} + in:recieve={{ duration: 300 }} + out:send={{ duration: 300 }} + > +
{display_as}
+
+{/if} diff --git a/src/components/parameters/File.svelte b/src/components/parameters/File.svelte index 6a9137f..77a5341 100644 --- a/src/components/parameters/File.svelte +++ b/src/components/parameters/File.svelte @@ -34,28 +34,23 @@ }; -
+ +
{display_as}
-
- -

- Currently selected: {current === "" || current === undefined ? "No file selected" : current} -

-
+
Select file
diff --git a/src/components/parameters/Range.svelte b/src/components/parameters/Range.svelte index 2200a72..7d46663 100644 --- a/src/components/parameters/Range.svelte +++ b/src/components/parameters/Range.svelte @@ -3,6 +3,8 @@ import type { Parameter } from "../../helpers/themeTools"; import { invoke } from "@tauri-apps/api/tauri"; import type ParameterValue from "./parameter"; + import { crossfade, fade } from "svelte/transition"; + import { clickOutside } from "../../helpers/clickOutside"; export let parameter: Parameter; const { name, display_as, default: def, min, max, step } = parameter; @@ -30,41 +32,92 @@ clearTimeout(timeout); timeout = setTimeout(updateConfig, 300); }; + + let expand = false; + const [send, recieve] = crossfade({ fallback: fade }); - -
-
- Current value - +{#if expand} +
+
(expand = false)} + in:recieve={{ duration: 300 }} + out:send={{ duration: 300 }} + > + +
+
+ Current value + +
+
+

{min}

+ +

{max}

+
+
+
-
-

{min}

- -

{max}

+{:else} + +
(expand = true)} + in:recieve={{ duration: 300 }} + out:send={{ duration: 300 }} + > +
{display_as}
-
+{/if} diff --git a/src/components/parameters/Text.svelte b/src/components/parameters/Text.svelte index e02c35b..43374d7 100644 --- a/src/components/parameters/Text.svelte +++ b/src/components/parameters/Text.svelte @@ -3,6 +3,9 @@ import type { Parameter } from "../../helpers/themeTools"; import { invoke } from "@tauri-apps/api/tauri"; import type ParameterValue from "./parameter"; + import { clickOutside } from "../../helpers/clickOutside"; + import { crossfade, fade } from "svelte/transition"; + export let parameter: Parameter; let input: HTMLInputElement; @@ -26,25 +29,75 @@ clearTimeout(timeout); timeout = setTimeout(updateConfig, 300); }; + + let expand = false; + const [send, recieve] = crossfade({ fallback: fade }); -
- - -
+{#if expand} +
+
(expand = false)} + in:recieve={{ duration: 300 }} + out:send={{ duration: 300 }} + > + + +
+
+{:else} +
(expand = true)} + in:recieve={{ duration: 300 }} + out:send={{ duration: 300 }} + > +
{display_as}
+
+{/if} diff --git a/src/helpers/clickOutside.js b/src/helpers/clickOutside.js new file mode 100644 index 0000000..384345a --- /dev/null +++ b/src/helpers/clickOutside.js @@ -0,0 +1,18 @@ +/** Dispatch event on click outside of node */ +// @ts-ignore +export function clickOutside(node) { + // @ts-ignore + const handleClick = (event) => { + if (node && !node.contains(event.target) && !event.defaultPrevented) { + node.dispatchEvent(new CustomEvent("click_outside", node)); + } + }; + + document.addEventListener("click", handleClick, true); + + return { + destroy() { + document.removeEventListener("click", handleClick, true); + }, + }; +} diff --git a/src/routes/+layout.scss b/src/routes/+layout.scss index e3da6c0..bcb2a79 100644 --- a/src/routes/+layout.scss +++ b/src/routes/+layout.scss @@ -46,8 +46,9 @@ } body { - background-color: #080d14 !important; + // background-color: #080d14 !important; color: #ffffff !important; + } main { @@ -101,7 +102,7 @@ a[href^="http://"], -webkit-app-region: drag; height: 2.8rem; - transition: all 150ms ease-in-out; + transition: all 90ms ease-in-out; .featured-text { position: absolute; @@ -281,3 +282,11 @@ main { .nav-link { color: #9499ff; } + +.bg-canvas { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; +} \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 902b787..4069fbe 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -12,6 +12,7 @@ import { fade, fly } from "svelte/transition"; import { cubicIn, cubicOut } from "svelte/easing"; + import { onMount } from "svelte"; export let data; @@ -41,16 +42,17 @@
- {#if showDarkened} -