Add ui.allocate_at_least and ui.allocate_exact_size
This commit is contained in:
parent
f68c30e0c7
commit
b4871e2aef
15 changed files with 74 additions and 72 deletions
|
@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
## Unreleased
|
||||
|
||||
### Added ⭐
|
||||
|
||||
* Add `ui.allocate_at_least` and `ui.allocate_exact_size`.
|
||||
|
||||
## 0.7.0 - 2021-01-04
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
|||
|
||||
impl Texture {
|
||||
pub fn ui(&self, ui: &mut Ui) {
|
||||
// Show font texture in demo Ui
|
||||
ui.label(format!(
|
||||
"Texture size: {} x {} (hover to zoom)",
|
||||
self.width, self.height
|
||||
|
@ -18,8 +19,7 @@ impl Texture {
|
|||
if size.x > ui.available_width() {
|
||||
size *= ui.available_width() / size.x;
|
||||
}
|
||||
let response = ui.allocate_response(size, Sense::hover());
|
||||
let rect = response.rect;
|
||||
let (rect, response) = ui.allocate_at_least(size, Sense::hover());
|
||||
let mut triangles = Triangles::default();
|
||||
triangles.add_rect_with_uv(
|
||||
rect,
|
||||
|
|
|
@ -468,6 +468,27 @@ impl Ui {
|
|||
self.interact(rect, id, sense)
|
||||
}
|
||||
|
||||
/// Returns a `Rect` with exactly what you asked for.
|
||||
///
|
||||
/// The response rect will be larger if this is part of a justified layout or similar.
|
||||
/// This means that iof this is a narrow widget in a wide justified layout, then
|
||||
/// the widget will react to interactions outside the returned `Rect`.
|
||||
pub fn allocate_exact_size(&mut self, desired_size: Vec2, sense: Sense) -> (Rect, Response) {
|
||||
let response = self.allocate_response(desired_size, sense);
|
||||
let rect = self
|
||||
.layout()
|
||||
.align_size_within_rect(desired_size, response.rect);
|
||||
(rect, response)
|
||||
}
|
||||
|
||||
/// Allocate at least as much space as needed, and interact with that rect.
|
||||
///
|
||||
/// The returned `Rect` will be the same size as `Response::rect`.
|
||||
pub fn allocate_at_least(&mut self, desired_size: Vec2, sense: Sense) -> (Rect, Response) {
|
||||
let response = self.allocate_response(desired_size, sense);
|
||||
(response.rect, response)
|
||||
}
|
||||
|
||||
/// Reserve this much space and move the cursor.
|
||||
/// Returns where to put the widget.
|
||||
///
|
||||
|
|
|
@ -106,23 +106,19 @@ impl Widget for Button {
|
|||
desired_size.y = desired_size.y.at_least(ui.style().spacing.interact_size.y);
|
||||
}
|
||||
|
||||
let response = ui.allocate_response(desired_size, sense);
|
||||
let (rect, response) = ui.allocate_at_least(desired_size, sense);
|
||||
|
||||
if ui.clip_rect().intersects(response.rect) {
|
||||
if ui.clip_rect().intersects(rect) {
|
||||
let visuals = ui.style().interact(&response);
|
||||
let text_cursor = ui
|
||||
.layout()
|
||||
.align_size_within_rect(galley.size, response.rect.shrink2(button_padding))
|
||||
.align_size_within_rect(galley.size, rect.shrink2(button_padding))
|
||||
.min;
|
||||
|
||||
if frame {
|
||||
let fill = fill.unwrap_or(visuals.bg_fill);
|
||||
ui.painter().rect(
|
||||
response.rect,
|
||||
visuals.corner_radius,
|
||||
fill,
|
||||
visuals.bg_stroke,
|
||||
);
|
||||
ui.painter()
|
||||
.rect(rect, visuals.corner_radius, fill, visuals.bg_stroke);
|
||||
}
|
||||
|
||||
let text_color = text_color
|
||||
|
@ -189,10 +185,7 @@ impl<'a> Widget for Checkbox<'a> {
|
|||
let mut desired_size = total_extra + galley.size;
|
||||
desired_size = desired_size.at_least(spacing.interact_size);
|
||||
desired_size.y = desired_size.y.max(icon_width);
|
||||
let response = ui.allocate_response(desired_size, Sense::click());
|
||||
let rect = ui
|
||||
.layout()
|
||||
.align_size_within_rect(desired_size, response.rect);
|
||||
let (rect, response) = ui.allocate_exact_size(desired_size, Sense::click());
|
||||
if response.clicked {
|
||||
*checked = !*checked;
|
||||
}
|
||||
|
@ -283,10 +276,7 @@ impl Widget for RadioButton {
|
|||
let mut desired_size = total_extra + galley.size;
|
||||
desired_size = desired_size.at_least(ui.style().spacing.interact_size);
|
||||
desired_size.y = desired_size.y.max(icon_width);
|
||||
let response = ui.allocate_response(desired_size, Sense::click());
|
||||
let rect = ui
|
||||
.layout()
|
||||
.align_size_within_rect(desired_size, response.rect);
|
||||
let (rect, response) = ui.allocate_exact_size(desired_size, Sense::click());
|
||||
|
||||
let text_cursor = pos2(
|
||||
rect.min.x + button_padding.x + icon_width + icon_spacing,
|
||||
|
@ -390,28 +380,27 @@ impl Widget for ImageButton {
|
|||
|
||||
let button_padding = ui.style().spacing.button_padding;
|
||||
let desired_size = image.desired_size() + 2.0 * button_padding;
|
||||
let response = ui.allocate_response(desired_size, sense);
|
||||
let (rect, response) = ui.allocate_at_least(desired_size, sense);
|
||||
|
||||
if ui.clip_rect().intersects(response.rect) {
|
||||
if ui.clip_rect().intersects(rect) {
|
||||
let visuals = ui.style().interact(&response);
|
||||
|
||||
if selected {
|
||||
let selection = ui.style().visuals.selection;
|
||||
ui.painter()
|
||||
.rect(response.rect, 0.0, selection.bg_fill, selection.stroke);
|
||||
.rect(rect, 0.0, selection.bg_fill, selection.stroke);
|
||||
} else if frame {
|
||||
ui.painter().rect(
|
||||
response.rect,
|
||||
rect,
|
||||
visuals.corner_radius,
|
||||
visuals.bg_fill,
|
||||
visuals.bg_stroke,
|
||||
);
|
||||
}
|
||||
|
||||
let image_rect = ui.layout().align_size_within_rect(
|
||||
image.desired_size(),
|
||||
response.rect.shrink2(button_padding),
|
||||
);
|
||||
let image_rect = ui
|
||||
.layout()
|
||||
.align_size_within_rect(image.desired_size(), rect.shrink2(button_padding));
|
||||
image.paint_at(ui, image_rect);
|
||||
}
|
||||
|
||||
|
|
|
@ -45,10 +45,10 @@ pub fn show_color(ui: &mut Ui, color: impl Into<Color32>, desired_size: Vec2) ->
|
|||
}
|
||||
|
||||
fn show_srgba(ui: &mut Ui, srgba: Color32, desired_size: Vec2) -> Response {
|
||||
let response = ui.allocate_response(desired_size, Sense::hover());
|
||||
background_checkers(ui.painter(), response.rect);
|
||||
let (rect, response) = ui.allocate_at_least(desired_size, Sense::hover());
|
||||
background_checkers(ui.painter(), rect);
|
||||
ui.painter().add(PaintCmd::Rect {
|
||||
rect: response.rect,
|
||||
rect,
|
||||
corner_radius: 2.0,
|
||||
fill: srgba,
|
||||
stroke: Stroke::new(3.0, srgba.to_opaque()),
|
||||
|
@ -58,11 +58,11 @@ fn show_srgba(ui: &mut Ui, srgba: Color32, desired_size: Vec2) -> Response {
|
|||
|
||||
fn color_button(ui: &mut Ui, color: Color32) -> Response {
|
||||
let desired_size = ui.style().spacing.interact_size;
|
||||
let response = ui.allocate_response(desired_size, Sense::click());
|
||||
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click());
|
||||
let visuals = ui.style().interact(&response);
|
||||
background_checkers(ui.painter(), response.rect);
|
||||
background_checkers(ui.painter(), rect);
|
||||
ui.painter().add(PaintCmd::Rect {
|
||||
rect: response.rect,
|
||||
rect,
|
||||
corner_radius: visuals.corner_radius.at_most(2.0),
|
||||
fill: color,
|
||||
stroke: visuals.fg_stroke,
|
||||
|
@ -77,8 +77,7 @@ fn color_slider_1d(ui: &mut Ui, value: &mut f32, color_at: impl Fn(f32) -> Color
|
|||
ui.style().spacing.slider_width,
|
||||
ui.style().spacing.interact_size.y * 2.0,
|
||||
);
|
||||
let response = ui.allocate_response(desired_size, Sense::click_and_drag());
|
||||
let rect = response.rect;
|
||||
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click_and_drag());
|
||||
|
||||
if response.active {
|
||||
if let Some(mpos) = ui.input().mouse.pos {
|
||||
|
@ -135,8 +134,7 @@ fn color_slider_2d(
|
|||
color_at: impl Fn(f32, f32) -> Color32,
|
||||
) -> Response {
|
||||
let desired_size = Vec2::splat(ui.style().spacing.slider_width);
|
||||
let response = ui.allocate_response(desired_size, Sense::click_and_drag());
|
||||
let rect = response.rect;
|
||||
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click_and_drag());
|
||||
|
||||
if response.active {
|
||||
if let Some(mpos) = ui.input().mouse.pos {
|
||||
|
|
|
@ -46,7 +46,7 @@ impl Widget for Hyperlink {
|
|||
let text_style = text_style.unwrap_or_else(|| ui.style().body_text_style);
|
||||
let font = &ui.fonts()[text_style];
|
||||
let galley = font.layout_multiline(text, ui.available_width());
|
||||
let response = ui.allocate_response(galley.size, Sense::click());
|
||||
let (rect, response) = ui.allocate_exact_size(galley.size, Sense::click());
|
||||
|
||||
if response.hovered {
|
||||
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
|
||||
|
@ -61,7 +61,7 @@ impl Widget for Hyperlink {
|
|||
if response.hovered {
|
||||
// Underline:
|
||||
for line in &galley.rows {
|
||||
let pos = response.rect.min;
|
||||
let pos = rect.min;
|
||||
let y = pos.y + line.y_max;
|
||||
let y = ui.painter().round_to_pixel(y);
|
||||
let min_x = pos.x + line.min_x();
|
||||
|
@ -73,8 +73,7 @@ impl Widget for Hyperlink {
|
|||
}
|
||||
}
|
||||
|
||||
ui.painter()
|
||||
.galley(response.rect.min, galley, text_style, color);
|
||||
ui.painter().galley(rect.min, galley, text_style, color);
|
||||
|
||||
response.on_hover_text(url)
|
||||
}
|
||||
|
|
|
@ -73,8 +73,8 @@ impl Image {
|
|||
|
||||
impl Widget for Image {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
let response = ui.allocate_response(self.desired_size, Sense::hover());
|
||||
self.paint_at(ui, response.rect);
|
||||
let (rect, response) = ui.allocate_at_least(self.desired_size, Sense::hover());
|
||||
self.paint_at(ui, rect);
|
||||
response
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,10 +157,7 @@ impl Widget for Label {
|
|||
total_response
|
||||
} else {
|
||||
let galley = self.layout(ui);
|
||||
let response = ui.allocate_response(galley.size, Sense::click());
|
||||
let rect = ui
|
||||
.layout()
|
||||
.align_size_within_rect(galley.size, response.rect);
|
||||
let (rect, response) = ui.allocate_exact_size(galley.size, Sense::click());
|
||||
self.paint_galley(ui, rect.min, galley);
|
||||
response
|
||||
}
|
||||
|
|
|
@ -33,11 +33,11 @@ impl Widget for SelectableLabel {
|
|||
|
||||
let mut desired_size = total_extra + galley.size;
|
||||
desired_size = desired_size.at_least(ui.style().spacing.interact_size);
|
||||
let response = ui.allocate_response(desired_size, Sense::click());
|
||||
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click());
|
||||
|
||||
let text_cursor = pos2(
|
||||
response.rect.min.x + button_padding.x,
|
||||
response.rect.center().y - 0.5 * galley.size.y,
|
||||
rect.min.x + button_padding.x,
|
||||
rect.center().y - 0.5 * galley.size.y,
|
||||
);
|
||||
|
||||
let visuals = ui.style().interact(&response);
|
||||
|
@ -48,8 +48,7 @@ impl Widget for SelectableLabel {
|
|||
} else {
|
||||
Default::default()
|
||||
};
|
||||
ui.painter()
|
||||
.rect(response.rect, 0.0, bg_fill, visuals.bg_stroke);
|
||||
ui.painter().rect(rect, 0.0, bg_fill, visuals.bg_stroke);
|
||||
}
|
||||
|
||||
let text_color = ui
|
||||
|
|
|
@ -30,8 +30,7 @@ impl Widget for Separator {
|
|||
vec2(available_space.x, spacing)
|
||||
};
|
||||
|
||||
let response = ui.allocate_response(size, Sense::hover());
|
||||
let rect = response.rect;
|
||||
let (rect, response) = ui.allocate_at_least(size, Sense::hover());
|
||||
let points = if ui.layout().main_dir().is_horizontal() {
|
||||
[
|
||||
pos2(rect.center().x, rect.top()),
|
||||
|
|
|
@ -294,10 +294,10 @@ impl ColorTest {
|
|||
|
||||
fn vertex_gradient(ui: &mut Ui, bg_fill: Color32, gradient: &Gradient) -> Response {
|
||||
use egui::paint::*;
|
||||
let response = ui.allocate_response(GRADIENT_SIZE, Sense::hover());
|
||||
let (rect, response) = ui.allocate_at_least(GRADIENT_SIZE, Sense::hover());
|
||||
if bg_fill != Default::default() {
|
||||
let mut triangles = Triangles::default();
|
||||
triangles.add_colored_rect(response.rect, bg_fill);
|
||||
triangles.add_colored_rect(rect, bg_fill);
|
||||
ui.painter().add(PaintCmd::triangles(triangles));
|
||||
}
|
||||
{
|
||||
|
@ -306,9 +306,9 @@ fn vertex_gradient(ui: &mut Ui, bg_fill: Color32, gradient: &Gradient) -> Respon
|
|||
let mut triangles = Triangles::default();
|
||||
for (i, &color) in gradient.0.iter().enumerate() {
|
||||
let t = i as f32 / (n as f32 - 1.0);
|
||||
let x = lerp(response.rect.x_range(), t);
|
||||
triangles.colored_vertex(pos2(x, response.rect.top()), color);
|
||||
triangles.colored_vertex(pos2(x, response.rect.bottom()), color);
|
||||
let x = lerp(rect.x_range(), t);
|
||||
triangles.colored_vertex(pos2(x, rect.top()), color);
|
||||
triangles.colored_vertex(pos2(x, rect.bottom()), color);
|
||||
if i < n - 1 {
|
||||
let i = i as u32;
|
||||
triangles.add_triangle(2 * i, 2 * i + 1, 2 * i + 2);
|
||||
|
|
|
@ -89,10 +89,10 @@ impl DemoWindow {
|
|||
ui.horizontal(|ui| {
|
||||
ui.label("You can pretty easily paint your own small icons:");
|
||||
use std::f32::consts::TAU;
|
||||
let response = ui.allocate_response(Vec2::splat(16.0), Sense::hover());
|
||||
let (rect, _response) = ui.allocate_at_least(Vec2::splat(16.0), Sense::hover());
|
||||
let painter = ui.painter();
|
||||
let c = response.rect.center();
|
||||
let r = response.rect.width() / 2.0 - 1.0;
|
||||
let c = rect.center();
|
||||
let r = rect.width() / 2.0 - 1.0;
|
||||
let color = Color32::from_gray(128);
|
||||
let stroke = Stroke::new(1.0, color);
|
||||
painter.circle_stroke(c, r, stroke);
|
||||
|
@ -207,9 +207,9 @@ impl BoxPainting {
|
|||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
for _ in 0..self.num_boxes {
|
||||
let response = ui.allocate_response(self.size, Sense::hover());
|
||||
let (rect, _response) = ui.allocate_at_least(self.size, Sense::hover());
|
||||
ui.painter().rect(
|
||||
response.rect,
|
||||
rect,
|
||||
self.corner_radius,
|
||||
Color32::from_gray(64),
|
||||
Stroke::new(self.stroke_width, Color32::WHITE),
|
||||
|
|
|
@ -47,7 +47,7 @@ pub fn drop_target<R>(
|
|||
let mut content_ui = ui.child_ui(inner_rect, *ui.layout());
|
||||
let ret = body(&mut content_ui);
|
||||
let outer_rect = Rect::from_min_max(outer_rect_bounds.min, content_ui.min_rect().max + margin);
|
||||
let response = ui.allocate_response(outer_rect.size(), Sense::hover());
|
||||
let (rect, response) = ui.allocate_at_least(outer_rect.size(), Sense::hover());
|
||||
|
||||
let style = if is_being_dragged && can_accept_what_is_being_dragged && response.hovered {
|
||||
ui.style().visuals.widgets.active
|
||||
|
@ -65,7 +65,7 @@ pub fn drop_target<R>(
|
|||
corner_radius: style.corner_radius,
|
||||
fill: style.bg_fill,
|
||||
stroke: style.bg_stroke,
|
||||
rect: response.rect,
|
||||
rect,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ pub fn toggle(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
|
|||
// 2. Allocating space:
|
||||
// This is where we get a region of the screen assigned.
|
||||
// We also tell the Ui to sense clicks in the allocated region.
|
||||
let response = ui.allocate_response(desired_size, egui::Sense::click());
|
||||
let (rect, response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
|
||||
|
||||
// 3. Interact: Time to check for clicks!.
|
||||
if response.clicked {
|
||||
|
@ -44,7 +44,6 @@ pub fn toggle(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
|
|||
let on_bg_fill = egui::Rgba::from_rgb(0.0, 0.5, 0.25);
|
||||
let bg_fill = egui::lerp(off_bg_fill..=on_bg_fill, how_on);
|
||||
// All coordinates are in absolute screen coordinates so we use `rect` to place the elements.
|
||||
let rect = response.rect;
|
||||
let radius = 0.5 * rect.height();
|
||||
ui.painter().rect(rect, radius, bg_fill, visuals.bg_stroke);
|
||||
// Paint the circle, animating it from left to right with `how_on`:
|
||||
|
@ -62,7 +61,7 @@ pub fn toggle(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
|
|||
#[allow(dead_code)]
|
||||
fn toggle_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
|
||||
let desired_size = ui.style().spacing.interact_size;
|
||||
let response = ui.allocate_response(desired_size, egui::Sense::click());
|
||||
let (rect, response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
|
||||
*on ^= response.clicked; // toggle if clicked
|
||||
|
||||
let how_on = ui.ctx().animate_bool(response.id, *on);
|
||||
|
@ -70,7 +69,6 @@ fn toggle_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
|
|||
let off_bg_fill = egui::Rgba::TRANSPARENT;
|
||||
let on_bg_fill = egui::Rgba::from_rgb(0.0, 0.5, 0.25);
|
||||
let bg_fill = egui::lerp(off_bg_fill..=on_bg_fill, how_on);
|
||||
let rect = response.rect;
|
||||
let radius = 0.5 * rect.height();
|
||||
ui.painter().rect(rect, radius, bg_fill, visuals.bg_stroke);
|
||||
let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on);
|
||||
|
|
|
@ -66,8 +66,7 @@ impl FrameHistory {
|
|||
// TODO: we should not use `slider_width` as default graph width.
|
||||
let height = ui.style().spacing.slider_width;
|
||||
let size = vec2(ui.available_size_before_wrap_finite().x, height);
|
||||
let response = ui.allocate_response(size, Sense::hover());
|
||||
let rect = response.rect;
|
||||
let (rect, response) = ui.allocate_at_least(size, Sense::hover());
|
||||
let style = ui.style().noninteractive();
|
||||
|
||||
let mut cmds = vec![PaintCmd::Rect {
|
||||
|
|
Loading…
Reference in a new issue