mirror of
https://github.com/Zenithsiz/zsw.git
synced 2026-02-03 09:50:31 +00:00
Compare commits
2 Commits
50c7dc44e6
...
81cf5ae013
| Author | SHA1 | Date | |
|---|---|---|---|
| 81cf5ae013 | |||
| 4df597ac72 |
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -14,6 +14,7 @@
|
||||
"endifs",
|
||||
"epaint",
|
||||
"gles",
|
||||
"humantime",
|
||||
"imageops",
|
||||
"impls",
|
||||
"indexmap",
|
||||
@ -34,6 +35,7 @@
|
||||
"rwlock",
|
||||
"rwlocks",
|
||||
"smallvec",
|
||||
"subsec",
|
||||
"thiserror",
|
||||
"turbofish",
|
||||
"Undefines",
|
||||
|
||||
17
Cargo.lock
generated
17
Cargo.lock
generated
@ -1563,9 +1563,19 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
|
||||
checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
|
||||
|
||||
[[package]]
|
||||
name = "humantime-serde"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
@ -4801,6 +4811,8 @@ dependencies = [
|
||||
"egui",
|
||||
"egui_plot",
|
||||
"futures",
|
||||
"humantime",
|
||||
"humantime-serde",
|
||||
"image",
|
||||
"itertools 0.14.0",
|
||||
"naga",
|
||||
@ -4853,7 +4865,6 @@ dependencies = [
|
||||
"pin-project",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"toml 0.9.5",
|
||||
|
||||
@ -29,6 +29,8 @@ egui_wgpu_backend = "0.35.0"
|
||||
egui_winit_platform = "0.27.0"
|
||||
extend = "1.2.0"
|
||||
futures = "0.3.31"
|
||||
humantime = "2.3.0"
|
||||
humantime-serde = "1.1.1"
|
||||
image = "0.25.8"
|
||||
include_dir = "0.7.4"
|
||||
itertools = "0.14.0"
|
||||
|
||||
@ -8,4 +8,7 @@ absolute-paths-allowed-crates = [
|
||||
|
||||
# Crate consists of only nondescript modules
|
||||
"naga_oil",
|
||||
|
||||
# Some modules are named generically, so we can't import them (e.g. `egui::util`)
|
||||
"egui",
|
||||
]
|
||||
|
||||
@ -13,7 +13,6 @@ image = { workspace = true }
|
||||
pin-project = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
serde_with = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tokio-stream = { features = ["fs"], workspace = true }
|
||||
toml = { workspace = true }
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
//! Duration display
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::AppError,
|
||||
app_error::Context,
|
||||
core::{fmt, str::FromStr, time::Duration},
|
||||
};
|
||||
|
||||
/// Duration
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(serde_with::SerializeDisplay)]
|
||||
#[derive(serde_with::DeserializeFromStr)]
|
||||
pub struct DurationDisplay(pub Duration);
|
||||
|
||||
impl FromStr for DurationDisplay {
|
||||
type Err = AppError;
|
||||
|
||||
fn from_str(mut s: &str) -> Result<Self, Self::Err> {
|
||||
let mut time = Duration::ZERO;
|
||||
|
||||
// Remove any hours from the time
|
||||
if let Some((hours, rest)) = s.split_once('h') {
|
||||
let hours = hours
|
||||
.parse::<u64>()
|
||||
.with_context(|| format!("Expected an integer before `h`, found {hours:?}"))?;
|
||||
time += Duration::from_hours(hours);
|
||||
s = rest;
|
||||
}
|
||||
|
||||
// Remove any minutes from the time
|
||||
if let Some((mins, rest)) = s.split_once('m') {
|
||||
let mins = mins
|
||||
.parse::<u64>()
|
||||
.with_context(|| format!("Expected an integer before `m`, found {mins:?}"))?;
|
||||
time += Duration::from_mins(mins);
|
||||
s = rest;
|
||||
}
|
||||
|
||||
// Then remove any trailing `s` the user might have added
|
||||
let secs = s.strip_suffix('s').unwrap_or(s);
|
||||
|
||||
// And parse the rest as seconds (may be empty)
|
||||
let secs = match secs {
|
||||
"" => 0.0,
|
||||
_ => secs
|
||||
.parse::<f64>()
|
||||
.with_context(|| format!("Expected a number of seconds, found {secs:?}"))?,
|
||||
};
|
||||
time += Duration::from_secs_f64(secs);
|
||||
|
||||
Ok(Self(time))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DurationDisplay {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let hours = self.0.as_secs() / 3600;
|
||||
if hours != 0 {
|
||||
write!(f, "{hours}h")?;
|
||||
}
|
||||
|
||||
let mins = (self.0 - Duration::from_hours(hours)).as_secs() / 60;
|
||||
if mins != 0 {
|
||||
write!(f, "{mins}m")?;
|
||||
}
|
||||
|
||||
let secs = (self.0 - Duration::from_hours(hours) - Duration::from_mins(mins)).as_secs_f64();
|
||||
if secs != 0.0 || (hours == 0 && mins == 0) {
|
||||
// TODO: Find some other way of having variable precision (up to millisecond)
|
||||
let mut secs = format!("{secs:.3}");
|
||||
while secs.ends_with('0') {
|
||||
_ = secs.pop();
|
||||
}
|
||||
if secs.ends_with('.') {
|
||||
_ = secs.pop();
|
||||
}
|
||||
secs.push('s');
|
||||
f.pad(&secs)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -29,7 +29,6 @@
|
||||
)]
|
||||
|
||||
// Modules
|
||||
pub mod duration_display;
|
||||
pub mod loadable;
|
||||
mod rect;
|
||||
pub mod resource_manager;
|
||||
@ -39,7 +38,6 @@ pub mod walk_dir;
|
||||
|
||||
// Exports
|
||||
pub use {
|
||||
duration_display::DurationDisplay,
|
||||
loadable::Loadable,
|
||||
rect::Rect,
|
||||
resource_manager::ResourceManager,
|
||||
|
||||
@ -21,6 +21,8 @@ directories = { workspace = true }
|
||||
egui = { features = ["default_fonts"], workspace = true }
|
||||
egui_plot = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
humantime = { workspace = true }
|
||||
humantime-serde = { workspace = true }
|
||||
image = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
naga = { features = ["deserialize"], workspace = true }
|
||||
|
||||
@ -21,7 +21,7 @@ use {
|
||||
profile::Profiles,
|
||||
shared::SharedWindow,
|
||||
},
|
||||
core::{ops::RangeInclusive, str::FromStr, time::Duration},
|
||||
core::{ops::RangeInclusive, time::Duration},
|
||||
egui::Widget,
|
||||
std::{
|
||||
collections::HashMap,
|
||||
@ -30,7 +30,7 @@ use {
|
||||
},
|
||||
strum::IntoEnumIterator,
|
||||
winit::{event_loop::EventLoopProxy, window::WindowId},
|
||||
zsw_util::{AppError, DurationDisplay, Rect},
|
||||
zsw_util::{AppError, Rect},
|
||||
zsw_wgpu::Wgpu,
|
||||
};
|
||||
|
||||
@ -147,9 +147,19 @@ fn draw_duration(ui: &mut egui::Ui, duration: &mut Duration, range: RangeInclusi
|
||||
let start = range.start().as_secs_f32();
|
||||
let end = range.end().as_secs_f32();
|
||||
egui::Slider::new(&mut secs, start..=end)
|
||||
.custom_formatter(|secs, _| DurationDisplay(Duration::from_secs_f64(secs)).to_string())
|
||||
.custom_parser(|s| DurationDisplay::from_str(s).ok().map(|d| d.0.as_secs_f64()))
|
||||
.clamping(egui::SliderClamping::Edits)
|
||||
.custom_formatter(|secs, _| {
|
||||
// Note: We round any durations to the nearest millisecond to avoid displaying
|
||||
// numbers that are too big
|
||||
let duration = Duration::from_secs_f64(secs);
|
||||
let nanos_per_ms = Duration::from_millis(1).subsec_nanos();
|
||||
let duration = Duration::new(
|
||||
duration.as_secs(),
|
||||
duration.subsec_nanos().next_multiple_of(nanos_per_ms),
|
||||
);
|
||||
humantime::format_duration(duration).to_string()
|
||||
})
|
||||
.custom_parser(|s| humantime::parse_duration(s).ok().map(|d| d.as_secs_f64()))
|
||||
.clamping(egui::SliderClamping::Never)
|
||||
.ui(ui);
|
||||
*duration = Duration::from_secs_f32(secs);
|
||||
}
|
||||
|
||||
@ -7,8 +7,8 @@ pub mod render_panels;
|
||||
// Imports
|
||||
use {
|
||||
crate::{menu, metrics::FrameTimes},
|
||||
core::time::Duration,
|
||||
egui::{Widget, style},
|
||||
core::{hash::Hash, time::Duration},
|
||||
egui::{Widget, epaint, style},
|
||||
std::collections::{HashMap, HashSet},
|
||||
};
|
||||
|
||||
@ -219,11 +219,19 @@ where
|
||||
},
|
||||
};
|
||||
|
||||
egui_plot::BarChart::new(duration_idx.name(), bars)
|
||||
// Note: This auto-color algorithm is based on egui's auto color, but using
|
||||
// a stable value. This also makes the colors the same between runs.
|
||||
let color = {
|
||||
let duration_hash = egui::util::hash(duration_idx) as u16;
|
||||
let h = f32::from(duration_hash) / f32::from(u16::MAX);
|
||||
epaint::Hsva::new(h, 0.85, 0.5, 1.0)
|
||||
};
|
||||
|
||||
egui_plot::BarChart::new(duration_idx.name(), bars).color(color)
|
||||
}
|
||||
|
||||
/// Duration index
|
||||
pub trait DurationIdx<T> {
|
||||
pub trait DurationIdx<T>: Hash {
|
||||
/// Returns the name of this index
|
||||
fn name(&self) -> String;
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ pub fn draw(ui: &mut egui::Ui, render_frame_times: &mut FrameTimes<RenderFrameTi
|
||||
super::draw_plot(ui, render_frame_times, &display, DurationIdx::iter());
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
|
||||
#[derive(strum::EnumIter)]
|
||||
enum DurationIdx {
|
||||
WaitNextFrame,
|
||||
|
||||
@ -8,7 +8,7 @@ use {
|
||||
crate::{display::DisplayName, playlist::PlaylistName},
|
||||
core::time::Duration,
|
||||
std::{borrow::Borrow, fmt, sync::Arc},
|
||||
zsw_util::{DurationDisplay, ResourceManager, resource_manager},
|
||||
zsw_util::{ResourceManager, resource_manager},
|
||||
};
|
||||
|
||||
/// Profiles
|
||||
@ -90,8 +90,8 @@ impl resource_manager::FromSerialized<ProfileName, ser::Profile> for Profile {
|
||||
}),
|
||||
ser::ProfilePanelShader::Fade(shader) => ProfilePanelShader::Fade(ProfilePanelFadeShader {
|
||||
playlists: shader.playlists.into_iter().map(PlaylistName::from).collect(),
|
||||
duration: shader.duration.0,
|
||||
fade_duration: shader.fade_duration.0,
|
||||
duration: shader.duration,
|
||||
fade_duration: shader.fade_duration,
|
||||
inner: match shader.inner {
|
||||
ser::ProfilePanelFadeShaderInner::Basic => ProfilePanelFadeShaderInner::Basic,
|
||||
ser::ProfilePanelFadeShaderInner::White { strength } =>
|
||||
@ -130,8 +130,8 @@ impl resource_manager::ToSerialized<ProfileName, ser::Profile> for Profile {
|
||||
ProfilePanelShader::Fade(shader) =>
|
||||
ser::ProfilePanelShader::Fade(ser::ProfilePanelFadeShader {
|
||||
playlists: shader.playlists.iter().map(PlaylistName::to_string).collect(),
|
||||
duration: DurationDisplay(shader.duration),
|
||||
fade_duration: DurationDisplay(shader.fade_duration),
|
||||
duration: shader.duration,
|
||||
fade_duration: shader.fade_duration,
|
||||
inner: match shader.inner {
|
||||
ProfilePanelFadeShaderInner::Basic => ser::ProfilePanelFadeShaderInner::Basic,
|
||||
ProfilePanelFadeShaderInner::White { strength } =>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
//! Serialized profile
|
||||
|
||||
// Imports
|
||||
use zsw_util::DurationDisplay;
|
||||
use core::time::Duration;
|
||||
|
||||
/// Profile
|
||||
#[derive(Debug)]
|
||||
@ -46,8 +45,10 @@ pub struct ProfilePanelNoneShader {
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct ProfilePanelFadeShader {
|
||||
pub playlists: Vec<String>,
|
||||
pub duration: DurationDisplay,
|
||||
pub fade_duration: DurationDisplay,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub duration: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub fade_duration: Duration,
|
||||
|
||||
/// Inner
|
||||
#[serde(flatten)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user