Skip to content

Commit

Permalink
[window] auto-position windows in a column layout
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed May 30, 2020
1 parent 347fdb9 commit 2f4a3a1
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 10 deletions.
1 change: 1 addition & 0 deletions egui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
63 changes: 59 additions & 4 deletions egui/src/containers/area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 19 additions & 3 deletions egui/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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] {
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion egui/src/widgets/text_edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down

0 comments on commit 2f4a3a1

Please sign in to comment.