[window] auto-position windows in a column layout

This commit is contained in:
Emil Ernerfeldt 2020-05-30 17:53:02 +02:00
parent 347fdb97d6
commit 2f4a3a1273
5 changed files with 82 additions and 10 deletions

View file

@ -17,6 +17,7 @@ This is the core library crate Egui. It is fully platform independent without an
* Then we could open the example app inside a window in the example app, recursively.
* [x] Resize any side and corner on windows
* [x] Fix autoshrink
* [x] Automatic positioning of new windows
* [ ] Scroll areas
* [x] Vertical scrolling
* [ ] Horizontal scrolling

View file

@ -116,12 +116,11 @@ impl Area {
fixed_pos,
} = self;
let default_pos = default_pos.unwrap_or_else(|| pos2(100.0, 100.0)); // TODO
let id = ctx.register_unique_id(id, "Area", default_pos);
let layer = Layer { order, id };
let mut state = ctx.memory().areas.get(id).unwrap_or_else(|| State {
pos: default_pos,
let state = ctx.memory().areas.get(id).cloned();
let mut state = state.unwrap_or_else(|| State {
pos: default_pos.unwrap_or_else(|| automatic_area_position(ctx)),
size: Vec2::zero(),
interactable,
vel: Vec2::zero(),
@ -233,3 +232,59 @@ fn mouse_pressed_on_area(ctx: &Context, layer: Layer) -> bool {
false
}
}
fn automatic_area_position(ctx: &Context) -> Pos2 {
let mut existing: Vec<Rect> = ctx
.memory()
.areas
.visible_windows()
.into_iter()
.map(State::rect)
.collect();
existing.sort_by_key(|r| r.left().round() as i32);
let left = 16.0;
let top = 32.0; // allow existence of menu bar. TODO: get from ui.available()
let spacing = 32.0;
if existing.is_empty() {
return pos2(left, top);
}
// Separate existing rectangles into columns:
let mut column_bbs = vec![existing[0]];
for &rect in &existing {
let current_column_bb = column_bbs.last_mut().unwrap();
if rect.left() < current_column_bb.right() {
// same column
*current_column_bb = current_column_bb.union(rect);
} else {
// new column
column_bbs.push(rect);
}
}
// Find first column with some available space at the bottom of it:
for col_bb in &column_bbs {
if col_bb.bottom() < ctx.input().screen_size.y * 0.5 {
return pos2(col_bb.left(), col_bb.bottom() + spacing);
}
}
// Maybe we can fit a new column?
let rightmost = column_bbs.last().unwrap().right();
if rightmost < ctx.input().screen_size.x - 200.0 {
return pos2(rightmost + spacing, top);
}
// Ok, just put us in the column with the most space at the bottom:
let mut best_pos = pos2(left, column_bbs[0].bottom() + spacing);
for col_bb in &column_bbs {
let col_pos = pos2(col_bb.left(), col_bb.bottom() + spacing);
if col_pos.y < best_pos.y {
best_pos = col_pos;
}
}
best_pos
}

View file

@ -399,8 +399,8 @@ impl Context {
);
}
pub fn debug_rect(&self, rect: Rect, text: impl Into<String>) {
let text = text.into();
pub fn debug_rect(&self, rect: Rect, name: impl Into<String>) {
let text = format!("{} {:?}", name.into(), rect);
let layer = Layer::debug();
self.add_paint_cmd(
layer,

View file

@ -98,7 +98,7 @@ impl Memory {
if window_interaction.is_pure_move() {
// Throw windows because it is fun:
let area_layer = window_interaction.area_layer;
let area_state = self.areas.get(area_layer.id);
let area_state = self.areas.get(area_layer.id).cloned();
if let Some(mut area_state) = area_state {
area_state.vel = prev_input.mouse.velocity;
self.areas.set_state(area_layer, area_state);
@ -123,8 +123,8 @@ impl Areas {
self.areas.len()
}
pub(crate) fn get(&mut self, id: Id) -> Option<area::State> {
self.areas.get(&id).cloned()
pub(crate) fn get(&self, id: Id) -> Option<&area::State> {
self.areas.get(&id)
}
pub(crate) fn order(&self) -> &[Layer] {
@ -164,6 +164,22 @@ impl Areas {
self.visible_last_frame.contains(layer) || self.visible_current_frame.contains(layer)
}
pub fn visible_layers(&self) -> HashSet<Layer> {
self.visible_last_frame
.iter()
.cloned()
.chain(self.visible_current_frame.iter().cloned())
.collect()
}
pub(crate) fn visible_windows(&self) -> Vec<&area::State> {
self.visible_layers()
.iter()
.filter(|layer| layer.order == crate::layers::Order::Middle)
.filter_map(|layer| self.get(layer.id))
.collect()
}
pub fn move_to_top(&mut self, layer: Layer) {
self.visible_current_frame.insert(layer);
self.wants_to_be_on_top.insert(layer);

View file

@ -151,7 +151,7 @@ impl<'t> Widget for TextEdit<'t> {
}
fn insert_text(cursor: &mut usize, text: &mut String, text_to_insert: &str) {
eprintln!("insert_text {:?}", text_to_insert);
// eprintln!("insert_text {:?}", text_to_insert);
let mut char_it = text.chars();
let mut new_text = String::with_capacity(text.capacity());