edit: dynamic gui scale

This commit is contained in:
Aleksander
2025-11-12 19:57:38 +01:00
parent 6e11c2cf87
commit 3aee4d68a3
8 changed files with 112 additions and 44 deletions

View File

@@ -1,7 +1,7 @@
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use button::setup_custom_button; use button::setup_custom_button;
use glam::{vec2, Affine2, Vec2}; use glam::{Affine2, Vec2, vec2};
use label::setup_custom_label; use label::setup_custom_label;
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
@@ -15,14 +15,14 @@ use wgui::{
layout::{Layout, LayoutParams, WidgetID}, layout::{Layout, LayoutParams, WidgetID},
parser::ParserState, parser::ParserState,
renderer_vk::context::Context as WguiContext, renderer_vk::context::Context as WguiContext,
widget::{label::WidgetLabel, rectangle::WidgetRectangle, EventResult}, widget::{EventResult, label::WidgetLabel, rectangle::WidgetRectangle},
}; };
use crate::{ use crate::{
backend::input::{Haptics, HoverResult, PointerHit, PointerMode}, backend::input::{Haptics, HoverResult, PointerHit, PointerMode},
state::AppState, state::AppState,
subsystem::hid::WheelDelta, subsystem::hid::WheelDelta,
windowing::backend::{ui_transform, FrameMeta, OverlayBackend, RenderResources, ShouldRender}, windowing::backend::{FrameMeta, OverlayBackend, RenderResources, ShouldRender, ui_transform},
}; };
use super::{timer::GuiTimer, timestep::Timestep}; use super::{timer::GuiTimer, timestep::Timestep};
@@ -41,6 +41,7 @@ pub struct GuiPanel<S> {
pub timers: Vec<GuiTimer>, pub timers: Vec<GuiTimer>,
pub parser_state: ParserState, pub parser_state: ParserState,
pub max_size: Vec2, pub max_size: Vec2,
pub gui_scale: f32,
interaction_transform: Option<Affine2>, interaction_transform: Option<Affine2>,
context: WguiContext, context: WguiContext,
timestep: Timestep, timestep: Timestep,
@@ -56,13 +57,28 @@ pub type OnCustomIdFunc = Box<
) -> anyhow::Result<()>, ) -> anyhow::Result<()>,
>; >;
pub struct NewGuiPanelParams {
pub on_custom_id: Option<OnCustomIdFunc>, // used only in `new_from_template`
pub resize_to_parent: bool,
pub gui_scale: f32,
}
impl Default for NewGuiPanelParams {
fn default() -> Self {
Self {
on_custom_id: None,
resize_to_parent: false,
gui_scale: 1.0,
}
}
}
impl<S: 'static> GuiPanel<S> { impl<S: 'static> GuiPanel<S> {
pub fn new_from_template( pub fn new_from_template(
app: &mut AppState, app: &mut AppState,
path: &str, path: &str,
state: S, state: S,
on_custom_id: Option<OnCustomIdFunc>, params: NewGuiPanelParams,
resize_to_parent: bool,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
let custom_elems = Rc::new(RefCell::new(vec![])); let custom_elems = Rc::new(RefCell::new(vec![]));
@@ -80,10 +96,14 @@ impl<S: 'static> GuiPanel<S> {
}, },
}; };
let (mut layout, mut parser_state) = let (mut layout, mut parser_state) = wgui::parser::new_layout_from_assets(
wgui::parser::new_layout_from_assets(&doc_params, &LayoutParams { resize_to_parent })?; &doc_params,
&LayoutParams {
resize_to_parent: params.resize_to_parent,
},
)?;
if let Some(on_element_id) = on_custom_id { if let Some(on_element_id) = params.on_custom_id {
let ids = parser_state.data.ids.clone(); // FIXME: copying all ids? let ids = parser_state.data.ids.clone(); // FIXME: copying all ids?
for (id, widget) in ids { for (id, widget) in ids {
@@ -128,11 +148,21 @@ impl<S: 'static> GuiPanel<S> {
max_size: vec2(DEFAULT_MAX_SIZE as _, DEFAULT_MAX_SIZE as _), max_size: vec2(DEFAULT_MAX_SIZE as _, DEFAULT_MAX_SIZE as _),
timers: vec![], timers: vec![],
interaction_transform: None, interaction_transform: None,
gui_scale: params.gui_scale,
}) })
} }
pub fn new_blank(app: &mut AppState, state: S, resize_to_parent: bool) -> anyhow::Result<Self> { pub fn new_blank(
let layout = Layout::new(app.wgui_globals.clone(), &LayoutParams { resize_to_parent })?; app: &mut AppState,
state: S,
params: NewGuiPanelParams,
) -> anyhow::Result<Self> {
let layout = Layout::new(
app.wgui_globals.clone(),
&LayoutParams {
resize_to_parent: params.resize_to_parent,
},
)?;
let context = WguiContext::new(&mut app.wgui_shared, 1.0)?; let context = WguiContext::new(&mut app.wgui_shared, 1.0)?;
let mut timestep = Timestep::new(); let mut timestep = Timestep::new();
timestep.set_tps(60.0); timestep.set_tps(60.0);
@@ -146,6 +176,7 @@ impl<S: 'static> GuiPanel<S> {
max_size: vec2(DEFAULT_MAX_SIZE as _, DEFAULT_MAX_SIZE as _), max_size: vec2(DEFAULT_MAX_SIZE as _, DEFAULT_MAX_SIZE as _),
timers: vec![], timers: vec![],
interaction_transform: None, interaction_transform: None,
gui_scale: params.gui_scale,
}) })
} }
@@ -222,8 +253,9 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> { fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> {
self.context self.context
.update_viewport(&mut app.wgui_shared, rdr.extent, 1.0)?; .update_viewport(&mut app.wgui_shared, rdr.extent, self.gui_scale)?;
self.layout.update(self.max_size, self.timestep.alpha)?; self.layout
.update(self.max_size / self.gui_scale, self.timestep.alpha)?;
let globals = self.layout.state.globals.clone(); // sorry let globals = self.layout.state.globals.clone(); // sorry
let mut globals = globals.get(); let mut globals = globals.get();

View File

@@ -3,13 +3,13 @@ use std::sync::{Arc, LazyLock};
use crate::gui::panel::GuiPanel; use crate::gui::panel::GuiPanel;
use crate::state::AppState; use crate::state::AppState;
use crate::windowing::window::{OverlayWindowConfig, OverlayWindowState, Positioning};
use crate::windowing::Z_ORDER_ANCHOR; use crate::windowing::Z_ORDER_ANCHOR;
use crate::windowing::window::{OverlayWindowConfig, OverlayWindowState, Positioning};
pub static ANCHOR_NAME: LazyLock<Arc<str>> = LazyLock::new(|| Arc::from("anchor")); pub static ANCHOR_NAME: LazyLock<Arc<str>> = LazyLock::new(|| Arc::from("anchor"));
pub fn create_anchor(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> { pub fn create_anchor(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
let mut panel = GuiPanel::new_from_template(app, "gui/anchor.xml", (), None, false)?; let mut panel = GuiPanel::new_from_template(app, "gui/anchor.xml", (), Default::default())?;
panel.update_layout()?; panel.update_layout()?;
Ok(OverlayWindowConfig { Ok(OverlayWindowConfig {

View File

@@ -15,7 +15,7 @@ struct BarState {}
#[allow(clippy::match_same_arms)] // TODO: remove later #[allow(clippy::match_same_arms)] // TODO: remove later
pub fn create_bar(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> { pub fn create_bar(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
let state = BarState {}; let state = BarState {};
let mut panel = GuiPanel::new_from_template(app, "gui/bar.xml", state, None, false)?; let mut panel = GuiPanel::new_from_template(app, "gui/bar.xml", state, Default::default())?;
for (id, _widget_id) in &panel.parser_state.data.ids { for (id, _widget_id) in &panel.parser_state.data.ids {
match id.as_ref() { match id.as_ref() {

View File

@@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use glam::{vec3, Affine3A, Quat, Vec3}; use glam::{Affine3A, Quat, Vec3, vec3};
use crate::{ use crate::{
gui::panel::GuiPanel, gui::panel::GuiPanel,
@@ -18,7 +18,7 @@ pub fn create_custom(app: &mut AppState, name: Arc<str>) -> Option<OverlayWindow
unreachable!(); unreachable!();
let panel = GuiPanel::new_blank(app, (), false).ok()?; let panel = GuiPanel::new_blank(app, (), Default::default()).ok()?;
panel.update_layout().ok()?; panel.update_layout().ok()?;
Some(OverlayWindowConfig { Some(OverlayWindowConfig {

View File

@@ -4,11 +4,11 @@ use std::{
sync::Arc, sync::Arc,
}; };
use glam::vec2; use glam::{UVec2, vec2};
use crate::{ use crate::{
backend::input::HoverResult, backend::input::HoverResult,
gui::panel::GuiPanel, gui::panel::{GuiPanel, NewGuiPanelParams},
state::AppState, state::AppState,
subsystem::hid::WheelDelta, subsystem::hid::WheelDelta,
windowing::{ windowing::{
@@ -35,11 +35,19 @@ impl EditWrapperManager {
return Ok(()); return Ok(());
} }
let Some(meta) = owc.backend.frame_meta() else {
log::error!("META NULL");
return Ok(());
};
log::debug!("EditMode wrap on {}", owc.name); log::debug!("EditMode wrap on {}", owc.name);
let inner = mem::replace(&mut owc.backend, Box::new(DummyBackend {})); let inner = mem::replace(&mut owc.backend, Box::new(DummyBackend {}));
let mut panel = self.panel_pool.pop(); let mut panel = self.panel_pool.pop();
if panel.is_none() { if panel.is_none() {
panel = Some(make_edit_panel(app)?); panel = Some(make_edit_panel(
app,
UVec2::new(meta.extent[0], meta.extent[1]),
)?);
} }
let mut panel = panel.unwrap(); let mut panel = panel.unwrap();
panel.state = owc.name.clone(); panel.state = owc.name.clone();
@@ -99,10 +107,15 @@ impl OverlayBackend for EditModeBackendWrapper {
if !matches!(i, ShouldRender::Unable) if !matches!(i, ShouldRender::Unable)
&& let Some(ref frame_meta) = self.inner.frame_meta() && let Some(ref frame_meta) = self.inner.frame_meta()
{ {
let new_size = vec2(frame_meta.extent[0] as _, frame_meta.extent[1] as _); let (width_px, height_px) = (frame_meta.extent[0], frame_meta.extent[1]);
let new_size = vec2(width_px as _, height_px as _);
if self.panel.max_size != new_size { if self.panel.max_size != new_size {
log::debug!("EditWrapperGui size {} → {new_size}", self.panel.max_size); log::debug!("EditWrapperGui size {} → {new_size}", self.panel.max_size);
self.panel.max_size = new_size; self.panel.max_size = new_size;
let gui_scale = width_px.min(height_px) as f32 / 550.0;
self.panel.gui_scale = gui_scale;
self.panel.update_layout()?; self.panel.update_layout()?;
} }
} else { } else {
@@ -165,8 +178,25 @@ impl OverlayBackend for EditModeBackendWrapper {
} }
} }
fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> { fn make_edit_panel(
let panel = GuiPanel::new_from_template(app, "gui/edit.xml", "".into(), None, true)?; app: &mut AppState,
overlay_resolution: UVec2,
) -> anyhow::Result<EditModeWrapPanel> {
log::error!(
"overlay res {} {}",
overlay_resolution.x,
overlay_resolution.y
);
let panel = GuiPanel::new_from_template(
app,
"gui/edit.xml",
"".into(),
NewGuiPanelParams {
resize_to_parent: true,
..Default::default()
},
)?;
Ok(panel) Ok(panel)
} }

View File

@@ -53,7 +53,7 @@ pub fn create_keyboard(
processes: vec![], processes: vec![],
}; };
let mut panel = GuiPanel::new_blank(app, state, false)?; let mut panel = GuiPanel::new_blank(app, state, Default::default())?;
let globals = app.wgui_globals.clone(); let globals = app.wgui_globals.clone();
let accent_color = globals.get().defaults.accent_color; let accent_color = globals.get().defaults.accent_color;

View File

@@ -5,7 +5,7 @@ use std::{
time::Instant, time::Instant,
}; };
use glam::{vec3, Affine3A, Quat, Vec3}; use glam::{Affine3A, Quat, Vec3, vec3};
use idmap_derive::IntegerId; use idmap_derive::IntegerId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wgui::{ use wgui::{
@@ -28,8 +28,8 @@ use crate::{
gui::panel::GuiPanel, gui::panel::GuiPanel,
state::{AppState, LeftRight}, state::{AppState, LeftRight},
windowing::{ windowing::{
window::{OverlayWindowConfig, OverlayWindowState, Positioning},
OverlaySelector, Z_ORDER_TOAST, OverlaySelector, Z_ORDER_TOAST,
window::{OverlayWindowConfig, OverlayWindowState, Positioning},
}, },
}; };
@@ -170,7 +170,7 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
toast.title toast.title
}; };
let mut panel = GuiPanel::new_blank(app, (), false).ok()?; let mut panel = GuiPanel::new_blank(app, (), Default::default()).ok()?;
let globals = panel.layout.state.globals.clone(); let globals = panel.layout.state.globals.clone();

View File

@@ -3,11 +3,14 @@ use std::{collections::HashMap, rc::Rc, time::Duration};
use glam::{Affine3A, Vec3, Vec3A}; use glam::{Affine3A, Vec3, Vec3A};
use crate::{ use crate::{
gui::{panel::GuiPanel, timer::GuiTimer}, gui::{
panel::{GuiPanel, NewGuiPanelParams},
timer::GuiTimer,
},
state::AppState, state::AppState,
windowing::{ windowing::{
window::{OverlayWindowConfig, OverlayWindowData, OverlayWindowState, Positioning},
Z_ORDER_WATCH, Z_ORDER_WATCH,
window::{OverlayWindowConfig, OverlayWindowData, OverlayWindowState, Positioning},
}, },
}; };
@@ -22,7 +25,8 @@ pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<Overl
app, app,
"gui/watch.xml", "gui/watch.xml",
state, state,
Some(Box::new( NewGuiPanelParams {
on_custom_id: Some(Box::new(
move |id, widget, doc_params, layout, parser_state| { move |id, widget, doc_params, layout, parser_state| {
if &*id != "sets" { if &*id != "sets" {
return Ok(()); return Ok(());
@@ -32,12 +36,14 @@ pub fn create_watch(app: &mut AppState, num_sets: usize) -> anyhow::Result<Overl
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new(); let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert("display".into(), (idx + 1).to_string().into()); params.insert("display".into(), (idx + 1).to_string().into());
params.insert("handle".into(), idx.to_string().into()); params.insert("handle".into(), idx.to_string().into());
parser_state.instantiate_template(doc_params, "Set", layout, widget, params)?; parser_state
.instantiate_template(doc_params, "Set", layout, widget, params)?;
} }
Ok(()) Ok(())
}, },
)), )),
false, ..Default::default()
},
)?; )?;
panel panel