1
0
Fork 0

Add a way to rescale and resize the UI from VIZIA

With some additional VIZIA patches. This currently causes the layout to
do weird things, so it's not usable yet. On the baseview side this has
also only been implemented for Linux. Once the vizia quirks are gone we
can add a widget that you can add to your GUI to make it scaleable.
This commit is contained in:
Robbert van der Helm 2022-03-27 20:38:49 +02:00
parent f9db59f4bc
commit a39eea1fe9
4 changed files with 159 additions and 27 deletions

38
Cargo.lock generated
View file

@ -327,6 +327,24 @@ dependencies = [
"xcb-util", "xcb-util",
] ]
[[package]]
name = "baseview"
version = "0.1.0"
source = "git+https://github.com/robbert-vdh/baseview.git?branch=feature/resize#b5d0751c82c3fd0301366f1f31c07f3cd53aa52f"
dependencies = [
"cocoa",
"core-foundation",
"keyboard-types",
"nix",
"objc",
"raw-window-handle",
"uuid",
"winapi 0.3.9",
"x11",
"xcb 0.9.0",
"xcb-util",
]
[[package]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.2" version = "0.5.2"
@ -884,7 +902,7 @@ name = "egui-baseview"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/robbert-vdh/egui-baseview.git?branch=fix/update-dependencies#afde9c661809ee08d69864caa899246592d93f8c" source = "git+https://github.com/robbert-vdh/egui-baseview.git?branch=fix/update-dependencies#afde9c661809ee08d69864caa899246592d93f8c"
dependencies = [ dependencies = [
"baseview", "baseview 0.1.0 (git+https://github.com/robbert-vdh/baseview.git?branch=feature/mouse-event-modifiers)",
"copypasta", "copypasta",
"egui", "egui",
"gl", "gl",
@ -1558,7 +1576,7 @@ name = "iced_baseview"
version = "0.0.3" version = "0.0.3"
source = "git+https://github.com/robbert-vdh/iced_baseview.git?branch=feature/update-baseview#116e9043779946c9b2a1b4d2a06bf46752930919" source = "git+https://github.com/robbert-vdh/iced_baseview.git?branch=feature/update-baseview#116e9043779946c9b2a1b4d2a06bf46752930919"
dependencies = [ dependencies = [
"baseview", "baseview 0.1.0 (git+https://github.com/robbert-vdh/baseview.git?branch=feature/mouse-event-modifiers)",
"copypasta", "copypasta",
"iced_core", "iced_core",
"iced_futures", "iced_futures",
@ -2165,7 +2183,7 @@ dependencies = [
name = "nih_plug_egui" name = "nih_plug_egui"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"baseview", "baseview 0.1.0 (git+https://github.com/robbert-vdh/baseview.git?branch=feature/mouse-event-modifiers)",
"crossbeam", "crossbeam",
"egui", "egui",
"egui-baseview", "egui-baseview",
@ -2179,7 +2197,7 @@ name = "nih_plug_iced"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"atomic_refcell", "atomic_refcell",
"baseview", "baseview 0.1.0 (git+https://github.com/robbert-vdh/baseview.git?branch=feature/mouse-event-modifiers)",
"crossbeam", "crossbeam",
"iced_baseview", "iced_baseview",
"nih_plug", "nih_plug",
@ -2190,7 +2208,7 @@ dependencies = [
name = "nih_plug_vizia" name = "nih_plug_vizia"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"baseview", "baseview 0.1.0 (git+https://github.com/robbert-vdh/baseview.git?branch=feature/resize)",
"crossbeam", "crossbeam",
"femtovg", "femtovg",
"nih_plug", "nih_plug",
@ -3607,7 +3625,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "vizia" name = "vizia"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/robbert-vdh/vizia.git?branch=feature/baseview-modifiers#c671d86063e5f7754b90fca3c92350efa6d9fc0c" source = "git+https://github.com/robbert-vdh/vizia.git?branch=patched#6c9844125ee68537842a025657cbd51734a2589d"
dependencies = [ dependencies = [
"vizia_baseview", "vizia_baseview",
"vizia_core", "vizia_core",
@ -3616,9 +3634,9 @@ dependencies = [
[[package]] [[package]]
name = "vizia_baseview" name = "vizia_baseview"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/robbert-vdh/vizia.git?branch=feature/baseview-modifiers#c671d86063e5f7754b90fca3c92350efa6d9fc0c" source = "git+https://github.com/robbert-vdh/vizia.git?branch=patched#6c9844125ee68537842a025657cbd51734a2589d"
dependencies = [ dependencies = [
"baseview", "baseview 0.1.0 (git+https://github.com/robbert-vdh/baseview.git?branch=feature/resize)",
"femtovg", "femtovg",
"keyboard-types", "keyboard-types",
"raw-window-handle", "raw-window-handle",
@ -3628,7 +3646,7 @@ dependencies = [
[[package]] [[package]]
name = "vizia_core" name = "vizia_core"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/robbert-vdh/vizia.git?branch=feature/baseview-modifiers#c671d86063e5f7754b90fca3c92350efa6d9fc0c" source = "git+https://github.com/robbert-vdh/vizia.git?branch=patched#6c9844125ee68537842a025657cbd51734a2589d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"copypasta", "copypasta",
@ -3651,7 +3669,7 @@ dependencies = [
[[package]] [[package]]
name = "vizia_derive" name = "vizia_derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/robbert-vdh/vizia.git?branch=feature/baseview-modifiers#c671d86063e5f7754b90fca3c92350efa6d9fc0c" source = "git+https://github.com/robbert-vdh/vizia.git?branch=patched#6c9844125ee68537842a025657cbd51734a2589d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

@ -11,10 +11,10 @@ description = "An adapter to use VIZIA GUIs with NIH-plug"
nih_plug = { path = ".." } nih_plug = { path = ".." }
nih_plug_assets = { git = "https://github.com/robbert-vdh/nih_plug_assets.git" } nih_plug_assets = { git = "https://github.com/robbert-vdh/nih_plug_assets.git" }
baseview = { git = "https://github.com/robbert-vdh/baseview.git", branch = "feature/mouse-event-modifiers" } baseview = { git = "https://github.com/robbert-vdh/baseview.git", branch = "feature/resize" }
crossbeam = "0.8" crossbeam = "0.8"
# Vizia doesn't re-export this, we will # Vizia doesn't re-export this, we will
femtovg = { version = "0.3.0", default-features = false, features = ["image-loading"] } femtovg = { version = "0.3.0", default-features = false, features = ["image-loading"] }
# This fork contains changed for better keyboard modifier handling and DPI # This fork contains changed for better keyboard modifier handling and DPI
# scaling # scaling, window scaling, and a lot more fixes and improvements
vizia = { git = "https://github.com/robbert-vdh/vizia.git", branch = "feature/baseview-modifiers", default_features = false, features = ["baseview", "clipboard"] } vizia = { git = "https://github.com/robbert-vdh/vizia.git", branch = "patched", default_features = false, features = ["baseview", "clipboard"] }

View file

@ -55,28 +55,67 @@ where
})) }))
} }
// TODO: Once we add resizing, we may want to be able to remember the GUI size. In that case we need /// State for a `nih_plug_vizia` editor. The scale factor can be manipulated at runtime by emitting
// to make this serializable (only restoring the size of course) so it can be persisted. /// [`WindowEvent::SetScale`][vizia::WindowEvent::SetScale] events.
///
/// # TODO
///
/// Make this serializable so it can be persisted.
pub struct ViziaState { pub struct ViziaState {
/// The window's size in logical pixels before applying `scale_factor`.
size: AtomicCell<(u32, u32)>, size: AtomicCell<(u32, u32)>,
/// A scale factor that should be applied to `size` separate from from any system HiDPI scaling.
/// This can be used to allow GUIs to be scaled uniformly.
scale_factor: AtomicCell<f64>,
open: AtomicBool, open: AtomicBool,
} }
impl ViziaState { impl ViziaState {
/// Initialize the GUI's state. This value can be passed to [`create_vizia_editor()`]. The window /// Initialize the GUI's state. This value can be passed to [`create_vizia_editor()`]. The
/// size is in logical pixels, so before it is multiplied by the DPI scaling factor. /// window size is in logical pixels, so before it is multiplied by the DPI scaling factor.
pub fn from_size(width: u32, height: u32) -> Arc<ViziaState> { pub fn from_size(width: u32, height: u32) -> Arc<ViziaState> {
Arc::new(ViziaState { Arc::new(ViziaState {
size: AtomicCell::new((width, height)), size: AtomicCell::new((width, height)),
scale_factor: AtomicCell::new(1.0),
open: AtomicBool::new(false), open: AtomicBool::new(false),
}) })
} }
/// Return a `(width, height)` pair for the current size of the GUI in logical pixels. /// The same as [`from_size()`][Self::from_size()], but with a separate initial scale factor.
pub fn size(&self) -> (u32, u32) { /// This scale factor gets applied on top of any HiDPI scaling, and it can be modified at
/// runtime by emitting [`WindowEvent::SetScale`][vizia::WindowEvent::SetScale] events.
pub fn from_size_with_scale(width: u32, height: u32, scale_factor: f64) -> Arc<ViziaState> {
Arc::new(ViziaState {
size: AtomicCell::new((width, height)),
scale_factor: AtomicCell::new(scale_factor),
open: AtomicBool::new(false),
})
}
/// Return a `(width, height)` pair for the current size of the GUI in logical pixels, after
/// applying the user scale factor.
pub fn scaled_logical_size(&self) -> (u32, u32) {
let (logical_width, logical_height) = self.size.load();
let scale_factor = self.scale_factor.load();
(
(logical_width as f64 * scale_factor).round() as u32,
(logical_height as f64 * scale_factor).round() as u32,
)
}
/// Return a `(width, height)` pair for the current size of the GUI in logical pixels before
/// applying the user scale factor.
pub fn inner_logical_size(&self) -> (u32, u32) {
self.size.load() self.size.load()
} }
/// Get the non-DPI related uniform scaling factor the GUI's size will be multiplied with. This
/// can be changed by emitting [`WindowEvent::SetScale`][vizia::WindowEvent::SetScale] events.
pub fn user_scale_factor(&self) -> f64 {
self.scale_factor.load()
}
/// Whether the GUI is currently visible. /// Whether the GUI is currently visible.
// Called `is_open()` instead of `open()` to avoid the ambiguity. // Called `is_open()` instead of `open()` to avoid the ambiguity.
pub fn is_open(&self) -> bool { pub fn is_open(&self) -> bool {
@ -105,12 +144,15 @@ impl Editor for ViziaEditor {
context: Arc<dyn GuiContext>, context: Arc<dyn GuiContext>,
) -> Box<dyn std::any::Any + Send + Sync> { ) -> Box<dyn std::any::Any + Send + Sync> {
let app = self.app.clone(); let app = self.app.clone();
let vizia_state = self.vizia_state.clone();
let apply_theming = self.apply_theming; let apply_theming = self.apply_theming;
let (unscaled_width, unscaled_height) = self.vizia_state.size(); let (unscaled_width, unscaled_height) = vizia_state.inner_logical_size();
let scaling_factor = self.scaling_factor.load(); let system_scaling_factor = self.scaling_factor.load();
let window_description = let user_scale_factor = vizia_state.user_scale_factor();
WindowDescription::new().with_inner_size(unscaled_width, unscaled_height); let window_description = WindowDescription::new()
.with_inner_size(unscaled_width, unscaled_height)
.with_scale_factor(user_scale_factor);
let window = Application::new(window_description, move |cx| { let window = Application::new(window_description, move |cx| {
// Set some default styles to match the iced integration // Set some default styles to match the iced integration
@ -136,10 +178,18 @@ impl Editor for ViziaEditor {
} }
.build(cx); .build(cx);
// And we'll link `WindowEvent::ResizeWindow` and `WindowEvent::SetScale` events to our
// `ViziaState`. We'll notify the host when any of these change.
widgets::WindowModel {
context: context.clone(),
vizia_state: vizia_state.clone(),
}
.build(cx);
app(cx) app(cx)
}) })
.with_scale_policy( .with_scale_policy(
scaling_factor system_scaling_factor
.map(|factor| WindowScalePolicy::ScaleFactor(factor as f64)) .map(|factor| WindowScalePolicy::ScaleFactor(factor as f64))
.unwrap_or(WindowScalePolicy::SystemScaleFactor), .unwrap_or(WindowScalePolicy::SystemScaleFactor),
) )
@ -153,16 +203,21 @@ impl Editor for ViziaEditor {
} }
fn size(&self) -> (u32, u32) { fn size(&self) -> (u32, u32) {
self.vizia_state.size() // This includes the user scale factor if set, but not any HiDPI scaling
self.vizia_state.scaled_logical_size()
} }
fn set_scale_factor(&self, factor: f32) -> bool { fn set_scale_factor(&self, factor: f32) -> bool {
// We're making things a bit more complicated by having both a system scale factor, which is
// used for HiDPI and also known to the host, and a user scale factor that the user can use
// to arbitrarily resize the GUI
self.scaling_factor.store(Some(factor)); self.scaling_factor.store(Some(factor));
true true
} }
fn param_values_changed(&self) { fn param_values_changed(&self) {
// TODO: Update the GUI when this happens // TODO: Update the GUI when this happens, right now this happens automatically as a result
// of of the reactivity
} }
} }

View file

@ -8,7 +8,9 @@
use nih_plug::prelude::{GuiContext, Param, ParamPtr}; use nih_plug::prelude::{GuiContext, Param, ParamPtr};
use std::sync::Arc; use std::sync::Arc;
use vizia::{Context, Model}; use vizia::{Context, Model, WindowEvent};
use super::ViziaState;
mod generic_ui; mod generic_ui;
mod param_slider; mod param_slider;
@ -63,6 +65,13 @@ pub(crate) struct ParamModel {
pub context: Arc<dyn GuiContext>, pub context: Arc<dyn GuiContext>,
} }
/// Handles interactions through `WindowEvent` for VIZIA GUIs by updating the `ViziaState`.
/// Registered in [`ViziaEditor::spawn()`][super::ViziaEditor::spawn()].
pub(crate) struct WindowModel {
pub context: Arc<dyn GuiContext>,
pub vizia_state: Arc<ViziaState>,
}
impl Model for ParamModel { impl Model for ParamModel {
fn event(&mut self, _cx: &mut vizia::Context, event: &mut vizia::Event) { fn event(&mut self, _cx: &mut vizia::Context, event: &mut vizia::Event) {
if let Some(param_event) = event.message.downcast() { if let Some(param_event) = event.message.downcast() {
@ -83,6 +92,56 @@ impl Model for ParamModel {
} }
} }
impl Model for WindowModel {
fn event(&mut self, cx: &mut vizia::Context, event: &mut vizia::Event) {
if let Some(window_event) = event.message.downcast() {
match *window_event {
WindowEvent::ResizeWindow(logical_width, logical_height) => {
let logical_size =
(logical_width.round() as u32, logical_height.round() as u32);
let old_size @ (old_logical_width, old_logical_height) =
self.vizia_state.size.load();
// Don't do anything if the current size already matches the new size, this
// could otherwise also cause a feedback loop on resize failure
if logical_size == old_size {
return;
}
// Our embedded baseview window will have already been resized. If the host does
// not accept our new size, then we'll try to undo that
self.vizia_state.size.store(logical_size);
if !self.context.request_resize() {
self.vizia_state.size.store(old_size);
cx.emit(WindowEvent::ResizeWindow(
old_logical_width as f32,
old_logical_height as f32,
));
}
}
WindowEvent::SetScale(user_scale_factor) => {
let old_user_scale_factor = self.vizia_state.scale_factor.load();
// Don't do anything if the current scale already matches the new scale
if user_scale_factor == old_user_scale_factor {
return;
}
// This works the same as the `ResizeWindow` handler. The actual window size
// reported to the host gets calculated from a combination of the window's
// logical size (before user scaling) and the user scale factor.
self.vizia_state.scale_factor.store(user_scale_factor);
if !self.context.request_resize() {
self.vizia_state.scale_factor.store(old_user_scale_factor);
cx.emit(WindowEvent::SetScale(old_user_scale_factor));
}
}
_ => (),
}
}
}
}
impl<P: Param> From<ParamEvent<'_, P>> for RawParamEvent { impl<P: Param> From<ParamEvent<'_, P>> for RawParamEvent {
fn from(event: ParamEvent<'_, P>) -> Self { fn from(event: ParamEvent<'_, P>) -> Self {
match event { match event {