Refactor layout (#241)
* Fix https://github.com/emilk/egui/issues/222 * Rewrite layout logic Cursor is now a Rect. Closes https://github.com/emilk/egui/issues/179
This commit is contained in:
parent
5621a46b4b
commit
589bae1211
5 changed files with 308 additions and 231 deletions
|
@ -61,6 +61,15 @@ impl GridLayout {
|
||||||
pub(crate) fn new(ui: &Ui, id: Id) -> Self {
|
pub(crate) fn new(ui: &Ui, id: Id) -> Self {
|
||||||
let prev_state = ui.memory().grid.get(&id).cloned().unwrap_or_default();
|
let prev_state = ui.memory().grid.get(&id).cloned().unwrap_or_default();
|
||||||
|
|
||||||
|
// TODO: respect current layout
|
||||||
|
|
||||||
|
let available = ui.placer().max_rect().intersect(ui.cursor());
|
||||||
|
let initial_x = available.min.x;
|
||||||
|
assert!(
|
||||||
|
initial_x.is_finite(),
|
||||||
|
"Grid not yet available for right-to-left layouts"
|
||||||
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
ctx: ui.ctx().clone(),
|
ctx: ui.ctx().clone(),
|
||||||
style: ui.style().clone(),
|
style: ui.style().clone(),
|
||||||
|
@ -69,7 +78,7 @@ impl GridLayout {
|
||||||
curr_state: State::default(),
|
curr_state: State::default(),
|
||||||
spacing: ui.spacing().item_spacing,
|
spacing: ui.spacing().item_spacing,
|
||||||
striped: false,
|
striped: false,
|
||||||
initial_x: ui.cursor().x,
|
initial_x,
|
||||||
min_cell_size: ui.spacing().interact_size,
|
min_cell_size: ui.spacing().interact_size,
|
||||||
max_cell_size: Vec2::INFINITY,
|
max_cell_size: Vec2::INFINITY,
|
||||||
col: 0,
|
col: 0,
|
||||||
|
@ -95,10 +104,6 @@ impl GridLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn available_rect(&self, region: &Region) -> Rect {
|
pub(crate) fn available_rect(&self, region: &Region) -> Rect {
|
||||||
// let mut rect = Rect::from_min_max(region.cursor, region.max_rect.max);
|
|
||||||
// rect.set_height(rect.height().at_least(self.min_cell_size.y));
|
|
||||||
// rect
|
|
||||||
|
|
||||||
// required for putting CollapsingHeader in anything but the last column:
|
// required for putting CollapsingHeader in anything but the last column:
|
||||||
self.available_rect_finite(region)
|
self.available_rect_finite(region)
|
||||||
}
|
}
|
||||||
|
@ -116,19 +121,21 @@ impl GridLayout {
|
||||||
.unwrap_or(self.min_cell_size.x)
|
.unwrap_or(self.min_cell_size.x)
|
||||||
};
|
};
|
||||||
|
|
||||||
let height = region.max_rect_finite().max.y - region.cursor.y;
|
let available = region.max_rect.intersect(region.cursor);
|
||||||
|
|
||||||
|
let height = region.max_rect_finite().max.y - available.top();
|
||||||
let height = height
|
let height = height
|
||||||
.at_least(self.min_cell_size.y)
|
.at_least(self.min_cell_size.y)
|
||||||
.at_most(self.max_cell_size.y);
|
.at_most(self.max_cell_size.y);
|
||||||
|
|
||||||
Rect::from_min_size(region.cursor, vec2(width, height))
|
Rect::from_min_size(available.min, vec2(width, height))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn next_cell(&self, cursor: Pos2, child_size: Vec2) -> Rect {
|
pub(crate) fn next_cell(&self, cursor: Rect, child_size: Vec2) -> Rect {
|
||||||
let width = self.prev_state.col_width(self.col).unwrap_or(0.0);
|
let width = self.prev_state.col_width(self.col).unwrap_or(0.0);
|
||||||
let height = self.prev_row_height(self.row);
|
let height = self.prev_row_height(self.row);
|
||||||
let size = child_size.max(vec2(width, height));
|
let size = child_size.max(vec2(width, height));
|
||||||
Rect::from_min_size(cursor, size)
|
Rect::from_min_size(cursor.min, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn align_size_within_rect(&self, size: Vec2, frame: Rect) -> Rect {
|
pub(crate) fn align_size_within_rect(&self, size: Vec2, frame: Rect) -> Rect {
|
||||||
|
@ -140,7 +147,7 @@ impl GridLayout {
|
||||||
self.align_size_within_rect(size, frame)
|
self.align_size_within_rect(size, frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn advance(&mut self, cursor: &mut Pos2, frame_rect: Rect, widget_rect: Rect) {
|
pub(crate) fn advance(&mut self, cursor: &mut Rect, frame_rect: Rect, widget_rect: Rect) {
|
||||||
let debug_expand_width = self.style.visuals.debug_expand_width;
|
let debug_expand_width = self.style.visuals.debug_expand_width;
|
||||||
let debug_expand_height = self.style.visuals.debug_expand_height;
|
let debug_expand_height = self.style.visuals.debug_expand_height;
|
||||||
if debug_expand_width || debug_expand_height {
|
if debug_expand_width || debug_expand_height {
|
||||||
|
@ -171,14 +178,14 @@ impl GridLayout {
|
||||||
);
|
);
|
||||||
|
|
||||||
self.col += 1;
|
self.col += 1;
|
||||||
cursor.x += frame_rect.width() + self.spacing.x;
|
cursor.min.x += frame_rect.width() + self.spacing.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn end_row(&mut self, cursor: &mut Pos2, painter: &Painter) {
|
pub(crate) fn end_row(&mut self, cursor: &mut Rect, painter: &Painter) {
|
||||||
let row_height = self.prev_row_height(self.row);
|
let row_height = self.prev_row_height(self.row);
|
||||||
|
|
||||||
cursor.x = self.initial_x;
|
cursor.min.x = self.initial_x;
|
||||||
cursor.y += row_height + self.spacing.y;
|
cursor.min.y += row_height + self.spacing.y;
|
||||||
self.col = 0;
|
self.col = 0;
|
||||||
self.row += 1;
|
self.row += 1;
|
||||||
|
|
||||||
|
@ -186,7 +193,7 @@ impl GridLayout {
|
||||||
if let Some(height) = self.prev_state.row_height(self.row) {
|
if let Some(height) = self.prev_state.row_height(self.row) {
|
||||||
// Paint background for coming row:
|
// Paint background for coming row:
|
||||||
let size = Vec2::new(self.prev_state.full_width(self.spacing.x), height);
|
let size = Vec2::new(self.prev_state.full_width(self.spacing.x), height);
|
||||||
let rect = Rect::from_min_size(*cursor, size);
|
let rect = Rect::from_min_size(cursor.min, size);
|
||||||
let rect = rect.expand2(0.5 * self.spacing.y * Vec2::Y);
|
let rect = rect.expand2(0.5 * self.spacing.y * Vec2::Y);
|
||||||
let rect = rect.expand2(2.0 * Vec2::X); // HACK: just looks better with some spacing on the sides
|
let rect = rect.expand2(2.0 * Vec2::X); // HACK: just looks better with some spacing on the sides
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{emath::*, Align};
|
use crate::{emath::*, Align};
|
||||||
|
use std::f32::INFINITY;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -29,9 +30,16 @@ pub(crate) struct Region {
|
||||||
pub max_rect: Rect,
|
pub max_rect: Rect,
|
||||||
|
|
||||||
/// Where the next widget will be put.
|
/// Where the next widget will be put.
|
||||||
|
///
|
||||||
|
/// One side of this will always be infinite: the direction in which new widgets will be added.
|
||||||
|
/// The opposing side is what is incremented.
|
||||||
|
/// The crossing sides are initialized to `max_rect`.
|
||||||
|
///
|
||||||
|
/// So one can think of `cursor` as a constraint on the available region.
|
||||||
|
///
|
||||||
/// If something has already been added, this will point ot `style.spacing.item_spacing` beyond the latest child.
|
/// If something has already been added, this will point ot `style.spacing.item_spacing` beyond the latest child.
|
||||||
/// The cursor can thus be `style.spacing.item_spacing` pixels outside of the min_rect.
|
/// The cursor can thus be `style.spacing.item_spacing` pixels outside of the min_rect.
|
||||||
pub(crate) cursor: Pos2,
|
pub(crate) cursor: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Region {
|
impl Region {
|
||||||
|
@ -61,14 +69,18 @@ impl Region {
|
||||||
self.max_rect = self.max_rect.union(rect);
|
self.max_rect = self.max_rect.union(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure we are big enough to contain the given x-coordinate.
|
/// Ensure we are big enough to contain the given X-coordinate.
|
||||||
/// This is sometimes useful to expand an ui to stretch to a certain place.
|
/// This is sometimes useful to expand an ui to stretch to a certain place.
|
||||||
pub fn expand_to_include_x(&mut self, x: f32) {
|
pub fn expand_to_include_x(&mut self, x: f32) {
|
||||||
self.min_rect.min.x = self.min_rect.min.x.min(x);
|
self.min_rect.extend_with_x(x);
|
||||||
self.min_rect.max.x = self.min_rect.max.x.max(x);
|
self.max_rect.extend_with_x(x);
|
||||||
|
}
|
||||||
|
|
||||||
self.max_rect.min.x = self.max_rect.min.x.min(x);
|
/// Ensure we are big enough to contain the given Y-coordinate.
|
||||||
self.max_rect.max.x = self.max_rect.max.x.max(x);
|
/// This is sometimes useful to expand an ui to stretch to a certain place.
|
||||||
|
pub fn expand_to_include_y(&mut self, y: f32) {
|
||||||
|
self.min_rect.extend_with_y(y);
|
||||||
|
self.max_rect.extend_with_y(y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,49 +335,55 @@ impl Layout {
|
||||||
self.align2().align_size_within_rect(size, outer)
|
self.align2().align_size_within_rect(size, outer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initial_cursor(&self, max_rect: Rect) -> Pos2 {
|
fn initial_cursor(&self, max_rect: Rect) -> Rect {
|
||||||
|
let mut cursor = max_rect;
|
||||||
|
|
||||||
match self.main_dir {
|
match self.main_dir {
|
||||||
Direction::LeftToRight => max_rect.left_top(),
|
Direction::LeftToRight => {
|
||||||
Direction::RightToLeft => max_rect.right_top(),
|
cursor.max.x = INFINITY;
|
||||||
Direction::TopDown => max_rect.left_top(),
|
}
|
||||||
Direction::BottomUp => max_rect.left_bottom(),
|
Direction::RightToLeft => {
|
||||||
|
cursor.min.x = -INFINITY;
|
||||||
|
}
|
||||||
|
Direction::TopDown => {
|
||||||
|
cursor.max.y = INFINITY;
|
||||||
|
}
|
||||||
|
Direction::BottomUp => {
|
||||||
|
cursor.min.y = -INFINITY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursor
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn region_from_max_rect(&self, max_rect: Rect) -> Region {
|
pub(crate) fn region_from_max_rect(&self, max_rect: Rect) -> Region {
|
||||||
let cursor = self.initial_cursor(max_rect);
|
debug_assert!(!max_rect.any_nan());
|
||||||
let min_rect = Rect::from_min_size(cursor, Vec2::ZERO);
|
let mut region = Region {
|
||||||
Region {
|
min_rect: Rect::NOTHING, // temporary
|
||||||
min_rect,
|
|
||||||
max_rect,
|
max_rect,
|
||||||
cursor,
|
cursor: self.initial_cursor(max_rect),
|
||||||
}
|
};
|
||||||
|
let seed = self.next_widget_position(®ion);
|
||||||
|
region.min_rect = Rect::from_center_size(seed, Vec2::ZERO);
|
||||||
|
region
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn available_rect_before_wrap(&self, region: &Region) -> Rect {
|
pub(crate) fn available_rect_before_wrap(&self, region: &Region) -> Rect {
|
||||||
self.available_from_cursor_max_rect(region.cursor, region.max_rect)
|
self.available_from_cursor_max_rect(region.cursor, region.max_rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn available_size_before_wrap(&self, region: &Region) -> Vec2 {
|
|
||||||
self.available_rect_before_wrap(region).size()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn available_rect_before_wrap_finite(&self, region: &Region) -> Rect {
|
pub(crate) fn available_rect_before_wrap_finite(&self, region: &Region) -> Rect {
|
||||||
self.available_from_cursor_max_rect(region.cursor, region.max_rect_finite())
|
self.available_from_cursor_max_rect(region.cursor, region.max_rect_finite())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn available_size_before_wrap_finite(&self, region: &Region) -> Vec2 {
|
|
||||||
self.available_rect_before_wrap_finite(region).size()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Amount of space available for a widget.
|
/// Amount of space available for a widget.
|
||||||
/// For wrapping layouts, this is the maximum (after wrap).
|
/// For wrapping layouts, this is the maximum (after wrap).
|
||||||
pub(crate) fn available_size(&self, r: &Region) -> Vec2 {
|
pub(crate) fn available_size(&self, r: &Region) -> Vec2 {
|
||||||
if self.main_wrap {
|
if self.main_wrap {
|
||||||
if self.main_dir.is_horizontal() {
|
if self.main_dir.is_horizontal() {
|
||||||
vec2(r.max_rect.width(), r.max_rect.bottom() - r.cursor.y)
|
vec2(r.max_rect.width(), r.cursor.height())
|
||||||
} else {
|
} else {
|
||||||
vec2(r.max_rect.right() - r.cursor.x, r.max_rect.height())
|
vec2(r.cursor.width(), r.max_rect.height())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.available_from_cursor_max_rect(r.cursor, r.max_rect)
|
self.available_from_cursor_max_rect(r.cursor, r.max_rect)
|
||||||
|
@ -375,123 +393,159 @@ impl Layout {
|
||||||
|
|
||||||
/// Given the cursor in the region, how much space is available
|
/// Given the cursor in the region, how much space is available
|
||||||
/// for the next widget?
|
/// for the next widget?
|
||||||
fn available_from_cursor_max_rect(&self, cursor: Pos2, max_rect: Rect) -> Rect {
|
fn available_from_cursor_max_rect(&self, cursor: Rect, max_rect: Rect) -> Rect {
|
||||||
let mut rect = max_rect;
|
max_rect.intersect(cursor)
|
||||||
|
|
||||||
match self.main_dir {
|
|
||||||
Direction::LeftToRight => {
|
|
||||||
rect.min.x = cursor.x;
|
|
||||||
rect.min.y = cursor.y;
|
|
||||||
}
|
|
||||||
Direction::RightToLeft => {
|
|
||||||
rect.max.x = cursor.x;
|
|
||||||
rect.min.y = cursor.y;
|
|
||||||
}
|
|
||||||
Direction::TopDown => {
|
|
||||||
rect.min.x = cursor.x;
|
|
||||||
rect.min.y = cursor.y;
|
|
||||||
}
|
|
||||||
Direction::BottomUp => {
|
|
||||||
rect.min.x = cursor.x;
|
|
||||||
rect.max.y = cursor.y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rect
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns where to put the next widget that is of the given size.
|
/// Returns where to put the next widget that is of the given size.
|
||||||
/// The returned `frame_rect` `Rect` will always be justified along the cross axis.
|
/// The returned `frame_rect` `Rect` will always be justified along the cross axis.
|
||||||
/// This is what you then pass to `advance_after_rects`.
|
/// This is what you then pass to `advance_after_rects`.
|
||||||
/// Use `justify_and_align` to get the inner `widget_rect`.
|
/// Use `justify_and_align` to get the inner `widget_rect`.
|
||||||
#[allow(clippy::collapsible_if)]
|
pub(crate) fn next_frame(&self, region: &Region, child_size: Vec2, spacing: Vec2) -> Rect {
|
||||||
pub(crate) fn next_space(
|
|
||||||
&self,
|
|
||||||
region: &Region,
|
|
||||||
mut child_size: Vec2,
|
|
||||||
item_spacing: Vec2,
|
|
||||||
) -> Rect {
|
|
||||||
let mut cursor = region.cursor;
|
|
||||||
|
|
||||||
if self.main_wrap {
|
if self.main_wrap {
|
||||||
let available_size = self.available_size_before_wrap(region);
|
let available_size = self.available_rect_before_wrap(region).size();
|
||||||
|
|
||||||
|
let Region {
|
||||||
|
mut cursor,
|
||||||
|
mut max_rect,
|
||||||
|
min_rect,
|
||||||
|
} = *region;
|
||||||
|
|
||||||
match self.main_dir {
|
match self.main_dir {
|
||||||
Direction::LeftToRight => {
|
Direction::LeftToRight => {
|
||||||
if available_size.x < child_size.x && region.max_rect.left() < cursor.x {
|
if available_size.x < child_size.x && max_rect.left() < cursor.left() {
|
||||||
// New row
|
// New row
|
||||||
cursor = pos2(
|
let new_row_height = cursor.height().max(child_size.y);
|
||||||
region.max_rect.left(),
|
cursor = Rect::from_min_max(
|
||||||
region.max_rect.bottom() + item_spacing.y,
|
pos2(max_rect.left(), cursor.bottom() + spacing.y),
|
||||||
|
pos2(INFINITY, cursor.bottom() + spacing.y + new_row_height),
|
||||||
);
|
);
|
||||||
|
max_rect.max.y = max_rect.max.y.max(cursor.max.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::RightToLeft => {
|
Direction::RightToLeft => {
|
||||||
if available_size.x < child_size.x && cursor.x < region.max_rect.right() {
|
if available_size.x < child_size.x && cursor.right() < max_rect.right() {
|
||||||
// New row
|
// New row
|
||||||
cursor = pos2(
|
let new_row_height = cursor.height().max(child_size.y);
|
||||||
region.max_rect.right(),
|
cursor = Rect::from_min_max(
|
||||||
region.max_rect.bottom() + item_spacing.y,
|
pos2(-INFINITY, cursor.bottom() + spacing.y),
|
||||||
|
pos2(
|
||||||
|
max_rect.right(),
|
||||||
|
cursor.bottom() + spacing.y + new_row_height,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
max_rect.max.y = max_rect.max.y.max(cursor.max.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::TopDown => {
|
Direction::TopDown => {
|
||||||
if available_size.y < child_size.y && region.max_rect.top() < cursor.y {
|
if available_size.y < child_size.y && max_rect.top() < cursor.top() {
|
||||||
// New column
|
// New column
|
||||||
cursor = pos2(
|
let new_col_width = cursor.width().max(child_size.x);
|
||||||
region.max_rect.right() + item_spacing.x,
|
cursor = Rect::from_min_max(
|
||||||
region.max_rect.top(),
|
pos2(cursor.right() + spacing.x, max_rect.top()),
|
||||||
|
pos2(cursor.right() + spacing.x + new_col_width, INFINITY),
|
||||||
);
|
);
|
||||||
|
max_rect.max.x = max_rect.max.x.max(cursor.max.x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::BottomUp => {
|
Direction::BottomUp => {
|
||||||
if available_size.y < child_size.y && cursor.y < region.max_rect.bottom() {
|
if available_size.y < child_size.y && cursor.bottom() < max_rect.bottom() {
|
||||||
// New column
|
// New column
|
||||||
cursor = pos2(
|
let new_col_width = cursor.width().max(child_size.x);
|
||||||
region.max_rect.right() + item_spacing.x,
|
cursor = Rect::from_min_max(
|
||||||
region.max_rect.bottom(),
|
pos2(cursor.right() + spacing.x, -INFINITY),
|
||||||
|
pos2(
|
||||||
|
cursor.right() + spacing.x + new_col_width,
|
||||||
|
max_rect.bottom(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
max_rect.max.x = max_rect.max.x.max(cursor.max.x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let available_size = self.available_size_before_wrap_finite(region);
|
// Use the new cursor:
|
||||||
|
let region = Region {
|
||||||
|
min_rect,
|
||||||
|
max_rect,
|
||||||
|
cursor,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next_frame_ignore_wrap(®ion, child_size)
|
||||||
|
} else {
|
||||||
|
self.next_frame_ignore_wrap(region, child_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_frame_ignore_wrap(&self, region: &Region, child_size: Vec2) -> Rect {
|
||||||
|
let available_rect = self.available_rect_before_wrap_finite(region);
|
||||||
|
|
||||||
|
let mut frame_size = child_size;
|
||||||
|
|
||||||
if self.is_vertical() || self.horizontal_justify() {
|
if self.is_vertical() || self.horizontal_justify() {
|
||||||
child_size.x = child_size.x.at_least(available_size.x); // fill full width
|
frame_size.x = frame_size.x.at_least(available_rect.width()); // fill full width
|
||||||
}
|
}
|
||||||
if self.is_horizontal() || self.vertical_justify() {
|
if self.is_horizontal() || self.vertical_justify() {
|
||||||
child_size.y = child_size.y.at_least(available_size.y); // fill full height
|
frame_size.y = frame_size.y.at_least(available_rect.height()); // fill full height
|
||||||
}
|
}
|
||||||
|
|
||||||
let child_pos = match self.main_dir {
|
let align2 = match self.main_dir {
|
||||||
Direction::LeftToRight => cursor,
|
Direction::LeftToRight => Align2([Align::LEFT, self.vertical_align()]),
|
||||||
Direction::RightToLeft => cursor + vec2(-child_size.x, 0.0),
|
Direction::RightToLeft => Align2([Align::RIGHT, self.vertical_align()]),
|
||||||
Direction::TopDown => cursor,
|
Direction::TopDown => Align2([self.horizontal_align(), Align::TOP]),
|
||||||
Direction::BottomUp => cursor + vec2(0.0, -child_size.y),
|
Direction::BottomUp => Align2([self.horizontal_align(), Align::BOTTOM]),
|
||||||
};
|
};
|
||||||
|
|
||||||
Rect::from_min_size(child_pos, child_size)
|
align2.align_size_within_rect(frame_size, available_rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply justify (fill width/height) and/or alignment after calling `next_space`.
|
/// Apply justify (fill width/height) and/or alignment after calling `next_space`.
|
||||||
pub(crate) fn justify_and_align(&self, rect: Rect, mut child_size: Vec2) -> Rect {
|
pub(crate) fn justify_and_align(&self, frame: Rect, mut child_size: Vec2) -> Rect {
|
||||||
if self.horizontal_justify() {
|
if self.horizontal_justify() {
|
||||||
child_size.x = child_size.x.at_least(rect.width()); // fill full width
|
child_size.x = child_size.x.at_least(frame.width()); // fill full width
|
||||||
}
|
}
|
||||||
if self.vertical_justify() {
|
if self.vertical_justify() {
|
||||||
child_size.y = child_size.y.at_least(rect.height()); // fill full height
|
child_size.y = child_size.y.at_least(frame.height()); // fill full height
|
||||||
}
|
}
|
||||||
self.align_size_within_rect(child_size, rect)
|
self.align_size_within_rect(child_size, frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance the cursor by this many points.
|
pub(crate) fn next_widget_space_ignore_wrap_justify(
|
||||||
pub(crate) fn advance_cursor(&self, cursor: &mut Pos2, amount: f32) {
|
&self,
|
||||||
|
region: &Region,
|
||||||
|
size: Vec2,
|
||||||
|
) -> Rect {
|
||||||
|
let frame = self.next_frame_ignore_wrap(region, size);
|
||||||
|
let rect = self.align_size_within_rect(size, frame);
|
||||||
|
debug_assert!((rect.size() - size).length() < 1.0);
|
||||||
|
rect
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Where would the next tiny widget be centered?
|
||||||
|
pub(crate) fn next_widget_position(&self, region: &Region) -> Pos2 {
|
||||||
|
self.next_widget_space_ignore_wrap_justify(region, Vec2::ZERO)
|
||||||
|
.center()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Advance the cursor by this many points, and allocate in region.
|
||||||
|
pub(crate) fn advance_cursor(&self, region: &mut Region, amount: f32) {
|
||||||
match self.main_dir {
|
match self.main_dir {
|
||||||
Direction::LeftToRight => cursor.x += amount,
|
Direction::LeftToRight => {
|
||||||
Direction::RightToLeft => cursor.x -= amount,
|
region.cursor.min.x += amount;
|
||||||
Direction::TopDown => cursor.y += amount,
|
region.expand_to_include_x(region.cursor.min.x);
|
||||||
Direction::BottomUp => cursor.y -= amount,
|
}
|
||||||
|
Direction::RightToLeft => {
|
||||||
|
region.cursor.max.x -= amount;
|
||||||
|
region.expand_to_include_x(region.cursor.max.x);
|
||||||
|
}
|
||||||
|
Direction::TopDown => {
|
||||||
|
region.cursor.min.y += amount;
|
||||||
|
region.expand_to_include_y(region.cursor.min.y);
|
||||||
|
}
|
||||||
|
Direction::BottomUp => {
|
||||||
|
region.cursor.max.y -= amount;
|
||||||
|
region.expand_to_include_y(region.cursor.max.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,28 +555,83 @@ impl Layout {
|
||||||
/// * `widget_rect`: the actual rect used by the widget
|
/// * `widget_rect`: the actual rect used by the widget
|
||||||
pub(crate) fn advance_after_rects(
|
pub(crate) fn advance_after_rects(
|
||||||
&self,
|
&self,
|
||||||
cursor: &mut Pos2,
|
cursor: &mut Rect,
|
||||||
frame_rect: Rect,
|
frame_rect: Rect,
|
||||||
widget_rect: Rect,
|
widget_rect: Rect,
|
||||||
item_spacing: Vec2,
|
item_spacing: Vec2,
|
||||||
) {
|
) {
|
||||||
*cursor = match self.main_dir {
|
if self.main_wrap {
|
||||||
Direction::LeftToRight => pos2(widget_rect.right() + item_spacing.x, frame_rect.top()),
|
if cursor.intersects(frame_rect) {
|
||||||
Direction::RightToLeft => pos2(widget_rect.left() - item_spacing.x, frame_rect.top()),
|
// make row/column larger if necessary
|
||||||
Direction::TopDown => pos2(frame_rect.left(), widget_rect.bottom() + item_spacing.y),
|
*cursor = cursor.union(frame_rect);
|
||||||
Direction::BottomUp => pos2(frame_rect.left(), widget_rect.top() - item_spacing.y),
|
} else {
|
||||||
|
// this is a new row or column. We temporarily use NAN for what will be filled in later.
|
||||||
|
match self.main_dir {
|
||||||
|
Direction::LeftToRight => {
|
||||||
|
*cursor = Rect::from_min_max(
|
||||||
|
pos2(f32::NAN, frame_rect.min.y),
|
||||||
|
pos2(INFINITY, frame_rect.max.y),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Direction::RightToLeft => {
|
||||||
|
*cursor = Rect::from_min_max(
|
||||||
|
pos2(-INFINITY, frame_rect.min.y),
|
||||||
|
pos2(f32::NAN, frame_rect.max.y),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Direction::TopDown => {
|
||||||
|
*cursor = Rect::from_min_max(
|
||||||
|
pos2(frame_rect.min.x, f32::NAN),
|
||||||
|
pos2(frame_rect.max.x, INFINITY),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Direction::BottomUp => {
|
||||||
|
*cursor = Rect::from_min_max(
|
||||||
|
pos2(frame_rect.min.x, -INFINITY),
|
||||||
|
pos2(frame_rect.max.x, f32::NAN),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.main_dir {
|
||||||
|
Direction::LeftToRight => {
|
||||||
|
cursor.min.x = widget_rect.right() + item_spacing.x;
|
||||||
|
}
|
||||||
|
Direction::RightToLeft => {
|
||||||
|
cursor.max.x = widget_rect.left() - item_spacing.x;
|
||||||
|
}
|
||||||
|
Direction::TopDown => {
|
||||||
|
cursor.min.y = widget_rect.bottom() + item_spacing.y;
|
||||||
|
}
|
||||||
|
Direction::BottomUp => {
|
||||||
|
cursor.max.y = widget_rect.top() - item_spacing.y;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move to the next row in a wrapping layout.
|
/// Move to the next row in a wrapping layout.
|
||||||
/// Otherwise does nothing.
|
/// Otherwise does nothing.
|
||||||
pub(crate) fn end_row(&mut self, region: &mut Region, item_spacing: Vec2) {
|
pub(crate) fn end_row(&mut self, region: &mut Region, spacing: Vec2) {
|
||||||
if self.main_wrap && self.is_horizontal() {
|
if self.main_wrap {
|
||||||
// New row
|
match self.main_dir {
|
||||||
region.cursor = pos2(
|
Direction::LeftToRight => {
|
||||||
region.max_rect.left(),
|
let new_top = region.cursor.bottom() + spacing.y;
|
||||||
region.max_rect.bottom() + item_spacing.y,
|
region.cursor = Rect::from_min_max(
|
||||||
);
|
pos2(region.max_rect.left(), new_top),
|
||||||
|
pos2(INFINITY, new_top + region.cursor.height()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Direction::RightToLeft => {
|
||||||
|
let new_top = region.cursor.bottom() + spacing.y;
|
||||||
|
region.cursor = Rect::from_min_max(
|
||||||
|
pos2(-INFINITY, new_top),
|
||||||
|
pos2(region.max_rect.right(), new_top + region.cursor.height()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Direction::TopDown | Direction::BottomUp => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -540,7 +649,7 @@ impl Layout {
|
||||||
) {
|
) {
|
||||||
use epaint::*;
|
use epaint::*;
|
||||||
|
|
||||||
let cursor = region.cursor;
|
let cursor = self.next_widget_position(region);
|
||||||
|
|
||||||
let align;
|
let align;
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ impl Placer {
|
||||||
self.region.min_rect = min_rect;
|
self.region.min_rect = min_rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cursor(&self) -> Pos2 {
|
pub(crate) fn cursor(&self) -> Rect {
|
||||||
self.region.cursor
|
self.region.cursor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,12 +108,24 @@ impl Placer {
|
||||||
grid.next_cell(self.region.cursor, child_size)
|
grid.next_cell(self.region.cursor, child_size)
|
||||||
} else {
|
} else {
|
||||||
self.layout
|
self.layout
|
||||||
.next_space(&self.region, child_size, item_spacing)
|
.next_frame(&self.region, child_size, item_spacing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Where do we expect a zero-sized widget to be placed?
|
||||||
|
pub(crate) fn next_widget_position(&self) -> Pos2 {
|
||||||
|
if let Some(grid) = &self.grid {
|
||||||
|
grid.next_cell(self.region.cursor, Vec2::ZERO).center()
|
||||||
|
} else {
|
||||||
|
self.layout.next_widget_position(&self.region)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply justify or alignment after calling `next_space`.
|
/// Apply justify or alignment after calling `next_space`.
|
||||||
pub(crate) fn justify_and_align(&self, rect: Rect, child_size: Vec2) -> Rect {
|
pub(crate) fn justify_and_align(&self, rect: Rect, child_size: Vec2) -> Rect {
|
||||||
|
debug_assert!(!rect.any_nan());
|
||||||
|
debug_assert!(!child_size.any_nan());
|
||||||
|
|
||||||
if let Some(grid) = &self.grid {
|
if let Some(grid) = &self.grid {
|
||||||
grid.justify_and_align(rect, child_size)
|
grid.justify_and_align(rect, child_size)
|
||||||
} else {
|
} else {
|
||||||
|
@ -128,10 +140,7 @@ impl Placer {
|
||||||
self.grid.is_none(),
|
self.grid.is_none(),
|
||||||
"You cannot advance the cursor when in a grid layout"
|
"You cannot advance the cursor when in a grid layout"
|
||||||
);
|
);
|
||||||
self.layout.advance_cursor(&mut self.region.cursor, amount);
|
self.layout.advance_cursor(&mut self.region, amount);
|
||||||
|
|
||||||
self.region
|
|
||||||
.expand_to_include_rect(Rect::from_min_size(self.cursor(), Vec2::ZERO));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance cursor after a widget was added to a specific rectangle
|
/// Advance cursor after a widget was added to a specific rectangle
|
||||||
|
@ -155,7 +164,7 @@ impl Placer {
|
||||||
item_spacing,
|
item_spacing,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
self.region.expand_to_include_rect(widget_rect);
|
self.region.expand_to_include_rect(frame_rect); // e.g. for centered layouts: pretend we used whole frame
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move to the next row in a grid layout or wrapping layout.
|
/// Move to the next row in a grid layout or wrapping layout.
|
||||||
|
@ -180,68 +189,45 @@ impl Placer {
|
||||||
self.region.expand_to_include_x(x);
|
self.region.expand_to_include_x(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_widget_space_ignore_wrap_justify(&self, size: Vec2) -> Rect {
|
||||||
|
self.layout
|
||||||
|
.next_widget_space_ignore_wrap_justify(&self.region, size)
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the maximum width of the ui.
|
/// Set the maximum width of the ui.
|
||||||
/// You won't be able to shrink it below the current minimum size.
|
/// You won't be able to shrink it below the current minimum size.
|
||||||
pub(crate) fn set_max_width(&mut self, width: f32) {
|
pub(crate) fn set_max_width(&mut self, width: f32) {
|
||||||
#![allow(clippy::float_cmp)]
|
let rect = self.next_widget_space_ignore_wrap_justify(vec2(width, 0.0));
|
||||||
let Self { layout, region, .. } = self;
|
let region = &mut self.region;
|
||||||
if layout.main_dir() == Direction::RightToLeft {
|
region.max_rect.min.x = rect.min.x;
|
||||||
debug_assert_eq!(region.min_rect.max.x, region.max_rect.max.x);
|
region.max_rect.max.x = rect.max.x;
|
||||||
region.max_rect.min.x = region.max_rect.max.x - width.at_least(region.min_rect.width());
|
region.max_rect = region.max_rect.union(region.min_rect); // make sure we didn't shrink too much
|
||||||
} else {
|
|
||||||
debug_assert_eq!(region.min_rect.min.x, region.max_rect.min.x);
|
|
||||||
region.max_rect.max.x = region.max_rect.min.x + width.at_least(region.min_rect.width());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum height of the ui.
|
/// Set the maximum height of the ui.
|
||||||
/// You won't be able to shrink it below the current minimum size.
|
/// You won't be able to shrink it below the current minimum size.
|
||||||
pub(crate) fn set_max_height(&mut self, height: f32) {
|
pub(crate) fn set_max_height(&mut self, height: f32) {
|
||||||
#![allow(clippy::float_cmp)]
|
let rect = self.next_widget_space_ignore_wrap_justify(vec2(0.0, height));
|
||||||
let Self { layout, region, .. } = self;
|
let region = &mut self.region;
|
||||||
if layout.main_dir() == Direction::BottomUp {
|
region.max_rect.min.y = rect.min.y;
|
||||||
debug_assert_eq!(region.min_rect.max.y, region.max_rect.max.y);
|
region.max_rect.max.y = rect.max.y;
|
||||||
region.max_rect.min.y =
|
region.max_rect = region.max_rect.union(region.min_rect); // make sure we didn't shrink too much
|
||||||
region.max_rect.max.y - height.at_least(region.min_rect.height());
|
|
||||||
} else {
|
|
||||||
debug_assert_eq!(region.min_rect.min.y, region.max_rect.min.y);
|
|
||||||
region.max_rect.max.y =
|
|
||||||
region.max_rect.min.y + height.at_least(region.min_rect.height());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the minimum width of the ui.
|
/// Set the minimum width of the ui.
|
||||||
/// This can't shrink the ui, only make it larger.
|
/// This can't shrink the ui, only make it larger.
|
||||||
pub(crate) fn set_min_width(&mut self, width: f32) {
|
pub(crate) fn set_min_width(&mut self, width: f32) {
|
||||||
#![allow(clippy::float_cmp)]
|
let rect = self.next_widget_space_ignore_wrap_justify(vec2(width, 0.0));
|
||||||
let Self { layout, region, .. } = self;
|
self.region.expand_to_include_x(rect.min.x);
|
||||||
if layout.main_dir() == Direction::RightToLeft {
|
self.region.expand_to_include_x(rect.max.x);
|
||||||
debug_assert_eq!(region.min_rect.max.x, region.max_rect.max.x);
|
|
||||||
let min_rect = &mut region.min_rect;
|
|
||||||
min_rect.min.x = min_rect.min.x.min(min_rect.max.x - width);
|
|
||||||
} else {
|
|
||||||
debug_assert_eq!(region.min_rect.min.x, region.max_rect.min.x);
|
|
||||||
let min_rect = &mut region.min_rect;
|
|
||||||
min_rect.max.x = min_rect.max.x.max(min_rect.min.x + width);
|
|
||||||
}
|
|
||||||
region.max_rect = region.max_rect.union(region.min_rect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the minimum height of the ui.
|
/// Set the minimum height of the ui.
|
||||||
/// This can't shrink the ui, only make it larger.
|
/// This can't shrink the ui, only make it larger.
|
||||||
pub(crate) fn set_min_height(&mut self, height: f32) {
|
pub(crate) fn set_min_height(&mut self, height: f32) {
|
||||||
#![allow(clippy::float_cmp)]
|
let rect = self.next_widget_space_ignore_wrap_justify(vec2(0.0, height));
|
||||||
let Self { layout, region, .. } = self;
|
self.region.expand_to_include_y(rect.min.y);
|
||||||
if layout.main_dir() == Direction::BottomUp {
|
self.region.expand_to_include_y(rect.max.y);
|
||||||
debug_assert_eq!(region.min_rect.max.y, region.max_rect.max.y);
|
|
||||||
let min_rect = &mut region.min_rect;
|
|
||||||
min_rect.min.y = min_rect.min.y.min(min_rect.max.y - height);
|
|
||||||
} else {
|
|
||||||
debug_assert_eq!(region.min_rect.min.y, region.max_rect.min.y);
|
|
||||||
let min_rect = &mut region.min_rect;
|
|
||||||
min_rect.max.y = min_rect.max.y.max(min_rect.min.y + height);
|
|
||||||
}
|
|
||||||
region.max_rect = region.max_rect.union(region.min_rect);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ impl Ui {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn child_ui(&mut self, max_rect: Rect, layout: Layout) -> Self {
|
pub fn child_ui(&mut self, max_rect: Rect, layout: Layout) -> Self {
|
||||||
|
debug_assert!(!max_rect.any_nan());
|
||||||
self.next_auto_id = self.next_auto_id.wrapping_add(1);
|
self.next_auto_id = self.next_auto_id.wrapping_add(1);
|
||||||
|
|
||||||
Ui {
|
Ui {
|
||||||
|
@ -645,7 +646,9 @@ impl Ui {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a specific part of the `Ui‘.
|
/// Allocate a specific part of the `Ui‘.
|
||||||
|
///
|
||||||
/// Ignore the layout of the `Ui‘: just put my widget here!
|
/// Ignore the layout of the `Ui‘: just put my widget here!
|
||||||
|
/// The layout cursor will advance to past this `rect`.
|
||||||
pub(crate) fn allocate_rect(&mut self, rect: Rect, sense: Sense) -> Response {
|
pub(crate) fn allocate_rect(&mut self, rect: Rect, sense: Sense) -> Response {
|
||||||
let id = self.advance_cursor_after_rect(rect);
|
let id = self.advance_cursor_after_rect(rect);
|
||||||
self.interact(rect, id, sense)
|
self.interact(rect, id, sense)
|
||||||
|
@ -659,10 +662,19 @@ impl Ui {
|
||||||
Id::new(self.next_auto_id)
|
Id::new(self.next_auto_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cursor(&self) -> Pos2 {
|
pub(crate) fn placer(&self) -> &Placer {
|
||||||
|
&self.placer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn cursor(&self) -> Rect {
|
||||||
self.placer.cursor()
|
self.placer.cursor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Where do we expect a zero-sized widget to be placed?
|
||||||
|
pub(crate) fn next_widget_position(&self) -> Pos2 {
|
||||||
|
self.placer.next_widget_position()
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocated the given space and then adds content to that space.
|
/// Allocated the given space and then adds content to that space.
|
||||||
/// If the contents overflow, more space will be allocated.
|
/// If the contents overflow, more space will be allocated.
|
||||||
/// When finished, the amount of space actually used (`min_rect`) will be allocated.
|
/// When finished, the amount of space actually used (`min_rect`) will be allocated.
|
||||||
|
@ -673,17 +685,15 @@ impl Ui {
|
||||||
add_contents: impl FnOnce(&mut Self) -> R,
|
add_contents: impl FnOnce(&mut Self) -> R,
|
||||||
) -> InnerResponse<R> {
|
) -> InnerResponse<R> {
|
||||||
let item_spacing = self.spacing().item_spacing;
|
let item_spacing = self.spacing().item_spacing;
|
||||||
let outer_child_rect = self.placer.next_space(desired_size, item_spacing);
|
let frame_rect = self.placer.next_space(desired_size, item_spacing);
|
||||||
let inner_child_rect = self
|
let child_rect = self.placer.justify_and_align(frame_rect, desired_size);
|
||||||
.placer
|
|
||||||
.justify_and_align(outer_child_rect, desired_size);
|
|
||||||
|
|
||||||
let mut child_ui = self.child_ui(inner_child_rect, *self.layout());
|
let mut child_ui = self.child_ui(child_rect, *self.layout());
|
||||||
let ret = add_contents(&mut child_ui);
|
let ret = add_contents(&mut child_ui);
|
||||||
let final_child_rect = child_ui.min_rect();
|
let final_child_rect = child_ui.min_rect();
|
||||||
|
|
||||||
self.placer.advance_after_rects(
|
self.placer.advance_after_rects(
|
||||||
outer_child_rect.union(final_child_rect),
|
frame_rect.union(final_child_rect),
|
||||||
final_child_rect,
|
final_child_rect,
|
||||||
item_spacing,
|
item_spacing,
|
||||||
);
|
);
|
||||||
|
@ -740,8 +750,8 @@ impl Ui {
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn scroll_to_cursor(&mut self, align: Align) {
|
pub fn scroll_to_cursor(&mut self, align: Align) {
|
||||||
let scroll_y = self.cursor().y;
|
let target_y = self.next_widget_position().y;
|
||||||
self.ctx().frame_state().scroll_target = Some((scroll_y, align));
|
self.ctx().frame_state().scroll_target = Some((target_y, align));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1094,8 +1104,7 @@ impl Ui {
|
||||||
let child_rect = self.available_rect_before_wrap();
|
let child_rect = self.available_rect_before_wrap();
|
||||||
let mut child_ui = self.child_ui(child_rect, *self.layout());
|
let mut child_ui = self.child_ui(child_rect, *self.layout());
|
||||||
let ret = add_contents(&mut child_ui);
|
let ret = add_contents(&mut child_ui);
|
||||||
let size = child_ui.min_size();
|
let response = self.allocate_rect(child_ui.min_rect(), Sense::hover());
|
||||||
let response = self.allocate_response(size, Sense::hover());
|
|
||||||
InnerResponse::new(ret, response)
|
InnerResponse::new(ret, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1140,8 +1149,11 @@ impl Ui {
|
||||||
"You can only indent vertical layouts, found {:?}",
|
"You can only indent vertical layouts, found {:?}",
|
||||||
self.layout()
|
self.layout()
|
||||||
);
|
);
|
||||||
let indent = vec2(self.spacing().indent, 0.0);
|
|
||||||
let child_rect = Rect::from_min_max(self.cursor() + indent, self.max_rect().right_bottom()); // TODO: wrong for reversed layouts
|
let indent = self.spacing().indent;
|
||||||
|
let mut child_rect = self.placer.available_rect_before_wrap();
|
||||||
|
child_rect.min.x += indent;
|
||||||
|
|
||||||
let mut child_ui = Self {
|
let mut child_ui = Self {
|
||||||
id: self.id.with(id_source),
|
id: self.id.with(id_source),
|
||||||
..self.child_ui(child_rect, *self.layout())
|
..self.child_ui(child_rect, *self.layout())
|
||||||
|
@ -1153,13 +1165,11 @@ impl Ui {
|
||||||
child_ui.advance_cursor(4.0);
|
child_ui.advance_cursor(4.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = child_ui.min_size();
|
|
||||||
|
|
||||||
// draw a faint line on the left to mark the indented section
|
// draw a faint line on the left to mark the indented section
|
||||||
let stroke = self.visuals().widgets.noninteractive.bg_stroke;
|
let stroke = self.visuals().widgets.noninteractive.bg_stroke;
|
||||||
let left_top = child_rect.min - indent * 0.5;
|
let left_top = child_rect.min - 0.5 * indent * Vec2::X;
|
||||||
let left_top = self.painter().round_pos_to_pixels(left_top);
|
let left_top = self.painter().round_pos_to_pixels(left_top);
|
||||||
let left_bottom = pos2(left_top.x, left_top.y + size.y - 2.0);
|
let left_bottom = pos2(left_top.x, child_ui.min_rect().bottom() - 2.0);
|
||||||
let left_bottom = self.painter().round_pos_to_pixels(left_bottom);
|
let left_bottom = self.painter().round_pos_to_pixels(left_bottom);
|
||||||
self.painter.line_segment([left_top, left_bottom], stroke);
|
self.painter.line_segment([left_top, left_bottom], stroke);
|
||||||
if end_with_horizontal_line {
|
if end_with_horizontal_line {
|
||||||
|
@ -1169,45 +1179,10 @@ impl Ui {
|
||||||
.line_segment([left_bottom, right_bottom], stroke);
|
.line_segment([left_bottom, right_bottom], stroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = self.allocate_response(indent + size, Sense::hover());
|
let response = self.allocate_rect(child_ui.min_rect(), Sense::hover());
|
||||||
InnerResponse::new(ret, response)
|
InnerResponse::new(ret, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deprecated]
|
|
||||||
pub fn left_column(&mut self, width: f32) -> Self {
|
|
||||||
#[allow(deprecated)]
|
|
||||||
self.column(Align::Min, width)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated]
|
|
||||||
pub fn centered_column(&mut self, width: f32) -> Self {
|
|
||||||
#[allow(deprecated)]
|
|
||||||
self.column(Align::Center, width)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated]
|
|
||||||
pub fn right_column(&mut self, width: f32) -> Self {
|
|
||||||
#[allow(deprecated)]
|
|
||||||
self.column(Align::Max, width)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A column ui with a given width.
|
|
||||||
#[deprecated]
|
|
||||||
pub fn column(&mut self, column_position: Align, width: f32) -> Self {
|
|
||||||
let x = match column_position {
|
|
||||||
Align::Min => 0.0,
|
|
||||||
Align::Center => self.available_width() / 2.0 - width / 2.0,
|
|
||||||
Align::Max => self.available_width() - width,
|
|
||||||
};
|
|
||||||
self.child_ui(
|
|
||||||
Rect::from_min_size(
|
|
||||||
self.cursor() + vec2(x, 0.0),
|
|
||||||
vec2(width, self.available_size_before_wrap().y),
|
|
||||||
),
|
|
||||||
*self.layout(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start a ui with horizontal layout.
|
/// Start a ui with horizontal layout.
|
||||||
/// After you have called this, the function registers the contents as any other widget.
|
/// After you have called this, the function registers the contents as any other widget.
|
||||||
///
|
///
|
||||||
|
@ -1403,7 +1378,7 @@ impl Ui {
|
||||||
let spacing = self.spacing().item_spacing.x;
|
let spacing = self.spacing().item_spacing.x;
|
||||||
let total_spacing = spacing * (num_columns as f32 - 1.0);
|
let total_spacing = spacing * (num_columns as f32 - 1.0);
|
||||||
let column_width = (self.available_width() - total_spacing) / (num_columns as f32);
|
let column_width = (self.available_width() - total_spacing) / (num_columns as f32);
|
||||||
let top_left = self.cursor();
|
let top_left = self.cursor().min;
|
||||||
|
|
||||||
let mut columns: Vec<Self> = (0..num_columns)
|
let mut columns: Vec<Self> = (0..num_columns)
|
||||||
.map(|col_idx| {
|
.map(|col_idx| {
|
||||||
|
|
|
@ -263,7 +263,7 @@ impl Widget for Label {
|
||||||
max_width,
|
max_width,
|
||||||
);
|
);
|
||||||
|
|
||||||
let pos = pos2(ui.min_rect().left(), ui.cursor().y);
|
let pos = pos2(ui.min_rect().left(), ui.cursor().top());
|
||||||
|
|
||||||
assert!(!galley.rows.is_empty(), "Galleys are never empty");
|
assert!(!galley.rows.is_empty(), "Galleys are never empty");
|
||||||
let rect = galley.rows[0].rect().translate(vec2(pos.x, pos.y));
|
let rect = galley.rows[0].rect().translate(vec2(pos.x, pos.y));
|
||||||
|
|
Loading…
Reference in a new issue