[window] auto-position windows in a column layout
This commit is contained in:
parent
347fdb97d6
commit
2f4a3a1273
5 changed files with 82 additions and 10 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in a new issue