From 4f76a4abfb42233ffa1f3426d6ff23b48895ab11 Mon Sep 17 00:00:00 2001
From: galister <22305755+galister@users.noreply.github.com>
Date: Sun, 5 Jul 2026 20:02:36 +0900
Subject: [PATCH] wip: grabbable resize corner
---
wayvr/src/assets/edit/resize-corner.svg | 1 +
wayvr/src/assets/gui/edit.xml | 3 ++
wayvr/src/backend/wayvr/mod.rs | 2 +
wayvr/src/backend/wayvr/window.rs | 19 +++++++-
wayvr/src/overlays/edit/mod.rs | 64 +++++++++++++++++++++++--
wayvr/src/overlays/wayvr.rs | 20 ++++++++
wlx-common/src/overlays.rs | 5 ++
7 files changed, 109 insertions(+), 5 deletions(-)
create mode 100644 wayvr/src/assets/edit/resize-corner.svg
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,
}
}
}