Added Zoom factor

Closes #146
This commit is contained in:
Jonathan Johnson 2024-07-25 11:31:38 -07:00
parent 83fe4b05ef
commit ba3a4b9b02
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
6 changed files with 144 additions and 59 deletions

View file

@ -43,6 +43,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
true, the window is allowed to be closed. If false is returned, the window
will remain open. This feature is most commonly used to prevent losing unsaved
changes.
- `Fraction` now has `LinearInterpolation` and `PercentBetween` implementations.
- `Window::zoom` allows setting a `Dynamic<Fraction>` that scales all DPI-scaled
operations by an additional scaling factor.
## v0.3.0 (2024-05-12)

68
Cargo.lock generated
View file

@ -1101,12 +1101,6 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hermit-abi"
version = "0.4.0"
@ -1242,9 +1236,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.31"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
@ -1296,8 +1290,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kludgine"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dac414f26b39a8b25572244a3cbb4a4a566c86ab9a0d878b88a2e46d657d59c"
source = "git+https://github.com/khonsulabs/kludgine#eca1ac5d3a9dead20c9c94fb849a4e2bf7719654"
dependencies = [
"ahash",
"alot",
@ -1522,7 +1515,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
dependencies = [
"cfg-if",
"rayon",
]
[[package]]
@ -1649,9 +1641,9 @@ dependencies = [
[[package]]
name = "nominals"
version = "0.3.0"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd4b6e50a0a7f2214e99ecf7f4a2c9cb9572e5817d96e37a6d31387961c23994"
checksum = "83d3716836932a56eadedb47bd27ee2174dbdd3ac97c585002e2994e162c5b37"
[[package]]
name = "noop_proc_macro"
@ -1720,16 +1712,6 @@ dependencies = [
"libm",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi 0.3.9",
"libc",
]
[[package]]
name = "num_enum"
version = "0.7.2"
@ -1965,9 +1947,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.36.1"
version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
dependencies = [
"memchr",
]
@ -2174,7 +2156,7 @@ checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi 0.4.0",
"hermit-abi",
"pin-project-lite",
"rustix",
"tracing",
@ -2392,16 +2374,15 @@ dependencies = [
[[package]]
name = "ravif"
version = "0.11.8"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6ba61c28ba24c0cf8406e025cb29a742637e3f70776e61c27a8a8b72a042d12"
checksum = "5797d09f9bd33604689e87e8380df4951d4912f01b63f71205e2abd4ae25e6b6"
dependencies = [
"avif-serialize",
"imgref",
"loop9",
"quick-error",
"rav1e",
"rayon",
"rgb",
]
@ -2628,9 +2609,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "0.6.6"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
dependencies = [
"serde",
]
@ -2928,32 +2909,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.38.1"
version = "1.39.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a"
dependencies = [
"backtrace",
"num_cpus",
"pin-project-lite",
]
[[package]]
name = "toml"
version = "0.8.15"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28"
checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.16",
"toml_edit 0.22.17",
]
[[package]]
name = "toml_datetime"
version = "0.6.6"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
dependencies = [
"serde",
]
@ -2971,15 +2951,15 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.16"
version = "0.22.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow 0.6.14",
"winnow 0.6.16",
]
[[package]]
@ -3797,9 +3777,9 @@ dependencies = [
[[package]]
name = "winnow"
version = "0.6.14"
version = "0.6.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f"
checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c"
dependencies = [
"memchr",
]

View file

@ -22,7 +22,9 @@ tokio = ["dep:tokio"]
tokio-multi-thread = ["tokio", "tokio/rt-multi-thread"]
[dependencies]
kludgine = { version = "0.9.0", features = ["app"] }
kludgine = { git = "https://github.com/khonsulabs/kludgine", features = [
"app",
] }
figures = { version = "0.4.0" }
alot = "0.3"
interner = "0.2.1"

21
examples/window-zoom.rs Normal file
View file

@ -0,0 +1,21 @@
use cushy::value::{Dynamic, Source};
use cushy::widget::MakeWidget;
use cushy::widgets::slider::Slidable;
use cushy::Run;
use figures::Fraction;
fn main() -> cushy::Result<()> {
let zoom = Dynamic::new(Fraction::ONE);
zoom.map_each(|z| z.to_string())
.and(
zoom.clone()
.slider_between(Fraction::new(1, 4), Fraction::new(4, 1)),
)
.into_rows()
.fit_horizontally()
.pad()
.expand()
.into_window()
.zoom(zoom)
.run()
}

View file

@ -49,7 +49,7 @@ use std::time::{Duration, Instant};
use alot::{LotId, Lots};
use figures::units::{Lp, Px, UPx};
use figures::{Angle, Point, Ranged, Rect, Size, UnscaledUnit, Zero};
use figures::{Angle, Fraction, Point, Ranged, Rect, Size, UnscaledUnit, Zero};
use intentional::Cast;
use kempt::Set;
use kludgine::Color;
@ -883,6 +883,11 @@ impl LinearInterpolate for f64 {
*self + delta * f64::from(percent)
}
}
impl LinearInterpolate for Fraction {
fn lerp(&self, target: &Self, percent: f32) -> Self {
Fraction::from(self.into_f32().lerp(&target.into_f32(), percent))
}
}
impl LinearInterpolate for Angle {
fn lerp(&self, target: &Self, percent: f32) -> Self {
@ -1130,6 +1135,13 @@ impl PercentBetween for Color {
}
}
impl PercentBetween for Fraction {
fn percent_between(&self, min: &Self, max: &Self) -> ZeroToOne {
self.into_f32()
.percent_between(&min.into_f32(), &max.into_f32())
}
}
#[test]
fn int_percent_between() {
assert_eq!(1_u8.percent_between(&1_u8, &2_u8), ZeroToOne::ZERO);

View file

@ -56,7 +56,8 @@ use crate::styles::{Edges, FontFamilyList, ThemePair};
use crate::tree::Tree;
use crate::utils::ModifiersExt;
use crate::value::{
Destination, Dynamic, DynamicReader, Generation, IntoDynamic, IntoValue, Source, Value,
Destination, Dynamic, DynamicRead, DynamicReader, Generation, IntoDynamic, IntoValue, Source,
Value,
};
use crate::widget::{
Callback, EventHandling, MakeWidget, MountedWidget, OnceCallback, RootBehavior, WidgetId,
@ -499,6 +500,7 @@ where
on_closed: Option<OnceCallback>,
inner_size: Option<Dynamic<Size<UPx>>>,
zoom: Option<Dynamic<Fraction>>,
occluded: Option<Dynamic<bool>>,
focused: Option<Dynamic<bool>>,
theme_mode: Option<Value<ThemeMode>>,
@ -577,6 +579,7 @@ where
multisample_count: NonZeroU32::new(4).assert("not 0"),
vsync: true,
close_requested: None,
zoom: None,
}
}
@ -610,7 +613,7 @@ where
self
}
/// Sets `inner_size` to be the dynamic syncrhonized with this window's
/// Sets `inner_size` to be the dynamic synchronized with this window's
/// inner size.
///
/// When the window is resized, the dynamic will contain its new size. When
@ -622,6 +625,15 @@ where
self
}
/// Sets this window's `zoom` factor.
///
/// The zoom factor is multiplied with the DPI scaling from the window
/// server to allow an additional scaling factor to be applied.
pub fn zoom(mut self, zoom: impl IntoDynamic<Fraction>) -> Self {
self.zoom = Some(zoom.into_dynamic().map_each_into());
self
}
/// Sets the [`ThemeMode`] for this window.
///
/// If a [`ThemeMode`] is provided, the window will be set to this theme
@ -729,6 +741,7 @@ where
vsync: self.vsync,
multisample_count: self.multisample_count,
close_requested: self.close_requested.map(|cb| Arc::new(Mutex::new(cb))),
zoom: self.zoom.unwrap_or_else(|| Dynamic::new(Fraction::ONE)),
}),
},
)?;
@ -808,6 +821,9 @@ struct OpenWindow<T> {
cushy: Cushy,
on_closed: Option<OnceCallback>,
vsync: bool,
dpi_scale: Dynamic<Fraction>,
zoom: Dynamic<Fraction>,
zoom_generation: Generation,
close_requested: Option<Arc<Mutex<Callback<(), bool>>>>,
}
@ -1172,6 +1188,8 @@ where
let on_closed = settings.on_closed.take();
let close_requested = settings.close_requested.take();
let vsync = settings.vsync;
let zoom = settings.zoom.clone();
let dpi_scale = Dynamic::new(graphics.dpi_scale());
inner_size.set(window.inner_size());
@ -1229,6 +1247,9 @@ where
on_closed,
vsync,
close_requested,
dpi_scale,
zoom_generation: zoom.generation(),
zoom,
}
}
@ -1247,9 +1268,17 @@ where
self.redraw_status.refresh_received();
graphics.reset_text_attributes();
let zoom = self.zoom.read();
if zoom.generation() != self.zoom_generation {
graphics.set_zoom(*zoom);
self.redraw_status.invalidate(self.root.id());
}
self.tree
.new_frame(self.redraw_status.invalidations().drain());
drop(zoom);
let resizable = window.is_resizable() || self.resize_to_fit;
let mut window = RunningWindow::new(
window,
@ -1892,7 +1921,14 @@ where
// fn occlusion_changed(&mut self, window: kludgine::app::Window<'_, ()>) {}
// fn scale_factor_changed(&mut self, window: kludgine::app::Window<'_, ()>) {}
fn scale_factor_changed(
&mut self,
mut window: kludgine::app::Window<'_, WindowCommand>,
kludgine: &mut Kludgine,
) {
self.dpi_scale.set(kludgine.dpi_scale());
window.set_needs_redraw();
}
fn resized(
&mut self,
@ -2055,7 +2091,7 @@ pub(crate) mod sealed {
use std::sync::Arc;
use figures::units::UPx;
use figures::{Point, Size};
use figures::{Fraction, Point, Size};
use image::DynamicImage;
use kludgine::Color;
use parking_lot::Mutex;
@ -2081,6 +2117,7 @@ pub(crate) mod sealed {
pub occluded: Dynamic<bool>,
pub focused: Dynamic<bool>,
pub inner_size: Dynamic<Size<UPx>>,
pub zoom: Dynamic<Fraction>,
pub theme: Option<Value<ThemePair>>,
pub theme_mode: Option<Value<ThemeMode>>,
pub transparent: bool,
@ -2441,7 +2478,7 @@ impl VirtualState {
/// Window state that is able to be updated outside of event handling,
/// potentially via other threads depending on the application.
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
pub struct WindowDynamicState {
/// The target of the next frame to draw.
pub redraw_target: Dynamic<RedrawTarget>,
@ -2453,6 +2490,16 @@ pub struct WindowDynamicState {
pub title: Dynamic<String>,
}
impl Default for WindowDynamicState {
fn default() -> Self {
Self {
redraw_target: Default::default(),
close_requested: Default::default(),
title: Default::default(),
}
}
}
/// A target for the next redraw of a window.
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
pub enum RedrawTarget {
@ -2530,6 +2577,7 @@ pub struct CushyWindowBuilder {
initial_size: Size<UPx>,
scale: f32,
transparent: bool,
zoom: Dynamic<Fraction>,
}
impl CushyWindowBuilder {
@ -2541,6 +2589,7 @@ impl CushyWindowBuilder {
multisample_count: NonZeroU32::new(4).assert("not 0"),
initial_size: Size::new(UPx::new(800), UPx::new(600)),
scale: 1.,
zoom: Dynamic::new(Fraction::ONE),
transparent: false,
}
}
@ -2621,6 +2670,7 @@ impl CushyWindowBuilder {
vsync: false,
multisample_count: self.multisample_count,
close_requested: None,
zoom: self.zoom,
},
);
@ -2757,13 +2807,24 @@ impl CushyWindow {
}
/// Returns the current DPI scale of the window.
pub const fn scale(&self) -> Fraction {
pub const fn dpi_scale(&self) -> Fraction {
self.kludgine.dpi_scale()
}
/// Returns the effective scale of the window.
pub fn effective_scale(&self) -> Fraction {
self.kludgine.scale()
}
/// Updates the dimensions and DPI scaling of the window.
pub fn resize(&mut self, new_size: Size<UPx>, new_scale: impl Into<f32>, queue: &wgpu::Queue) {
self.kludgine.resize(new_size, new_scale.into(), queue);
pub fn resize(
&mut self,
new_size: Size<UPx>,
new_scale: impl Into<Fraction>,
new_zoom: impl Into<Fraction>,
queue: &wgpu::Queue,
) {
self.kludgine.resize(new_size, new_scale, new_zoom, queue);
self.window.resized(new_size);
}
@ -2971,13 +3032,19 @@ impl VirtualWindow {
}
/// Returns the current DPI scale of the window.
pub const fn scale(&self) -> Fraction {
self.cushy.scale()
pub const fn dpi_scale(&self) -> Fraction {
self.cushy.dpi_scale()
}
/// Updates the dimensions and DPI scaling of the window.
pub fn resize(&mut self, new_size: Size<UPx>, new_scale: impl Into<f32>, queue: &wgpu::Queue) {
self.cushy.resize(new_size, new_scale.into(), queue);
pub fn resize(
&mut self,
new_size: Size<UPx>,
new_scale: impl Into<Fraction>,
queue: &wgpu::Queue,
) {
self.cushy
.resize(new_size, new_scale, self.cushy.kludgine.zoom(), queue);
}
/// Provide keyboard input to this virtual window.
@ -3431,7 +3498,7 @@ where
fn redraw(&mut self) {
let mut render_size = self.window.size().ceil();
if self.window.state.size != render_size {
let current_scale = self.window.scale();
let current_scale = self.window.dpi_scale();
self.window
.resize(self.window.state.size, current_scale, &self.queue);
render_size = self.window.state.size;