Break up some long container functions and reduce some code bloat
This commit is contained in:
parent
8270d59dbf
commit
002944b775
5 changed files with 172 additions and 69 deletions
|
@ -91,13 +91,15 @@ impl Area {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Area {
|
struct Prepared {
|
||||||
// TODO
|
layer: Layer,
|
||||||
// pub fn show(self, ui: &Ui, add_contents: impl FnOnce(&mut Ui)) {
|
state: State,
|
||||||
// let default_pos = self.default_pos.unwrap_or_else(|| ui.top_left() + pos2(100.0, 100.0)); // TODO
|
movable: bool,
|
||||||
// }
|
content_ui: Ui,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn show(self, ctx: &Arc<Context>, add_contents: impl FnOnce(&mut Ui)) -> InteractInfo {
|
impl Area {
|
||||||
|
fn prepare(self, ctx: &Arc<Context>) -> Prepared {
|
||||||
let Area {
|
let Area {
|
||||||
id,
|
id,
|
||||||
movable,
|
movable,
|
||||||
|
@ -120,19 +122,45 @@ impl Area {
|
||||||
state.pos = fixed_pos.unwrap_or(state.pos);
|
state.pos = fixed_pos.unwrap_or(state.pos);
|
||||||
state.pos = state.pos.round();
|
state.pos = state.pos.round();
|
||||||
|
|
||||||
let mut ui = Ui::new(
|
let content_ui = Ui::new(
|
||||||
ctx.clone(),
|
ctx.clone(),
|
||||||
layer,
|
layer,
|
||||||
id,
|
id,
|
||||||
Rect::from_min_size(state.pos, Vec2::infinity()),
|
Rect::from_min_size(state.pos, Vec2::infinity()),
|
||||||
);
|
);
|
||||||
add_contents(&mut ui);
|
|
||||||
state.size = (ui.child_bounds().max - state.pos).ceil();
|
Prepared {
|
||||||
|
layer,
|
||||||
|
state,
|
||||||
|
movable,
|
||||||
|
content_ui,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(self, ctx: &Arc<Context>, add_contents: impl FnOnce(&mut Ui)) -> InteractInfo {
|
||||||
|
let mut prepared = self.prepare(ctx);
|
||||||
|
add_contents(&mut prepared.content_ui);
|
||||||
|
Self::finish(ctx, prepared)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(ctx: &Arc<Context>, prepared: Prepared) -> InteractInfo {
|
||||||
|
let Prepared {
|
||||||
|
layer,
|
||||||
|
mut state,
|
||||||
|
movable,
|
||||||
|
content_ui,
|
||||||
|
} = prepared;
|
||||||
|
|
||||||
|
state.size = (content_ui.child_bounds().max - state.pos).ceil();
|
||||||
|
|
||||||
let rect = Rect::from_min_size(state.pos, state.size);
|
let rect = Rect::from_min_size(state.pos, state.size);
|
||||||
let clip_rect = Rect::everything(); // TODO: get from context
|
let clip_rect = Rect::everything(); // TODO: get from context
|
||||||
|
|
||||||
let interact_id = if movable { Some(id.with("move")) } else { None };
|
let interact_id = if movable {
|
||||||
|
Some(layer.id.with("move"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let move_interact = ctx.interact(layer, clip_rect, rect, interact_id);
|
let move_interact = ctx.interact(layer, clip_rect, rect, interact_id);
|
||||||
|
|
||||||
let input = ctx.input();
|
let input = ctx.input();
|
||||||
|
|
|
@ -162,8 +162,13 @@ impl CollapsingHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Prepared {
|
||||||
|
id: Id,
|
||||||
|
state: State,
|
||||||
|
}
|
||||||
|
|
||||||
impl CollapsingHeader {
|
impl CollapsingHeader {
|
||||||
pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> Option<R> {
|
fn prepare(self, ui: &mut Ui) -> Prepared {
|
||||||
assert!(
|
assert!(
|
||||||
ui.layout().dir() == Direction::Vertical,
|
ui.layout().dir() == Direction::Vertical,
|
||||||
"Horizontal collapsing is unimplemented"
|
"Horizontal collapsing is unimplemented"
|
||||||
|
@ -231,14 +236,14 @@ impl CollapsingHeader {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
ui.expand_to_include_child(interact.rect); // TODO: remove, just a test
|
Prepared { id, state }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> Option<R> {
|
||||||
|
let Prepared { id, mut state } = self.prepare(ui);
|
||||||
let r_interact = state.add_contents(ui, |ui| ui.indent(id, add_contents).0);
|
let r_interact = state.add_contents(ui, |ui| ui.indent(id, add_contents).0);
|
||||||
let ret = r_interact.map(|ri| ri.0);
|
let ret = r_interact.map(|ri| ri.0);
|
||||||
|
|
||||||
ui.memory().collapsing_headers.insert(id, state);
|
ui.memory().collapsing_headers.insert(id, state);
|
||||||
ui.response(interact);
|
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,8 +168,16 @@ impl Resize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Prepared {
|
||||||
|
id: Id,
|
||||||
|
state: State,
|
||||||
|
is_new: bool,
|
||||||
|
corner_interact: Option<InteractInfo>,
|
||||||
|
content_ui: Ui,
|
||||||
|
}
|
||||||
|
|
||||||
impl Resize {
|
impl Resize {
|
||||||
pub fn show<R>(mut self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> R {
|
fn prepare(&mut self, ui: &mut Ui) -> Prepared {
|
||||||
let id = self.id.unwrap_or_else(|| ui.make_child_id("resize"));
|
let id = self.id.unwrap_or_else(|| ui.make_child_id("resize"));
|
||||||
self.min_size = self.min_size.min(ui.available().size());
|
self.min_size = self.min_size.min(ui.available().size());
|
||||||
self.max_size = self.max_size.min(ui.available().size());
|
self.max_size = self.max_size.min(ui.available().size());
|
||||||
|
@ -232,27 +240,49 @@ impl Resize {
|
||||||
|
|
||||||
let inner_rect = Rect::from_min_size(position, state.size);
|
let inner_rect = Rect::from_min_size(position, state.size);
|
||||||
|
|
||||||
let (ret, desired_size);
|
let mut content_clip_rect = ui
|
||||||
{
|
.clip_rect()
|
||||||
let mut content_clip_rect = ui
|
.intersect(inner_rect.expand(ui.style().clip_rect_margin));
|
||||||
.clip_rect()
|
|
||||||
.intersect(inner_rect.expand(ui.style().clip_rect_margin));
|
|
||||||
|
|
||||||
// If we pull the resize handle to shrink, we want to TRY to shink it.
|
// If we pull the resize handle to shrink, we want to TRY to shink it.
|
||||||
// After laying out the contents, we might be much bigger.
|
// After laying out the contents, we might be much bigger.
|
||||||
// In those cases we don't want the clip_rect to be smaller, because
|
// In those cases we don't want the clip_rect to be smaller, because
|
||||||
// then we will clip the contents of the region even thought the result gets larger. This is simply ugly!
|
// then we will clip the contents of the region even thought the result gets larger. This is simply ugly!
|
||||||
// So we use the memory of last_frame_size to make the clip rect large enough.
|
// So we use the memory of last_frame_size to make the clip rect large enough.
|
||||||
content_clip_rect.max = content_clip_rect
|
content_clip_rect.max = content_clip_rect
|
||||||
.max
|
.max
|
||||||
.max(content_clip_rect.min + last_frame_size)
|
.max(content_clip_rect.min + last_frame_size)
|
||||||
.min(ui.clip_rect().max); // Respect parent region
|
.min(ui.clip_rect().max); // Respect parent region
|
||||||
|
|
||||||
let mut contents_ui = ui.child_ui(inner_rect);
|
let mut content_ui = ui.child_ui(inner_rect);
|
||||||
contents_ui.set_clip_rect(content_clip_rect);
|
content_ui.set_clip_rect(content_clip_rect);
|
||||||
ret = add_contents(&mut contents_ui);
|
|
||||||
desired_size = contents_ui.bounding_size();
|
Prepared {
|
||||||
};
|
id,
|
||||||
|
state,
|
||||||
|
is_new,
|
||||||
|
corner_interact,
|
||||||
|
content_ui,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show<R>(mut self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> R {
|
||||||
|
let mut prepared = self.prepare(ui);
|
||||||
|
let ret = add_contents(&mut prepared.content_ui);
|
||||||
|
self.finish(ui, prepared);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(self, ui: &mut Ui, prepared: Prepared) {
|
||||||
|
let Prepared {
|
||||||
|
id,
|
||||||
|
mut state,
|
||||||
|
is_new,
|
||||||
|
corner_interact,
|
||||||
|
content_ui,
|
||||||
|
} = prepared;
|
||||||
|
|
||||||
|
let desired_size = content_ui.bounding_size();
|
||||||
let desired_size = desired_size.ceil(); // Avoid rounding errors in math
|
let desired_size = desired_size.ceil(); // Avoid rounding errors in math
|
||||||
|
|
||||||
// ------------------------------
|
// ------------------------------
|
||||||
|
@ -279,7 +309,7 @@ impl Resize {
|
||||||
// ------------------------------
|
// ------------------------------
|
||||||
|
|
||||||
if self.outline && corner_interact.is_some() {
|
if self.outline && corner_interact.is_some() {
|
||||||
let rect = Rect::from_min_size(position, state.size);
|
let rect = Rect::from_min_size(content_ui.top_left(), state.size);
|
||||||
let rect = rect.expand(2.0); // breathing room for content
|
let rect = rect.expand(2.0); // breathing room for content
|
||||||
ui.add_paint_cmd(paint::PaintCmd::Rect {
|
ui.add_paint_cmd(paint::PaintCmd::Rect {
|
||||||
rect,
|
rect,
|
||||||
|
@ -298,8 +328,6 @@ impl Resize {
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.memory().resize.insert(id, state);
|
ui.memory().resize.insert(id, state);
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,15 +44,30 @@ impl ScrollArea {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollArea {
|
struct Prepared {
|
||||||
pub fn show(self, outer_ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
|
id: Id,
|
||||||
let ctx = outer_ui.ctx().clone();
|
state: State,
|
||||||
|
current_scroll_bar_width: f32,
|
||||||
|
always_show_scroll: bool,
|
||||||
|
inner_rect: Rect,
|
||||||
|
content_ui: Ui,
|
||||||
|
}
|
||||||
|
|
||||||
let scroll_area_id = outer_ui.make_child_id("scroll_area");
|
impl ScrollArea {
|
||||||
let mut state = ctx
|
fn prepare(self, ui: &mut Ui) -> Prepared {
|
||||||
|
let Self {
|
||||||
|
max_height,
|
||||||
|
always_show_scroll,
|
||||||
|
auto_hide_scroll,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let ctx = ui.ctx().clone();
|
||||||
|
|
||||||
|
let id = ui.make_child_id("scroll_area");
|
||||||
|
let state = ctx
|
||||||
.memory()
|
.memory()
|
||||||
.scroll_areas
|
.scroll_areas
|
||||||
.get(&scroll_area_id)
|
.get(&id)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
@ -62,29 +77,55 @@ impl ScrollArea {
|
||||||
|
|
||||||
let max_scroll_bar_width = 16.0;
|
let max_scroll_bar_width = 16.0;
|
||||||
|
|
||||||
let current_scroll_bar_width = if state.show_scroll || !self.auto_hide_scroll {
|
let current_scroll_bar_width = if state.show_scroll || !auto_hide_scroll {
|
||||||
max_scroll_bar_width // TODO: animate?
|
max_scroll_bar_width // TODO: animate?
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
|
||||||
let outer_size = vec2(
|
let outer_size = vec2(
|
||||||
outer_ui.available().width(),
|
ui.available().width(),
|
||||||
outer_ui.available().height().min(self.max_height),
|
ui.available().height().min(max_height),
|
||||||
);
|
);
|
||||||
|
|
||||||
let inner_size = outer_size - vec2(current_scroll_bar_width, 0.0);
|
let inner_size = outer_size - vec2(current_scroll_bar_width, 0.0);
|
||||||
let inner_rect = Rect::from_min_size(outer_ui.available().min, inner_size);
|
let inner_rect = Rect::from_min_size(ui.available().min, inner_size);
|
||||||
|
|
||||||
let mut content_ui = outer_ui.child_ui(Rect::from_min_size(
|
let mut content_ui = ui.child_ui(Rect::from_min_size(
|
||||||
inner_rect.min - state.offset,
|
inner_rect.min - state.offset,
|
||||||
vec2(inner_size.x, f32::INFINITY),
|
vec2(inner_size.x, f32::INFINITY),
|
||||||
));
|
));
|
||||||
let mut content_clip_rect = outer_ui.clip_rect().intersect(inner_rect);
|
let mut content_clip_rect = ui.clip_rect().intersect(inner_rect);
|
||||||
content_clip_rect.max.x = outer_ui.clip_rect().max.x - current_scroll_bar_width; // Nice handling of forced resizing beyond the possible
|
content_clip_rect.max.x = ui.clip_rect().max.x - current_scroll_bar_width; // Nice handling of forced resizing beyond the possible
|
||||||
content_ui.set_clip_rect(content_clip_rect);
|
content_ui.set_clip_rect(content_clip_rect);
|
||||||
|
|
||||||
add_contents(&mut content_ui);
|
Prepared {
|
||||||
|
id,
|
||||||
|
state,
|
||||||
|
always_show_scroll,
|
||||||
|
inner_rect,
|
||||||
|
current_scroll_bar_width,
|
||||||
|
content_ui,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> R {
|
||||||
|
let mut prepared = self.prepare(ui);
|
||||||
|
let ret = add_contents(&mut prepared.content_ui);
|
||||||
|
Self::finish(ui, prepared);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(ui: &mut Ui, prepared: Prepared) {
|
||||||
|
let Prepared {
|
||||||
|
id,
|
||||||
|
mut state,
|
||||||
|
inner_rect,
|
||||||
|
always_show_scroll,
|
||||||
|
current_scroll_bar_width,
|
||||||
|
content_ui,
|
||||||
|
} = prepared;
|
||||||
|
|
||||||
let content_size = content_ui.bounding_size();
|
let content_size = content_ui.bounding_size();
|
||||||
|
|
||||||
let inner_rect = Rect::from_min_size(
|
let inner_rect = Rect::from_min_size(
|
||||||
|
@ -100,22 +141,22 @@ impl ScrollArea {
|
||||||
inner_rect.size() + vec2(current_scroll_bar_width, 0.0),
|
inner_rect.size() + vec2(current_scroll_bar_width, 0.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
let content_is_too_small = content_size.y > inner_size.y;
|
let content_is_too_small = content_size.y > inner_rect.height();
|
||||||
|
|
||||||
if content_is_too_small {
|
if content_is_too_small {
|
||||||
// Dragg contents to scroll (for touch screens mostly):
|
// Dragg contents to scroll (for touch screens mostly):
|
||||||
let content_interact = outer_ui.interact_rect(inner_rect, scroll_area_id.with("area"));
|
let content_interact = ui.interact_rect(inner_rect, id.with("area"));
|
||||||
if content_interact.active {
|
if content_interact.active {
|
||||||
state.offset.y -= ctx.input().mouse_move.y;
|
state.offset.y -= ui.input().mouse_move.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check that nothing else is being inteacted with
|
// TODO: check that nothing else is being inteacted with
|
||||||
if outer_ui.contains_mouse(outer_rect) && ctx.memory().active_id.is_none() {
|
if ui.contains_mouse(outer_rect) && ui.memory().active_id.is_none() {
|
||||||
state.offset.y -= ctx.input().scroll_delta.y;
|
state.offset.y -= ui.input().scroll_delta.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
let show_scroll_this_frame = content_is_too_small || self.always_show_scroll;
|
let show_scroll_this_frame = content_is_too_small || always_show_scroll;
|
||||||
if show_scroll_this_frame || state.show_scroll {
|
if show_scroll_this_frame || state.show_scroll {
|
||||||
let left = inner_rect.right() + 2.0;
|
let left = inner_rect.right() + 2.0;
|
||||||
let right = outer_rect.right();
|
let right = outer_rect.right();
|
||||||
|
@ -137,18 +178,18 @@ impl ScrollArea {
|
||||||
);
|
);
|
||||||
|
|
||||||
// intentionally use same id for inside and outside of handle
|
// intentionally use same id for inside and outside of handle
|
||||||
let interact_id = scroll_area_id.with("vertical");
|
let interact_id = id.with("vertical");
|
||||||
let handle_interact = outer_ui.interact_rect(handle_rect, interact_id);
|
let handle_interact = ui.interact_rect(handle_rect, interact_id);
|
||||||
|
|
||||||
if let Some(mouse_pos) = ctx.input().mouse_pos {
|
if let Some(mouse_pos) = ui.input().mouse_pos {
|
||||||
if handle_interact.active {
|
if handle_interact.active {
|
||||||
if inner_rect.top() <= mouse_pos.y && mouse_pos.y <= inner_rect.bottom() {
|
if inner_rect.top() <= mouse_pos.y && mouse_pos.y <= inner_rect.bottom() {
|
||||||
state.offset.y +=
|
state.offset.y +=
|
||||||
ctx.input().mouse_move.y * content_size.y / inner_rect.height();
|
ui.input().mouse_move.y * content_size.y / inner_rect.height();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Check for mouse down outside handle:
|
// Check for mouse down outside handle:
|
||||||
let scroll_bg_interact = outer_ui.interact_rect(outer_scroll_rect, interact_id);
|
let scroll_bg_interact = ui.interact_rect(outer_scroll_rect, interact_id);
|
||||||
|
|
||||||
if scroll_bg_interact.active {
|
if scroll_bg_interact.active {
|
||||||
// Center scroll at mouse pos:
|
// Center scroll at mouse pos:
|
||||||
|
@ -167,18 +208,18 @@ impl ScrollArea {
|
||||||
pos2(right, from_content(state.offset.y + inner_rect.height())),
|
pos2(right, from_content(state.offset.y + inner_rect.height())),
|
||||||
);
|
);
|
||||||
|
|
||||||
let style = outer_ui.style();
|
let style = ui.style();
|
||||||
let handle_fill_color = style.interact(&handle_interact).fill_color;
|
let handle_fill_color = style.interact(&handle_interact).fill_color;
|
||||||
let handle_outline = style.interact(&handle_interact).rect_outline;
|
let handle_outline = style.interact(&handle_interact).rect_outline;
|
||||||
|
|
||||||
outer_ui.add_paint_cmd(paint::PaintCmd::Rect {
|
ui.add_paint_cmd(paint::PaintCmd::Rect {
|
||||||
rect: outer_scroll_rect,
|
rect: outer_scroll_rect,
|
||||||
corner_radius,
|
corner_radius,
|
||||||
fill_color: Some(outer_ui.style().dark_bg_color),
|
fill_color: Some(ui.style().dark_bg_color),
|
||||||
outline: None,
|
outline: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
outer_ui.add_paint_cmd(paint::PaintCmd::Rect {
|
ui.add_paint_cmd(paint::PaintCmd::Rect {
|
||||||
rect: handle_rect.expand(-2.0),
|
rect: handle_rect.expand(-2.0),
|
||||||
corner_radius,
|
corner_radius,
|
||||||
fill_color: Some(handle_fill_color),
|
fill_color: Some(handle_fill_color),
|
||||||
|
@ -192,12 +233,12 @@ impl ScrollArea {
|
||||||
// content_size.y.min(inner_rect.size().y), // respect vertical height.
|
// content_size.y.min(inner_rect.size().y), // respect vertical height.
|
||||||
// );
|
// );
|
||||||
let size = outer_rect.size();
|
let size = outer_rect.size();
|
||||||
outer_ui.reserve_space(size, None);
|
ui.reserve_space(size, None);
|
||||||
|
|
||||||
state.offset.y = state.offset.y.min(content_size.y - inner_rect.height());
|
state.offset.y = state.offset.y.min(content_size.y - inner_rect.height());
|
||||||
state.offset.y = state.offset.y.max(0.0);
|
state.offset.y = state.offset.y.max(0.0);
|
||||||
state.show_scroll = show_scroll_this_frame;
|
state.show_scroll = show_scroll_this_frame;
|
||||||
|
|
||||||
outer_ui.memory().scroll_areas.insert(scroll_area_id, state);
|
ui.memory().scroll_areas.insert(id, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -313,6 +313,7 @@ impl Ui {
|
||||||
.interact(self.layer, self.clip_rect, rect, Some(id))
|
.interact(self.layer, self.clip_rect, rect, Some(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn response(&mut self, interact: InteractInfo) -> GuiResponse {
|
pub fn response(&mut self, interact: InteractInfo) -> GuiResponse {
|
||||||
// TODO: unify GuiResponse and InteractInfo. They are the same thing!
|
// TODO: unify GuiResponse and InteractInfo. They are the same thing!
|
||||||
GuiResponse {
|
GuiResponse {
|
||||||
|
|
Loading…
Reference in a new issue