Break up some long container functions and reduce some code bloat

This commit is contained in:
Emil Ernerfeldt 2020-05-20 18:39:35 +02:00
parent 8270d59dbf
commit 002944b775
5 changed files with 172 additions and 69 deletions

View file

@ -91,13 +91,15 @@ impl Area {
}
}
impl Area {
// TODO
// pub fn show(self, ui: &Ui, add_contents: impl FnOnce(&mut Ui)) {
// let default_pos = self.default_pos.unwrap_or_else(|| ui.top_left() + pos2(100.0, 100.0)); // TODO
// }
struct Prepared {
layer: Layer,
state: State,
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 {
id,
movable,
@ -120,19 +122,45 @@ impl Area {
state.pos = fixed_pos.unwrap_or(state.pos);
state.pos = state.pos.round();
let mut ui = Ui::new(
let content_ui = Ui::new(
ctx.clone(),
layer,
id,
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 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 input = ctx.input();

View file

@ -162,8 +162,13 @@ impl CollapsingHeader {
}
}
struct Prepared {
id: Id,
state: State,
}
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!(
ui.layout().dir() == Direction::Vertical,
"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 ret = r_interact.map(|ri| ri.0);
ui.memory().collapsing_headers.insert(id, state);
ui.response(interact);
ret
}
}

View file

@ -168,8 +168,16 @@ impl Resize {
}
}
struct Prepared {
id: Id,
state: State,
is_new: bool,
corner_interact: Option<InteractInfo>,
content_ui: Ui,
}
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"));
self.min_size = self.min_size.min(ui.available().size());
self.max_size = self.max_size.min(ui.available().size());
@ -232,8 +240,6 @@ impl Resize {
let inner_rect = Rect::from_min_size(position, state.size);
let (ret, desired_size);
{
let mut content_clip_rect = ui
.clip_rect()
.intersect(inner_rect.expand(ui.style().clip_rect_margin));
@ -248,11 +254,35 @@ impl Resize {
.max(content_clip_rect.min + last_frame_size)
.min(ui.clip_rect().max); // Respect parent region
let mut contents_ui = ui.child_ui(inner_rect);
contents_ui.set_clip_rect(content_clip_rect);
ret = add_contents(&mut contents_ui);
desired_size = contents_ui.bounding_size();
};
let mut content_ui = ui.child_ui(inner_rect);
content_ui.set_clip_rect(content_clip_rect);
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
// ------------------------------
@ -279,7 +309,7 @@ impl Resize {
// ------------------------------
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
ui.add_paint_cmd(paint::PaintCmd::Rect {
rect,
@ -298,8 +328,6 @@ impl Resize {
}
ui.memory().resize.insert(id, state);
ret
}
}

View file

@ -44,15 +44,30 @@ impl ScrollArea {
}
}
impl ScrollArea {
pub fn show(self, outer_ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
let ctx = outer_ui.ctx().clone();
struct Prepared {
id: Id,
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");
let mut state = ctx
impl ScrollArea {
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()
.scroll_areas
.get(&scroll_area_id)
.get(&id)
.cloned()
.unwrap_or_default();
@ -62,29 +77,55 @@ impl ScrollArea {
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?
} else {
0.0
};
let outer_size = vec2(
outer_ui.available().width(),
outer_ui.available().height().min(self.max_height),
ui.available().width(),
ui.available().height().min(max_height),
);
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,
vec2(inner_size.x, f32::INFINITY),
));
let mut content_clip_rect = outer_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
let mut content_clip_rect = ui.clip_rect().intersect(inner_rect);
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);
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 inner_rect = Rect::from_min_size(
@ -100,22 +141,22 @@ impl ScrollArea {
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 {
// 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 {
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
if outer_ui.contains_mouse(outer_rect) && ctx.memory().active_id.is_none() {
state.offset.y -= ctx.input().scroll_delta.y;
if ui.contains_mouse(outer_rect) && ui.memory().active_id.is_none() {
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 {
let left = inner_rect.right() + 2.0;
let right = outer_rect.right();
@ -137,18 +178,18 @@ impl ScrollArea {
);
// intentionally use same id for inside and outside of handle
let interact_id = scroll_area_id.with("vertical");
let handle_interact = outer_ui.interact_rect(handle_rect, interact_id);
let interact_id = id.with("vertical");
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 inner_rect.top() <= mouse_pos.y && mouse_pos.y <= inner_rect.bottom() {
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 {
// 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 {
// Center scroll at mouse pos:
@ -167,18 +208,18 @@ impl ScrollArea {
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_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,
corner_radius,
fill_color: Some(outer_ui.style().dark_bg_color),
fill_color: Some(ui.style().dark_bg_color),
outline: None,
});
outer_ui.add_paint_cmd(paint::PaintCmd::Rect {
ui.add_paint_cmd(paint::PaintCmd::Rect {
rect: handle_rect.expand(-2.0),
corner_radius,
fill_color: Some(handle_fill_color),
@ -192,12 +233,12 @@ impl ScrollArea {
// content_size.y.min(inner_rect.size().y), // respect vertical height.
// );
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.max(0.0);
state.show_scroll = show_scroll_this_frame;
outer_ui.memory().scroll_areas.insert(scroll_area_id, state);
ui.memory().scroll_areas.insert(id, state);
}
}

View file

@ -313,6 +313,7 @@ impl Ui {
.interact(self.layer, self.clip_rect, rect, Some(id))
}
#[must_use]
pub fn response(&mut self, interact: InteractInfo) -> GuiResponse {
// TODO: unify GuiResponse and InteractInfo. They are the same thing!
GuiResponse {