Blacksite/crates/editor/src/ui/materials/uv_transform.rs
Rbanh 53dc1e44d8
Some checks are pending
CI / Format, lint, test, build (push) Waiting to run
feat: add production UI gallery and inspector system
2026-07-18 12:05:26 -04:00

132 lines
3.8 KiB
Rust

//! Shared material UV-transform authoring used by Materials and Material Instances.
use bevy::prelude::Vec2;
use bevy_egui::egui;
use egui_phosphor_icons::icons;
use shared::{MaterialInputSet, MaterialParameter, MaterialParameterValue};
use crate::ui::design_system;
use crate::ui::design_system::typography::TypeRole;
pub(super) fn uv_transform_section(
ui: &mut egui::Ui,
offset: &mut Vec2,
tiling: &mut Vec2,
reset_offset: Vec2,
reset_tiling: Vec2,
) {
let palette = design_system::palette(ui);
let width = ui.available_width().max(1.0);
let height = match super::layout::ResponsiveMaterialLayout::for_width(width) {
super::layout::ResponsiveMaterialLayout::Reference => {
crate::ui::design_system::UV_SECTION_HEIGHT
}
super::layout::ResponsiveMaterialLayout::Compact => {
crate::ui::design_system::COMPACT_UV_SECTION_HEIGHT
}
super::layout::ResponsiveMaterialLayout::Transient => 100.0,
};
let (rect, _) = ui.allocate_exact_size(egui::vec2(width, height), egui::Sense::hover());
ui.painter().rect(
rect,
5.0,
palette.section,
egui::Stroke::new(1.0_f32, palette.border),
egui::StrokeKind::Inside,
);
let mut child = ui.new_child(egui::UiBuilder::new().max_rect(rect));
child.set_clip_rect(rect.intersect(ui.clip_rect()));
let geometry = super::layout::UvSectionGeometry::for_rect(rect);
child.painter().text(
geometry.title.left_center(),
egui::Align2::LEFT_CENTER,
"UV",
TypeRole::Section.font(),
palette.text_primary,
);
vec2_group(
&mut child,
geometry.offset,
"Offset",
offset,
reset_offset,
"Reset UV offset",
);
vec2_group(
&mut child,
geometry.tiling,
"Tiling",
tiling,
reset_tiling,
"Reset UV tiling",
);
}
fn vec2_group(
ui: &mut egui::Ui,
geometry: super::layout::UvGroupGeometry,
label: &str,
value: &mut Vec2,
reset: Vec2,
tooltip: &str,
) {
let palette = design_system::palette(ui);
ui.painter().text(
geometry.label.left_center(),
egui::Align2::LEFT_CENTER,
label,
TypeRole::Small.font(),
palette.text_muted,
);
ui.put(
geometry.x,
egui::DragValue::new(&mut value.x).speed(0.01).prefix("X "),
);
ui.put(
geometry.y,
egui::DragValue::new(&mut value.y).speed(0.01).prefix("Y "),
);
if let Some(reset_rect) = geometry.reset {
let enabled = *value != reset;
let response = ui.put(
reset_rect,
egui::Button::new(
egui::RichText::new(icons::ARROW_COUNTER_CLOCKWISE.as_str()).font(
egui::FontId::new(14.0, egui::FontFamily::Name("phosphor-bold".into())),
),
),
);
if enabled && response.clicked() {
*value = reset;
}
response.on_hover_text(tooltip);
}
}
pub(super) fn material_vec2_value(inputs: &MaterialInputSet, name: &str, default: Vec2) -> Vec2 {
inputs
.values
.iter()
.find(|parameter| parameter.name == name)
.and_then(|parameter| match parameter.value {
MaterialParameterValue::Vec2(value) => Some(value),
_ => None,
})
.unwrap_or(default)
}
pub(super) fn set_material_vec2_value(inputs: &mut MaterialInputSet, name: &str, value: Vec2) {
if let Some(parameter) = inputs
.values
.iter_mut()
.find(|parameter| parameter.name == name)
{
parameter.value = MaterialParameterValue::Vec2(value);
} else {
inputs.values.push(MaterialParameter {
name: name.into(),
value: MaterialParameterValue::Vec2(value),
});
}
}