Add anchors to windows and areas (#310)
This is so that you can put a window in e.g. the top right corner or the center of the screen.
This commit is contained in:
parent
5667e7eb51
commit
580d27e0d3
6 changed files with 220 additions and 56 deletions
|
@ -8,14 +8,15 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md) and [
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
### Added ⭐
|
### Added ⭐
|
||||||
|
* Add anchors to windows and areas so you can put a window in e.g. the top right corner.
|
||||||
* Make labels interactive with `Label::sense(Sense::click())`.
|
* Make labels interactive with `Label::sense(Sense::click())`.
|
||||||
* Add `Response::request_focus` and `Response::surrender_focus`.
|
* Add `Response::request_focus` and `Response::surrender_focus`.
|
||||||
|
|
||||||
### Changed 🔧
|
### Changed 🔧
|
||||||
* Make `Memory::has_focus` public (again)
|
* Make `Memory::has_focus` public (again).
|
||||||
|
|
||||||
### Fixed 🐛
|
### Fixed 🐛
|
||||||
* Fix [defocus-bug on touch screens](https://github.com/emilk/egui/issues/288)
|
* Fix [defocus-bug on touch screens](https://github.com/emilk/egui/issues/288).
|
||||||
|
|
||||||
## 0.11.0 - 2021-04-05 - Optimization, screen reader & new layout logic
|
## 0.11.0 - 2021-04-05 - Optimization, screen reader & new layout logic
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ pub struct Area {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
order: Order,
|
order: Order,
|
||||||
default_pos: Option<Pos2>,
|
default_pos: Option<Pos2>,
|
||||||
|
anchor: Option<(Align2, Vec2)>,
|
||||||
new_pos: Option<Pos2>,
|
new_pos: Option<Pos2>,
|
||||||
drag_bounds: Option<Rect>,
|
drag_bounds: Option<Rect>,
|
||||||
}
|
}
|
||||||
|
@ -62,6 +63,7 @@ impl Area {
|
||||||
order: Order::Middle,
|
order: Order::Middle,
|
||||||
default_pos: None,
|
default_pos: None,
|
||||||
new_pos: None,
|
new_pos: None,
|
||||||
|
anchor: None,
|
||||||
drag_bounds: None,
|
drag_bounds: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,24 +122,46 @@ impl Area {
|
||||||
|
|
||||||
/// Positions the window and prevents it from being moved
|
/// Positions the window and prevents it from being moved
|
||||||
pub fn fixed_pos(mut self, fixed_pos: impl Into<Pos2>) -> Self {
|
pub fn fixed_pos(mut self, fixed_pos: impl Into<Pos2>) -> Self {
|
||||||
let fixed_pos = fixed_pos.into();
|
self.new_pos = Some(fixed_pos.into());
|
||||||
self.new_pos = Some(fixed_pos);
|
|
||||||
self.movable = false;
|
self.movable = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Positions the window but you can still move it.
|
/// Positions the window but you can still move it.
|
||||||
pub fn current_pos(mut self, current_pos: impl Into<Pos2>) -> Self {
|
pub fn current_pos(mut self, current_pos: impl Into<Pos2>) -> Self {
|
||||||
let current_pos = current_pos.into();
|
self.new_pos = Some(current_pos.into());
|
||||||
self.new_pos = Some(current_pos);
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set anchor and distance.
|
||||||
|
///
|
||||||
|
/// An anchor of `Align2::RIGHT_TOP` means "put the right-top corner of the window
|
||||||
|
/// in the right-top corner of the screen".
|
||||||
|
///
|
||||||
|
/// The offset is added to the position, so e.g. an offset of `[-5.0, 5.0]`
|
||||||
|
/// would move the window left and down from the given anchor.
|
||||||
|
///
|
||||||
|
/// Anchoring also makes the window immovable.
|
||||||
|
///
|
||||||
|
/// It is an error to set both an anchor and a position.
|
||||||
|
pub fn anchor(mut self, align: Align2, offset: impl Into<Vec2>) -> Self {
|
||||||
|
self.anchor = Some((align, offset.into()));
|
||||||
|
self.movable(false)
|
||||||
|
}
|
||||||
|
|
||||||
/// Constrain the area up to which the window can be dragged.
|
/// Constrain the area up to which the window can be dragged.
|
||||||
pub fn drag_bounds(mut self, bounds: Rect) -> Self {
|
pub fn drag_bounds(mut self, bounds: Rect) -> Self {
|
||||||
self.drag_bounds = Some(bounds);
|
self.drag_bounds = Some(bounds);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_pivot(&self) -> Align2 {
|
||||||
|
if let Some((pivot, _)) = self.anchor {
|
||||||
|
pivot
|
||||||
|
} else {
|
||||||
|
Align2::LEFT_TOP
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Prepared {
|
pub(crate) struct Prepared {
|
||||||
|
@ -158,18 +182,31 @@ impl Area {
|
||||||
enabled,
|
enabled,
|
||||||
default_pos,
|
default_pos,
|
||||||
new_pos,
|
new_pos,
|
||||||
|
anchor,
|
||||||
drag_bounds,
|
drag_bounds,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let layer_id = LayerId::new(order, id);
|
let layer_id = LayerId::new(order, id);
|
||||||
|
|
||||||
let state = ctx.memory().areas.get(id).cloned();
|
let state = ctx.memory().areas.get(id).cloned();
|
||||||
|
let is_new = state.is_none();
|
||||||
let mut state = state.unwrap_or_else(|| State {
|
let mut state = state.unwrap_or_else(|| State {
|
||||||
pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)),
|
pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)),
|
||||||
size: Vec2::ZERO,
|
size: Vec2::ZERO,
|
||||||
interactable,
|
interactable,
|
||||||
});
|
});
|
||||||
state.pos = new_pos.unwrap_or(state.pos);
|
state.pos = new_pos.unwrap_or(state.pos);
|
||||||
|
|
||||||
|
if let Some((anchor, offset)) = anchor {
|
||||||
|
if is_new {
|
||||||
|
// unknown size
|
||||||
|
ctx.request_repaint()
|
||||||
|
} else {
|
||||||
|
let screen = ctx.available_rect();
|
||||||
|
state.pos = anchor.align_size_within_rect(state.size, screen).min + offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state.pos = ctx.round_pos_to_pixels(state.pos);
|
state.pos = ctx.round_pos_to_pixels(state.pos);
|
||||||
|
|
||||||
Prepared {
|
Prepared {
|
||||||
|
|
|
@ -318,17 +318,20 @@ use epaint::Stroke;
|
||||||
|
|
||||||
pub fn paint_resize_corner(ui: &mut Ui, response: &Response) {
|
pub fn paint_resize_corner(ui: &mut Ui, response: &Response) {
|
||||||
let stroke = ui.style().interact(response).fg_stroke;
|
let stroke = ui.style().interact(response).fg_stroke;
|
||||||
paint_resize_corner_with_style(ui, &response.rect, stroke);
|
paint_resize_corner_with_style(ui, &response.rect, stroke, Align2::RIGHT_BOTTOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint_resize_corner_with_style(ui: &mut Ui, rect: &Rect, stroke: Stroke) {
|
pub fn paint_resize_corner_with_style(ui: &mut Ui, rect: &Rect, stroke: Stroke, corner: Align2) {
|
||||||
let painter = ui.painter();
|
let painter = ui.painter();
|
||||||
let corner = painter.round_pos_to_pixels(rect.right_bottom());
|
let cp = painter.round_pos_to_pixels(corner.pos_in_rect(rect));
|
||||||
let mut w = 2.0;
|
let mut w = 2.0;
|
||||||
|
|
||||||
while w <= rect.width() && w <= rect.height() {
|
while w <= rect.width() && w <= rect.height() {
|
||||||
painter.line_segment(
|
painter.line_segment(
|
||||||
[pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)],
|
[
|
||||||
|
pos2(cp.x - w * corner.x().to_sign(), cp.y),
|
||||||
|
pos2(cp.x, cp.y - w * corner.y().to_sign()),
|
||||||
|
],
|
||||||
stroke,
|
stroke,
|
||||||
);
|
);
|
||||||
w += 4.0;
|
w += 4.0;
|
||||||
|
|
|
@ -121,6 +121,22 @@ impl<'open> Window<'open> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set anchor and distance.
|
||||||
|
///
|
||||||
|
/// An anchor of `Align2::RIGHT_TOP` means "put the right-top corner of the window
|
||||||
|
/// in the right-top corner of the screen".
|
||||||
|
///
|
||||||
|
/// The offset is added to the position, so e.g. an offset of `[-5.0, 5.0]`
|
||||||
|
/// would move the window left and down from the given anchor.
|
||||||
|
///
|
||||||
|
/// Anchoring also makes the window immovable.
|
||||||
|
///
|
||||||
|
/// It is an error to set both an anchor and a position.
|
||||||
|
pub fn anchor(mut self, align: Align2, offset: impl Into<Vec2>) -> Self {
|
||||||
|
self.area = self.area.anchor(align, offset);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set initial size of the window.
|
/// Set initial size of the window.
|
||||||
pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {
|
pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {
|
||||||
self.resize = self.resize.default_size(default_size);
|
self.resize = self.resize.default_size(default_size);
|
||||||
|
@ -248,12 +264,9 @@ impl<'open> Window<'open> {
|
||||||
let resize_id = area_id.with("resize");
|
let resize_id = area_id.with("resize");
|
||||||
let collapsing_id = area_id.with("collapsing");
|
let collapsing_id = area_id.with("collapsing");
|
||||||
|
|
||||||
let is_maximized = !with_title_bar
|
let is_collapsed = with_title_bar
|
||||||
|| collapsing_header::State::is_open(ctx, collapsing_id).unwrap_or_default();
|
&& !collapsing_header::State::is_open(ctx, collapsing_id).unwrap_or_default();
|
||||||
let possible = PossibleInteractions {
|
let possible = PossibleInteractions::new(&area, &resize, is_collapsed);
|
||||||
movable: area.is_enabled() && area.is_movable(),
|
|
||||||
resizable: area.is_enabled() && resize.is_resizable() && is_maximized,
|
|
||||||
};
|
|
||||||
|
|
||||||
let area = area.movable(false); // We move it manually
|
let area = area.movable(false); // We move it manually
|
||||||
let resize = resize.resizable(false); // We move it manually
|
let resize = resize.resizable(false); // We move it manually
|
||||||
|
@ -265,7 +278,7 @@ impl<'open> Window<'open> {
|
||||||
|
|
||||||
// First interact (move etc) to avoid frame delay:
|
// First interact (move etc) to avoid frame delay:
|
||||||
let last_frame_outer_rect = area.state().rect();
|
let last_frame_outer_rect = area.state().rect();
|
||||||
let interaction = if possible.movable || possible.resizable {
|
let interaction = if possible.movable || possible.resizable() {
|
||||||
window_interaction(
|
window_interaction(
|
||||||
ctx,
|
ctx,
|
||||||
possible,
|
possible,
|
||||||
|
@ -344,10 +357,7 @@ impl<'open> Window<'open> {
|
||||||
.map(|ir| ir.response);
|
.map(|ir| ir.response);
|
||||||
|
|
||||||
let outer_rect = frame.end(&mut area_content_ui).rect;
|
let outer_rect = frame.end(&mut area_content_ui).rect;
|
||||||
|
paint_resize_corner(&mut area_content_ui, &possible, outer_rect, frame_stroke);
|
||||||
if possible.resizable {
|
|
||||||
paint_resize_corner(&mut area_content_ui, outer_rect, frame_stroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
// END FRAME --------------------------------
|
// END FRAME --------------------------------
|
||||||
|
|
||||||
|
@ -391,12 +401,28 @@ impl<'open> Window<'open> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_resize_corner(ui: &mut Ui, outer_rect: Rect, stroke: Stroke) {
|
fn paint_resize_corner(
|
||||||
|
ui: &mut Ui,
|
||||||
|
possible: &PossibleInteractions,
|
||||||
|
outer_rect: Rect,
|
||||||
|
stroke: Stroke,
|
||||||
|
) {
|
||||||
|
let corner = if possible.resize_right && possible.resize_bottom {
|
||||||
|
Align2::RIGHT_BOTTOM
|
||||||
|
} else if possible.resize_left && possible.resize_bottom {
|
||||||
|
Align2::LEFT_BOTTOM
|
||||||
|
} else if possible.resize_left && possible.resize_top {
|
||||||
|
Align2::LEFT_TOP
|
||||||
|
} else if possible.resize_right && possible.resize_top {
|
||||||
|
Align2::RIGHT_TOP
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let corner_size = Vec2::splat(ui.visuals().resize_corner_size);
|
let corner_size = Vec2::splat(ui.visuals().resize_corner_size);
|
||||||
let handle_offset = -Vec2::splat(2.0);
|
let corner_rect = corner.align_size_within_rect(corner_size, outer_rect);
|
||||||
let corner_rect =
|
let corner_rect = corner_rect.translate(-2.0 * corner.to_sign()); // move away from corner
|
||||||
Rect::from_min_size(outer_rect.max - corner_size + handle_offset, corner_size);
|
crate::resize::paint_resize_corner_with_style(ui, &corner_rect, stroke, corner);
|
||||||
crate::resize::paint_resize_corner_with_style(ui, &corner_rect, stroke);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -404,7 +430,30 @@ fn paint_resize_corner(ui: &mut Ui, outer_rect: Rect, stroke: Stroke) {
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct PossibleInteractions {
|
struct PossibleInteractions {
|
||||||
movable: bool,
|
movable: bool,
|
||||||
resizable: bool,
|
// Which sized can we drag to resize?
|
||||||
|
resize_left: bool,
|
||||||
|
resize_right: bool,
|
||||||
|
resize_top: bool,
|
||||||
|
resize_bottom: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PossibleInteractions {
|
||||||
|
fn new(area: &Area, resize: &Resize, is_collapsed: bool) -> Self {
|
||||||
|
let movable = area.is_enabled() && area.is_movable();
|
||||||
|
let resizable = area.is_enabled() && resize.is_resizable() && !is_collapsed;
|
||||||
|
let pivot = area.get_pivot();
|
||||||
|
Self {
|
||||||
|
movable,
|
||||||
|
resize_left: resizable && (movable || pivot.x() != Align::LEFT),
|
||||||
|
resize_right: resizable && (movable || pivot.x() != Align::RIGHT),
|
||||||
|
resize_top: resizable && (movable || pivot.y() != Align::TOP),
|
||||||
|
resize_bottom: resizable && (movable || pivot.y() != Align::BOTTOM),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resizable(&self) -> bool {
|
||||||
|
self.resize_left || self.resize_right || self.resize_top || self.resize_bottom
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Either a move or resize
|
/// Either a move or resize
|
||||||
|
@ -550,13 +599,13 @@ fn resize_hover(
|
||||||
area_layer_id: LayerId,
|
area_layer_id: LayerId,
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
) -> Option<WindowInteraction> {
|
) -> Option<WindowInteraction> {
|
||||||
let pointer_pos = ctx.input().pointer.interact_pos()?;
|
let pointer = ctx.input().pointer.interact_pos()?;
|
||||||
|
|
||||||
if ctx.input().pointer.any_down() && !ctx.input().pointer.any_pressed() {
|
if ctx.input().pointer.any_down() && !ctx.input().pointer.any_pressed() {
|
||||||
return None; // already dragging (something)
|
return None; // already dragging (something)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(top_layer_id) = ctx.layer_id_at(pointer_pos) {
|
if let Some(top_layer_id) = ctx.layer_id_at(pointer) {
|
||||||
if top_layer_id != area_layer_id && top_layer_id.order != Order::Background {
|
if top_layer_id != area_layer_id && top_layer_id.order != Order::Background {
|
||||||
return None; // Another window is on top here
|
return None; // Another window is on top here
|
||||||
}
|
}
|
||||||
|
@ -569,38 +618,45 @@ fn resize_hover(
|
||||||
|
|
||||||
let side_grab_radius = ctx.style().interaction.resize_grab_radius_side;
|
let side_grab_radius = ctx.style().interaction.resize_grab_radius_side;
|
||||||
let corner_grab_radius = ctx.style().interaction.resize_grab_radius_corner;
|
let corner_grab_radius = ctx.style().interaction.resize_grab_radius_corner;
|
||||||
if !rect.expand(side_grab_radius).contains(pointer_pos) {
|
if !rect.expand(side_grab_radius).contains(pointer) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mut left, mut right, mut top, mut bottom) = Default::default();
|
let mut left = possible.resize_left && (rect.left() - pointer.x).abs() <= side_grab_radius;
|
||||||
if possible.resizable {
|
let mut right = possible.resize_right && (rect.right() - pointer.x).abs() <= side_grab_radius;
|
||||||
right = (rect.right() - pointer_pos.x).abs() <= side_grab_radius;
|
let mut top = possible.resize_top && (rect.top() - pointer.y).abs() <= side_grab_radius;
|
||||||
bottom = (rect.bottom() - pointer_pos.y).abs() <= side_grab_radius;
|
let mut bottom =
|
||||||
|
possible.resize_bottom && (rect.bottom() - pointer.y).abs() <= side_grab_radius;
|
||||||
|
|
||||||
if rect.right_bottom().distance(pointer_pos) < corner_grab_radius {
|
if possible.resize_right
|
||||||
|
&& possible.resize_bottom
|
||||||
|
&& rect.right_bottom().distance(pointer) < corner_grab_radius
|
||||||
|
{
|
||||||
right = true;
|
right = true;
|
||||||
bottom = true;
|
bottom = true;
|
||||||
}
|
}
|
||||||
|
if possible.resize_right
|
||||||
if possible.movable {
|
&& possible.resize_top
|
||||||
left = (rect.left() - pointer_pos.x).abs() <= side_grab_radius;
|
&& rect.right_top().distance(pointer) < corner_grab_radius
|
||||||
top = (rect.top() - pointer_pos.y).abs() <= side_grab_radius;
|
{
|
||||||
|
|
||||||
if rect.right_top().distance(pointer_pos) < corner_grab_radius {
|
|
||||||
right = true;
|
right = true;
|
||||||
top = true;
|
top = true;
|
||||||
}
|
}
|
||||||
if rect.left_top().distance(pointer_pos) < corner_grab_radius {
|
if possible.resize_left
|
||||||
|
&& possible.resize_top
|
||||||
|
&& rect.left_top().distance(pointer) < corner_grab_radius
|
||||||
|
{
|
||||||
left = true;
|
left = true;
|
||||||
top = true;
|
top = true;
|
||||||
}
|
}
|
||||||
if rect.left_bottom().distance(pointer_pos) < corner_grab_radius {
|
if possible.resize_left
|
||||||
|
&& possible.resize_bottom
|
||||||
|
&& rect.left_bottom().distance(pointer) < corner_grab_radius
|
||||||
|
{
|
||||||
left = true;
|
left = true;
|
||||||
bottom = true;
|
bottom = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
let any_resize = left || right || top || bottom;
|
let any_resize = left || right || top || bottom;
|
||||||
|
|
||||||
if !any_resize && !possible.movable {
|
if !any_resize && !possible.movable {
|
||||||
|
|
|
@ -8,6 +8,10 @@ pub struct WindowOptions {
|
||||||
resizable: bool,
|
resizable: bool,
|
||||||
scroll: bool,
|
scroll: bool,
|
||||||
disabled_time: f64,
|
disabled_time: f64,
|
||||||
|
|
||||||
|
anchored: bool,
|
||||||
|
anchor: egui::Align2,
|
||||||
|
anchor_offset: egui::Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WindowOptions {
|
impl Default for WindowOptions {
|
||||||
|
@ -20,6 +24,9 @@ impl Default for WindowOptions {
|
||||||
resizable: true,
|
resizable: true,
|
||||||
scroll: false,
|
scroll: false,
|
||||||
disabled_time: f64::NEG_INFINITY,
|
disabled_time: f64::NEG_INFINITY,
|
||||||
|
anchored: false,
|
||||||
|
anchor: egui::Align2::RIGHT_TOP,
|
||||||
|
anchor_offset: egui::Vec2::ZERO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +45,9 @@ impl super::Demo for WindowOptions {
|
||||||
resizable,
|
resizable,
|
||||||
scroll,
|
scroll,
|
||||||
disabled_time,
|
disabled_time,
|
||||||
|
anchored,
|
||||||
|
anchor,
|
||||||
|
anchor_offset,
|
||||||
} = self.clone();
|
} = self.clone();
|
||||||
|
|
||||||
let enabled = ctx.input().time - disabled_time > 2.0;
|
let enabled = ctx.input().time - disabled_time > 2.0;
|
||||||
|
@ -56,6 +66,9 @@ impl super::Demo for WindowOptions {
|
||||||
if closable {
|
if closable {
|
||||||
window = window.open(open);
|
window = window.open(open);
|
||||||
}
|
}
|
||||||
|
if anchored {
|
||||||
|
window = window.anchor(anchor, anchor_offset);
|
||||||
|
}
|
||||||
window.show(ctx, |ui| self.ui(ui));
|
window.show(ctx, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +83,9 @@ impl super::View for WindowOptions {
|
||||||
resizable,
|
resizable,
|
||||||
scroll,
|
scroll,
|
||||||
disabled_time,
|
disabled_time,
|
||||||
|
anchored,
|
||||||
|
anchor,
|
||||||
|
anchor_offset,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
@ -82,6 +98,28 @@ impl super::View for WindowOptions {
|
||||||
ui.checkbox(resizable, "resizable");
|
ui.checkbox(resizable, "resizable");
|
||||||
ui.checkbox(scroll, "scroll");
|
ui.checkbox(scroll, "scroll");
|
||||||
|
|
||||||
|
ui.group(|ui| {
|
||||||
|
ui.checkbox(anchored, "anchored");
|
||||||
|
ui.set_enabled(*anchored);
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("x:");
|
||||||
|
ui.selectable_value(&mut anchor.0[0], egui::Align::LEFT, "Left");
|
||||||
|
ui.selectable_value(&mut anchor.0[0], egui::Align::Center, "Center");
|
||||||
|
ui.selectable_value(&mut anchor.0[0], egui::Align::RIGHT, "Right");
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("y:");
|
||||||
|
ui.selectable_value(&mut anchor.0[1], egui::Align::TOP, "Top");
|
||||||
|
ui.selectable_value(&mut anchor.0[1], egui::Align::Center, "Center");
|
||||||
|
ui.selectable_value(&mut anchor.0[1], egui::Align::BOTTOM, "Bottom");
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Offset:");
|
||||||
|
ui.add(egui::DragValue::new(&mut anchor_offset.x));
|
||||||
|
ui.add(egui::DragValue::new(&mut anchor_offset.y));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if ui.button("Disable for 2 seconds").clicked() {
|
if ui.button("Disable for 2 seconds").clicked() {
|
||||||
*disabled_time = ui.input().time;
|
*disabled_time = ui.input().time;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,15 @@ impl Align {
|
||||||
Self::Max => 1.0,
|
Self::Max => 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert `Min => -1.0`, `Center => 0.0` or `Max => 1.0`.
|
||||||
|
pub fn to_sign(&self) -> f32 {
|
||||||
|
match self {
|
||||||
|
Self::Min => -1.0,
|
||||||
|
Self::Center => 0.0,
|
||||||
|
Self::Max => 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Align {
|
impl Default for Align {
|
||||||
|
@ -88,6 +97,11 @@ impl Align2 {
|
||||||
self.0[1]
|
self.0[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// -1, 0, or +1 for each axis
|
||||||
|
pub fn to_sign(&self) -> Vec2 {
|
||||||
|
vec2(self.x().to_sign(), self.y().to_sign())
|
||||||
|
}
|
||||||
|
|
||||||
/// Used e.g. to anchor a piece of text to a part of the rectangle.
|
/// Used e.g. to anchor a piece of text to a part of the rectangle.
|
||||||
/// Give a position within the rect, specified by the aligns
|
/// Give a position within the rect, specified by the aligns
|
||||||
pub fn anchor_rect(self, rect: Rect) -> Rect {
|
pub fn anchor_rect(self, rect: Rect) -> Rect {
|
||||||
|
@ -119,6 +133,21 @@ impl Align2 {
|
||||||
|
|
||||||
Rect::from_min_size(Pos2::new(x, y), size)
|
Rect::from_min_size(Pos2::new(x, y), size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pos_in_rect(self, frame: &Rect) -> Pos2 {
|
||||||
|
let x = match self.x() {
|
||||||
|
Align::Min => frame.left(),
|
||||||
|
Align::Center => frame.center().x,
|
||||||
|
Align::Max => frame.right(),
|
||||||
|
};
|
||||||
|
let y = match self.y() {
|
||||||
|
Align::Min => frame.top(),
|
||||||
|
Align::Center => frame.center().y,
|
||||||
|
Align::Max => frame.bottom(),
|
||||||
|
};
|
||||||
|
|
||||||
|
pos2(x, y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn center_size_in_rect(size: Vec2, frame: Rect) -> Rect {
|
pub fn center_size_in_rect(size: Vec2, frame: Rect) -> Rect {
|
||||||
|
|
Loading…
Reference in a new issue