diff --git a/wayvr/src/assets/edit/resize-corner.svg b/wayvr/src/assets/edit/resize-corner.svg new file mode 100644 index 00000000..def972c6 --- /dev/null +++ b/wayvr/src/assets/edit/resize-corner.svg @@ -0,0 +1 @@ + diff --git a/wayvr/src/assets/gui/edit.xml b/wayvr/src/assets/gui/edit.xml index ca50072d..1a35c16a 100644 --- a/wayvr/src/assets/gui/edit.xml +++ b/wayvr/src/assets/gui/edit.xml @@ -126,6 +126,9 @@ + diff --git a/wayvr/src/backend/wayvr/mod.rs b/wayvr/src/backend/wayvr/mod.rs index 5364ddb5..b0cb0d39 100644 --- a/wayvr/src/backend/wayvr/mod.rs +++ b/wayvr/src/backend/wayvr/mod.rs @@ -392,6 +392,8 @@ impl WvrServerState { let window_handle = wvr_server.wm.create_window( toplevel.clone(), process_handle, + min_size, + max_size, fallback_size.w as _, fallback_size.h as _, ); diff --git a/wayvr/src/backend/wayvr/window.rs b/wayvr/src/backend/wayvr/window.rs index c302304a..8d67b8c2 100644 --- a/wayvr/src/backend/wayvr/window.rs +++ b/wayvr/src/backend/wayvr/window.rs @@ -8,6 +8,8 @@ use crate::{backend::wayvr::process, gen_id}; #[derive(Debug)] pub struct Window { + pub min_size: Size, + pub max_size: Size, pub size_x: u32, pub size_y: u32, pub visible: bool, @@ -16,8 +18,15 @@ pub struct Window { } impl Window { - const fn new(toplevel: Rc, process: process::ProcessHandle) -> Self { + const fn new( + toplevel: Rc, + process: process::ProcessHandle, + min_size: Size, + max_size: Size, + ) -> Self { Self { + min_size, + max_size, size_x: 0, size_y: 0, visible: true, @@ -26,6 +35,10 @@ impl Window { } } + pub fn resizable(&self) -> bool { + self.min_size != self.max_size + } + pub fn configure_size(&mut self, size: Option>, bounds: Size) { self.toplevel.with_pending_state(|state| { state.bounds = Some(bounds); @@ -93,10 +106,12 @@ impl WindowManager { &mut self, toplevel: Rc, process: process::ProcessHandle, + min_size: Size, + max_size: Size, size_x: u32, size_y: u32, ) -> WindowHandle { - let mut window = Window::new(toplevel, process); + let mut window = Window::new(toplevel, process, min_size, max_size); window.remember_committed_size(Size::new(size_x as i32, size_y as i32)); self.windows.add(window) } diff --git a/wayvr/src/overlays/edit/mod.rs b/wayvr/src/overlays/edit/mod.rs index d9f913b2..81410dea 100644 --- a/wayvr/src/overlays/edit/mod.rs +++ b/wayvr/src/overlays/edit/mod.rs @@ -6,13 +6,14 @@ use std::{ time::{Duration, Instant}, }; -use glam::vec2; +use glam::{Vec2, vec2}; use slotmap::Key; use wgui::{ components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider}, - event::EventCallback, + event::{CallbackDataCommon, EventCallback, StyleSetRequest}, i18n::Translation, parser::Fetchable, + taffy::Display, widget::EventResult, }; use wlx_common::overlays::{BackendAttrib, BackendAttribValue, MouseTransform, StereoMode}; @@ -59,6 +60,7 @@ struct EditModeState { pos: SpriteTabHandler, stereo: SpriteTabHandler, mouse: SpriteTabHandler, + resize_uv: Option, } type EditModeWrapPanel = GuiPanel; @@ -94,6 +96,7 @@ impl EditWrapperManager { owc.backend = Box::new(EditModeBackendWrapper { inner: ManuallyDrop::new(inner), panel: ManuallyDrop::new(panel), + last_extent: [0, 0], last_render: Instant::now(), can_render_inner: false, }); @@ -135,6 +138,7 @@ pub struct EditModeBackendWrapper { panel: ManuallyDrop, inner: ManuallyDrop>, + last_extent: [u32; 2], last_render: Instant, can_render_inner: bool, } @@ -213,6 +217,8 @@ impl OverlayBackend for EditModeBackendWrapper { rdr.cmd_bufs.reverse(); } + self.last_extent = rdr.extent; + Ok(()) } fn frame_meta(&mut self) -> Option { @@ -225,7 +231,23 @@ impl OverlayBackend for EditModeBackendWrapper { ) -> HoverResult { // pass through hover events to force pipewire to capture frames for us let _ = self.inner.on_hover(app, hit); - self.panel.on_hover(app, hit) + let res = self.panel.on_hover(app, hit); + + if let Some(start_uv) = self.panel.state.resize_uv.as_mut() { + let extent_f32 = vec2(self.last_extent[0] as f32, self.last_extent[1] as f32); + + if start_uv.length_squared() > 10.0 { + // at first this is raw position in pixels, transform to UV here + *start_uv /= extent_f32; + } else { + // actually do resize + let new_res = extent_f32 * hit.uv / *start_uv; + let new_extent = [new_res.x as u32, new_res.y as u32]; + log::warn!("NEW SIZE: {new_extent:?}"); + } + } + + res } fn on_left(&mut self, app: &mut crate::state::AppState, pointer: usize) { self.inner.on_left(app, pointer); @@ -271,6 +293,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result { pos: SpriteTabHandler::default(), stereo: SpriteTabHandler::default(), mouse: SpriteTabHandler::default(), + resize_uv: None, }; let anim_mult = app.wgui_theme.animation_mult; @@ -407,6 +430,20 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result { ))); Ok(EventResult::Consumed) }), + "::EditModeResizeStart" => Box::new(move |_common, data, app, state| { + if !test_button(data) || !test_duration(&button, app) { + return Ok(EventResult::Pass); + } + state.resize_uv = data.metadata.get_mouse_pos_absolute(); + Ok(EventResult::Consumed) + }), + "::EditModeResizeStop" => Box::new(move |_common, data, app, state| { + if !test_button(data) || !test_duration(&button, app) { + return Ok(EventResult::Pass); + } + state.resize_uv = None; + Ok(EventResult::Consumed) + }), _ => return, }; @@ -564,6 +601,27 @@ fn reset_panel( c.set_checked(&mut com, adjust_mouse); } + let resizable = attrib_value!( + owc.backend.get_attrib(BackendAttrib::Resizable), + BackendAttribValue::Resizable + ) + .unwrap_or(false); + let c = panel + .parser_state + .fetch_component_as::("resize_corner")?; + let common = CallbackDataCommon { + state: &panel.layout.state, + alterables: &mut panel.layout.alterables, + }; + common.alterables.set_style( + c.get_rect(), + StyleSetRequest::Display(if resizable { + Display::Block + } else { + Display::None + }), + ); + Ok(()) } diff --git a/wayvr/src/overlays/wayvr.rs b/wayvr/src/overlays/wayvr.rs index d0859f81..f2ee2c71 100644 --- a/wayvr/src/overlays/wayvr.rs +++ b/wayvr/src/overlays/wayvr.rs @@ -86,6 +86,13 @@ pub fn create_wl_window_overlay( -0.95 }; + let resizable = app + .wvr_server + .as_mut() + .and_then(|wvr| wvr.wm.windows.get(&window)) + .map(|w| w.resizable()) + .unwrap_or(false); + Ok(OverlayWindowConfig { name: name.clone(), default_state: OverlayWindowState { @@ -113,6 +120,7 @@ pub fn create_wl_window_overlay( window, icon, (scale, curve_scale), + resizable, )?)) }) } @@ -175,6 +183,7 @@ pub struct WvrWindowBackend { scrolling: bool, overlay_id: OverlayID, scale: (f32, f32), + resizable: bool, } impl WvrWindowBackend { @@ -184,6 +193,7 @@ impl WvrWindowBackend { window: wayvr::window::WindowHandle, icon: Arc, scale: (f32, f32), + resizable: bool, ) -> anyhow::Result { let subsurface_pipeline = app.gfx.create_pipeline( app.gfx_extras.shaders.get("vert_quad").unwrap(), // want panic @@ -285,6 +295,7 @@ impl WvrWindowBackend { scrolling: false, overlay_id: OverlayID::null(), scale, + resizable, }) } @@ -300,6 +311,13 @@ impl WvrWindowBackend { if (scale_delta - 1.0).abs() > f32::EPSILON || (curve_scale_delta - 1.0).abs() > f32::EPSILON { + self.resizable = app + .wvr_server + .as_mut() + .and_then(|wvr| wvr.wm.windows.get(&self.window)) + .map(|w| w.resizable()) + .unwrap_or(false); + app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify( OverlaySelector::Id(self.overlay_id), Box::new(move |_app, owc| { @@ -978,6 +996,8 @@ impl OverlayBackend for WvrWindowBackend { BackendAttrib::StereoAdjustMouse => Some(BackendAttribValue::StereoAdjustMouse( self.stereo_adjust_mouse, )), + BackendAttrib::Resizable => Some(BackendAttribValue::Resizable(self.resizable)), + _ => None, } } diff --git a/wlx-common/src/overlays.rs b/wlx-common/src/overlays.rs index 63490e71..2728b596 100644 --- a/wlx-common/src/overlays.rs +++ b/wlx-common/src/overlays.rs @@ -26,6 +26,7 @@ pub enum BackendAttrib { StereoAdjustMouse, MouseTransform, Icon, + Resizable, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -36,9 +37,12 @@ pub enum BackendAttribValue { MouseTransform(MouseTransform), #[serde(skip_serializing, skip_deserializing)] Icon(Arc), + #[serde(skip_serializing, skip_deserializing)] + Resizable(bool), } impl BackendAttribValue { + /// Used to determine if value should be saved pub fn is_default(&self) -> bool { match self { Self::Stereo(val) => *val == StereoMode::default(), @@ -46,6 +50,7 @@ impl BackendAttribValue { Self::StereoAdjustMouse(val) => !*val, Self::MouseTransform(val) => *val == MouseTransform::default(), Self::Icon(_) => false, + Self::Resizable(_) => false, } } }