mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 13:50:04 -04:00
Last cleanup pass
This commit is contained in:
@@ -25,7 +25,7 @@ pub trait Behavior<Leaf> {
|
|||||||
if let Some(node) = nodes.nodes.get(&node_id) {
|
if let Some(node) = nodes.nodes.get(&node_id) {
|
||||||
match node {
|
match node {
|
||||||
Node::Leaf(leaf) => self.tab_title_for_leaf(leaf),
|
Node::Leaf(leaf) => self.tab_title_for_leaf(leaf),
|
||||||
Node::Branch(branch) => format!("{:?}", branch.get_layout()).into(),
|
Node::Branch(branch) => format!("{:?}", branch.layout()).into(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
"MISSING NODE".into()
|
"MISSING NODE".into()
|
||||||
|
|||||||
@@ -198,8 +198,6 @@ impl Grid {
|
|||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
) {
|
) {
|
||||||
// Grid drops are handled during layout. TODO: handle here instead.
|
|
||||||
|
|
||||||
for &child in &self.children {
|
for &child in &self.children {
|
||||||
nodes.node_ui(behavior, drop_context, ui, child);
|
nodes.node_ui(behavior, drop_context, ui, child);
|
||||||
}
|
}
|
||||||
@@ -425,7 +423,10 @@ fn shrink_shares<Leaf>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sizes_from_shares(shares: &[f32], available_size: f32, gap_width: f32) -> Vec<f32> {
|
fn sizes_from_shares(shares: &[f32], available_size: f32, gap_width: f32) -> Vec<f32> {
|
||||||
assert!(!shares.is_empty());
|
if shares.is_empty() {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
let available_size = available_size - gap_width * (shares.len() - 1) as f32;
|
let available_size = available_size - gap_width * (shares.len() - 1) as f32;
|
||||||
let available_size = available_size.at_least(0.0);
|
let available_size = available_size.at_least(0.0);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use egui::{pos2, vec2, NumExt, Rect};
|
use egui::{pos2, vec2, NumExt, Rect};
|
||||||
use itertools::Itertools as _;
|
use itertools::Itertools as _;
|
||||||
|
|
||||||
@@ -6,7 +8,60 @@ use crate::dock::{
|
|||||||
ResizeState,
|
ResizeState,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Shares;
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// How large of a share of space each child has, on a 1D axis.
|
||||||
|
///
|
||||||
|
/// Used for [`Linear`] layouts (horizontal and vertical).
|
||||||
|
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Shares {
|
||||||
|
/// How large of a share each child has.
|
||||||
|
///
|
||||||
|
/// For instance, the shares `[1, 2, 3]` means that the first child gets 1/6 of the space,
|
||||||
|
/// the second gets 2/6 and the third gets 3/6.
|
||||||
|
shares: HashMap<NodeId, f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shares {
|
||||||
|
pub fn replace_with(&mut self, remove: NodeId, new: NodeId) {
|
||||||
|
if let Some(share) = self.shares.remove(&remove) {
|
||||||
|
self.shares.insert(new, share);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split the given width based on the share of the children.
|
||||||
|
pub fn split(&self, children: &[NodeId], available_width: f32) -> Vec<f32> {
|
||||||
|
let mut num_shares = 0.0;
|
||||||
|
for &child in children {
|
||||||
|
num_shares += self[child];
|
||||||
|
}
|
||||||
|
if num_shares == 0.0 {
|
||||||
|
num_shares = 1.0;
|
||||||
|
}
|
||||||
|
children
|
||||||
|
.iter()
|
||||||
|
.map(|&child| available_width * self[child] / num_shares)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Index<NodeId> for Shares {
|
||||||
|
type Output = f32;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, id: NodeId) -> &Self::Output {
|
||||||
|
self.shares.get(&id).unwrap_or(&1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::IndexMut<NodeId> for Shares {
|
||||||
|
#[inline]
|
||||||
|
fn index_mut(&mut self, id: NodeId) -> &mut Self::Output {
|
||||||
|
self.shares.entry(id).or_insert(1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum LinearDir {
|
pub enum LinearDir {
|
||||||
@@ -350,7 +405,8 @@ fn linear_drop_zones<Leaf>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drop_zones(
|
/// Register drop-zones for a linear layout.
|
||||||
|
pub(super) fn drop_zones(
|
||||||
preview_thickness: f32,
|
preview_thickness: f32,
|
||||||
children: &[NodeId],
|
children: &[NodeId],
|
||||||
dragged_index: Option<usize>,
|
dragged_index: Option<usize>,
|
||||||
@@ -407,7 +463,9 @@ pub fn drop_zones(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(last_rect) = prev_rect {
|
if let Some(last_rect) = prev_rect {
|
||||||
// Suggest dropping after the last child:
|
// Suggest dropping after the last child (unless that's the one being dragged):
|
||||||
add_drop_drect(after_rect(last_rect), insertion_index + 1);
|
if dragged_index != Some(children.len() - 1) {
|
||||||
|
add_drop_drect(after_rect(last_rect), insertion_index + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use egui::Rect;
|
use egui::Rect;
|
||||||
|
|
||||||
use super::{Behavior, DropContext, NodeId, Nodes, SimplifyAction};
|
use super::{Behavior, DropContext, NodeId, Nodes, SimplifyAction};
|
||||||
@@ -9,7 +7,7 @@ mod linear;
|
|||||||
mod tabs;
|
mod tabs;
|
||||||
|
|
||||||
pub use grid::{Grid, GridLoc};
|
pub use grid::{Grid, GridLoc};
|
||||||
pub use linear::{Linear, LinearDir};
|
pub use linear::{Linear, LinearDir, Shares};
|
||||||
pub use tabs::Tabs;
|
pub use tabs::Tabs;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -29,53 +27,6 @@ impl Layout {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// How large of a share of space each child has, on a 1D axis.
|
|
||||||
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct Shares {
|
|
||||||
/// How large of a share each child has.
|
|
||||||
///
|
|
||||||
/// For instance, the shares `[1, 2, 3]` means that the first child gets 1/6 of the space,
|
|
||||||
/// the second gets 2/6 and the third gets 3/6.
|
|
||||||
shares: HashMap<NodeId, f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Shares {
|
|
||||||
pub fn replace_with(&mut self, a: NodeId, b: NodeId) {
|
|
||||||
if let Some(share) = self.shares.remove(&a) {
|
|
||||||
self.shares.insert(b, share);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn split(&self, children: &[NodeId], available_width: f32) -> Vec<f32> {
|
|
||||||
let mut num_shares = 0.0;
|
|
||||||
for &child in children {
|
|
||||||
num_shares += self[child];
|
|
||||||
}
|
|
||||||
if num_shares == 0.0 {
|
|
||||||
num_shares = 1.0;
|
|
||||||
}
|
|
||||||
children
|
|
||||||
.iter()
|
|
||||||
.map(|&child| available_width * self[child] / num_shares)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Index<NodeId> for Shares {
|
|
||||||
type Output = f32;
|
|
||||||
fn index(&self, id: NodeId) -> &Self::Output {
|
|
||||||
self.shares.get(&id).unwrap_or(&1.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::IndexMut<NodeId> for Shares {
|
|
||||||
fn index_mut(&mut self, id: NodeId) -> &mut Self::Output {
|
|
||||||
self.shares.entry(id).or_insert(1.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum Branch {
|
pub enum Branch {
|
||||||
Tabs(Tabs),
|
Tabs(Tabs),
|
||||||
@@ -116,7 +67,7 @@ impl Branch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_layout(&self) -> Layout {
|
pub fn layout(&self) -> Layout {
|
||||||
match self {
|
match self {
|
||||||
Self::Tabs(_) => Layout::Tabs,
|
Self::Tabs(_) => Layout::Tabs,
|
||||||
Self::Linear(linear) => match linear.dir {
|
Self::Linear(linear) => match linear.dir {
|
||||||
@@ -128,7 +79,7 @@ impl Branch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_layout(&mut self, layout: Layout) {
|
pub fn set_layout(&mut self, layout: Layout) {
|
||||||
if layout == self.get_layout() {
|
if layout == self.layout() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +142,7 @@ impl Branch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Branch {
|
impl Branch {
|
||||||
pub(super) fn layout<Leaf>(
|
pub(super) fn layout_recursive<Leaf>(
|
||||||
&mut self,
|
&mut self,
|
||||||
nodes: &mut Nodes<Leaf>,
|
nodes: &mut Nodes<Leaf>,
|
||||||
style: &egui::Style,
|
style: &egui::Style,
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ use crate::dock::{
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Tabs {
|
pub struct Tabs {
|
||||||
|
/// The tabs, in order.
|
||||||
pub children: Vec<NodeId>,
|
pub children: Vec<NodeId>,
|
||||||
|
|
||||||
|
/// The currenlty open tab.
|
||||||
pub active: NodeId,
|
pub active: NodeId,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +29,7 @@ impl Tabs {
|
|||||||
self.active = child;
|
self.active = child;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout<Leaf>(
|
pub(super) fn layout<Leaf>(
|
||||||
&mut self,
|
&mut self,
|
||||||
nodes: &mut Nodes<Leaf>,
|
nodes: &mut Nodes<Leaf>,
|
||||||
style: &egui::Style,
|
style: &egui::Style,
|
||||||
@@ -62,10 +65,11 @@ impl Tabs {
|
|||||||
nodes.node_ui(behavior, drop_context, ui, self.active);
|
nodes.node_ui(behavior, drop_context, ui, self.active);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have only laid out the active tab, so we need to switch active tab after the ui pass:
|
// We have only laid out the active tab, so we need to switch active tab _after_ the ui pass above:
|
||||||
self.active = next_active;
|
self.active = next_active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the next active tab (e.g. the one clicked, or the current).
|
||||||
fn tab_bar_ui<Leaf>(
|
fn tab_bar_ui<Leaf>(
|
||||||
&self,
|
&self,
|
||||||
behavior: &mut dyn Behavior<Leaf>,
|
behavior: &mut dyn Behavior<Leaf>,
|
||||||
|
|||||||
@@ -16,9 +16,15 @@
|
|||||||
//! The user needs to implement this in order to specify the `ui` of each `Leaf` and
|
//! The user needs to implement this in order to specify the `ui` of each `Leaf` and
|
||||||
//! the tab name of leaves (if there are tab nodes).
|
//! the tab name of leaves (if there are tab nodes).
|
||||||
//!
|
//!
|
||||||
//! ## Shortcomings
|
//! ## Shares
|
||||||
//! We use real recursion, so if your trees get too deep you will get a stack overflow.
|
//! The relative sizes of linear layout (horizontal or vertical) and grid columns and rows are spcified by _shares_.
|
||||||
|
//! If the shares are `1,2,3` it means the first element gets `1/6` of the space, the second `2/6`, and the third `3/6`.
|
||||||
|
//! The default share size is `1`, and when resizing the shares are restributed so that
|
||||||
|
//! the total shares are always aproximately the same as the number of rows/columns.
|
||||||
|
//! This makes it easy to add new rows/columns.
|
||||||
//!
|
//!
|
||||||
|
//! ## Shortcomings
|
||||||
|
//! The implementation is recursive, so if your trees get too deep you will get a stack overflow.
|
||||||
//!
|
//!
|
||||||
//! ## Future improvements
|
//! ## Future improvements
|
||||||
//! * Easy per-tab close-buttons
|
//! * Easy per-tab close-buttons
|
||||||
@@ -93,7 +99,7 @@ impl<Leaf> Node<Leaf> {
|
|||||||
fn layout(&self) -> Option<Layout> {
|
fn layout(&self) -> Option<Layout> {
|
||||||
match self {
|
match self {
|
||||||
Node::Leaf(_) => None,
|
Node::Leaf(_) => None,
|
||||||
Node::Branch(branch) => Some(branch.get_layout()),
|
Node::Branch(branch) => Some(branch.layout()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
self.rects.insert(node_id, rect);
|
self.rects.insert(node_id, rect);
|
||||||
|
|
||||||
if let Node::Branch(branch) = &mut node {
|
if let Node::Branch(branch) = &mut node {
|
||||||
branch.layout(self, style, behavior, rect);
|
branch.layout_recursive(self, style, behavior, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.nodes.insert(node_id, node);
|
self.nodes.insert(node_id, node);
|
||||||
@@ -296,7 +296,7 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
|
|
||||||
branch.simplify_children(|child| self.simplify(options, child));
|
branch.simplify_children(|child| self.simplify(options, child));
|
||||||
|
|
||||||
if branch.get_layout() == Layout::Tabs {
|
if branch.layout() == Layout::Tabs {
|
||||||
if options.prune_empty_tabs && branch.is_empty() {
|
if options.prune_empty_tabs && branch.is_empty() {
|
||||||
log::debug!("Simplify: removing empty tabs node");
|
log::debug!("Simplify: removing empty tabs node");
|
||||||
return SimplifyAction::Remove;
|
return SimplifyAction::Remove;
|
||||||
@@ -346,7 +346,7 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Node::Branch(branch) => {
|
Node::Branch(branch) => {
|
||||||
let is_tabs = branch.get_layout() == Layout::Tabs;
|
let is_tabs = branch.layout() == Layout::Tabs;
|
||||||
for &child in branch.children() {
|
for &child in branch.children() {
|
||||||
self.make_all_leaves_children_of_tabs(is_tabs, child);
|
self.make_all_leaves_children_of_tabs(is_tabs, child);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ fn tree_ui(
|
|||||||
.show(ui, |ui| match &mut node {
|
.show(ui, |ui| match &mut node {
|
||||||
dock::Node::Leaf(_) => {}
|
dock::Node::Leaf(_) => {}
|
||||||
dock::Node::Branch(branch) => {
|
dock::Node::Branch(branch) => {
|
||||||
let mut layout = branch.get_layout();
|
let mut layout = branch.layout();
|
||||||
egui::ComboBox::from_label("Layout")
|
egui::ComboBox::from_label("Layout")
|
||||||
.selected_text(format!("{:?}", layout))
|
.selected_text(format!("{:?}", layout))
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
@@ -272,7 +272,7 @@ fn tree_ui(
|
|||||||
.clicked();
|
.clicked();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if layout != branch.get_layout() {
|
if layout != branch.layout() {
|
||||||
branch.set_layout(layout);
|
branch.set_layout(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user