Add `Ui::with_visual_transform` (#5055)
* [X] I have followed the instructions in the PR template This allows you to transform widgets without having to put them on a new layer. Example usage: https://github.com/user-attachments/assets/6b547782-f15e-42ce-835f-e8febe8d2d65 ```rust use eframe::egui; use eframe::egui::{Button, Frame, InnerResponse, Label, Pos2, RichText, UiBuilder, Widget}; use eframe::emath::TSTransform; use eframe::NativeOptions; use egui::{CentralPanel, Sense, WidgetInfo}; pub fn main() -> eframe::Result { eframe::run_simple_native("focus test", NativeOptions::default(), |ctx, _frame| { CentralPanel::default().show(ctx, |ui| { let response = ui.ctx().read_response(ui.next_auto_id()); let pressed = response .as_ref() .is_some_and(|r| r.is_pointer_button_down_on()); let hovered = response.as_ref().is_some_and(|r| r.hovered()); let target_scale = match (pressed, hovered) { (true, _) => 0.94, (_, true) => 1.06, _ => 1.0, }; let scale = ui .ctx() .animate_value_with_time(ui.id().with("Down"), target_scale, 0.1); let mut center = response .as_ref() .map(|r| r.rect.center()) .unwrap_or_else(|| Pos2::new(0.0, 0.0)); if center.any_nan() { center = Pos2::new(0.0, 0.0); } let transform = TSTransform::from_translation(center.to_vec2()) * TSTransform::from_scaling(scale) * TSTransform::from_translation(-center.to_vec2()); ui.with_visual_transform(transform, |ui| { Button::new(RichText::new("Yaaaay").size(20.0)) .sense(Sense::click()) .ui(ui) }); }); }) } ```
This commit is contained in:
parent
f897405a82
commit
89b6055f9c
|
|
@ -124,10 +124,14 @@ impl PaintList {
|
|||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn next_idx(&self) -> ShapeIdx {
|
||||
ShapeIdx(self.0.len())
|
||||
}
|
||||
|
||||
/// Returns the index of the new [`Shape`] that can be used with `PaintList::set`.
|
||||
#[inline(always)]
|
||||
pub fn add(&mut self, clip_rect: Rect, shape: Shape) -> ShapeIdx {
|
||||
let idx = ShapeIdx(self.0.len());
|
||||
let idx = self.next_idx();
|
||||
self.0.push(ClippedShape { clip_rect, shape });
|
||||
idx
|
||||
}
|
||||
|
|
@ -171,6 +175,14 @@ impl PaintList {
|
|||
}
|
||||
}
|
||||
|
||||
/// Transform each [`Shape`] and clip rectangle in range by this much, in-place
|
||||
pub fn transform_range(&mut self, start: ShapeIdx, end: ShapeIdx, transform: TSTransform) {
|
||||
for ClippedShape { clip_rect, shape } in &mut self.0[start.0..end.0] {
|
||||
*clip_rect = transform.mul_rect(*clip_rect);
|
||||
shape.transform(transform);
|
||||
}
|
||||
}
|
||||
|
||||
/// Read-only access to all held shapes.
|
||||
pub fn all_entries(&self) -> impl ExactSizeIterator<Item = &ClippedShape> {
|
||||
self.0.iter()
|
||||
|
|
|
|||
|
|
@ -2690,6 +2690,33 @@ impl Ui {
|
|||
|
||||
(InnerResponse { inner, response }, payload)
|
||||
}
|
||||
|
||||
/// Create a new Scope and transform its contents via a [`emath::TSTransform`].
|
||||
/// This only affects visuals, inputs will not be transformed. So this is mostly useful
|
||||
/// to create visual effects on interactions, e.g. scaling a button on hover / click.
|
||||
///
|
||||
/// Check out [`Context::set_transform_layer`] for a persistent transform that also affects
|
||||
/// inputs.
|
||||
pub fn with_visual_transform<R>(
|
||||
&mut self,
|
||||
transform: emath::TSTransform,
|
||||
add_contents: impl FnOnce(&mut Self) -> R,
|
||||
) -> InnerResponse<R> {
|
||||
let start_idx = self.ctx().graphics(|gx| {
|
||||
gx.get(self.layer_id())
|
||||
.map_or(crate::layers::ShapeIdx(0), |l| l.next_idx())
|
||||
});
|
||||
|
||||
let r = self.scope_dyn(UiBuilder::new(), Box::new(add_contents));
|
||||
|
||||
self.ctx().graphics_mut(|g| {
|
||||
let list = g.entry(self.layer_id());
|
||||
let end_idx = list.next_idx();
|
||||
list.transform_range(start_idx, end_idx, transform);
|
||||
});
|
||||
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
/// # Menus
|
||||
|
|
|
|||
Loading…
Reference in New Issue