Browse Source

Add ui.allocate_at_least and ui.allocate_exact_size

pull/100/head
Emil Ernerfeldt 4 years ago
parent
commit
b4871e2aef
  1. 3
      CHANGELOG.md
  2. 4
      egui/src/introspection.rs
  3. 21
      egui/src/ui.rs
  4. 39
      egui/src/widgets/button.rs
  5. 18
      egui/src/widgets/color_picker.rs
  6. 7
      egui/src/widgets/hyperlink.rs
  7. 4
      egui/src/widgets/image.rs
  8. 5
      egui/src/widgets/label.rs
  9. 9
      egui/src/widgets/selected_label.rs
  10. 3
      egui/src/widgets/separator.rs
  11. 10
      egui_demo_lib/src/apps/color_test.rs
  12. 10
      egui_demo_lib/src/apps/demo/demo_window.rs
  13. 4
      egui_demo_lib/src/apps/demo/drag_and_drop.rs
  14. 6
      egui_demo_lib/src/apps/demo/toggle_switch.rs
  15. 3
      egui_demo_lib/src/frame_history.rs

3
CHANGELOG.md

@ -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

4
egui/src/introspection.rs

@ -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,

21
egui/src/ui.rs

@ -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.
///

39
egui/src/widgets/button.rs

@ -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);
}

18
egui/src/widgets/color_picker.rs

@ -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 {

7
egui/src/widgets/hyperlink.rs

@ -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)
}

4
egui/src/widgets/image.rs

@ -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
}
}

5
egui/src/widgets/label.rs

@ -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
}

9
egui/src/widgets/selected_label.rs

@ -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

3
egui/src/widgets/separator.rs

@ -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()),

10
egui_demo_lib/src/apps/color_test.rs

@ -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);

10
egui_demo_lib/src/apps/demo/demo_window.rs

@ -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),

4
egui_demo_lib/src/apps/demo/drag_and_drop.rs

@ -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,
},
);

6
egui_demo_lib/src/apps/demo/toggle_switch.rs

@ -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);

3
egui_demo_lib/src/frame_history.rs

@ -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…
Cancel
Save