diff --git a/src/ui.rs b/src/ui.rs
index ff73104d0525856ecd195db04506e3906352ee23..b2b096645c31b0dbca79497bae801b9be3b7a016 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -1,5 +1,6 @@
 mod composable_view;
 mod panes;
 mod shortcuts;
+mod utils;
 
 pub use composable_view::ComposableView;
diff --git a/src/ui/panes/default.rs b/src/ui/panes/default.rs
index 780dfa7df8975e87d5671aac368e798167643108..ca6fd663644056c9e5217745ad6ddd8440b785a9 100644
--- a/src/ui/panes/default.rs
+++ b/src/ui/panes/default.rs
@@ -1,20 +1,22 @@
 use super::{plot_2d::Plot2DPane, Pane, PaneBehavior, PaneKind};
 use serde::{Deserialize, Serialize};
 
-use crate::ui::composable_view::{PaneAction, PaneResponse};
+use crate::ui::{
+    composable_view::{PaneAction, PaneResponse},
+    utils::{vertically_centered, SizingMemo},
+};
 
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct DefaultPane {
-    occupied: f32,
-    fixed: bool,
+    #[serde(skip)]
+    centering_memo: SizingMemo,
     contains_pointer: bool,
 }
 
 impl Default for DefaultPane {
     fn default() -> Self {
         DefaultPane {
-            occupied: 0.0,
-            fixed: false,
+            centering_memo: SizingMemo::default(),
             contains_pointer: false,
         }
     }
@@ -23,48 +25,29 @@ impl Default for DefaultPane {
 impl PaneBehavior for DefaultPane {
     fn ui(&mut self, ui: &mut egui::Ui) -> PaneResponse {
         let mut response = PaneResponse::default();
-        let pane_rect = ui.max_rect();
 
-        let parent = ui.vertical_centered(|ui| {
-            let hpad = (pane_rect.height() - self.occupied) / 2.0;
-            if self.fixed {
-                ui.add_space(hpad);
-            }
-            let mut height_occupied = 0.0;
-            let btn = ui.button("Vertical Split");
-            if btn.clicked() {
-                response.set_action(PaneAction::SplitV);
-                log::debug!("Vertical Split button clicked");
-            }
-            height_occupied += btn.rect.height();
-            let btn = ui.button("Horizontal Split");
-            if btn.clicked() {
-                response.set_action(PaneAction::SplitH);
-                log::debug!("Horizontal Split button clicked");
-            }
-            height_occupied += btn.rect.height();
-            let btn = ui.button("Plot");
-            if btn.clicked() {
-                response.set_action(PaneAction::Replace(Pane::boxed(PaneKind::Plot2D(
-                    Plot2DPane::default(),
-                ))));
-            }
-            height_occupied += btn.rect.height();
-            if !self.fixed {
-                self.occupied = height_occupied;
-                ui.ctx().request_discard("test");
-                self.fixed = true;
-            }
-            if self.fixed {
-                ui.add_space(hpad);
-            }
-            ui.set_min_height(pane_rect.height());
+        let parent = vertically_centered(ui, &mut self.centering_memo, |ui| {
+            ui.vertical_centered(|ui| {
+                if ui.button("Vertical Split").clicked() {
+                    response.set_action(PaneAction::SplitV);
+                    log::debug!("Vertical Split button clicked");
+                }
+                if ui.button("Horizontal Split").clicked() {
+                    response.set_action(PaneAction::SplitH);
+                    log::debug!("Horizontal Split button clicked");
+                }
+                if ui.button("Plot").clicked() {
+                    response.set_action(PaneAction::Replace(Pane::boxed(PaneKind::Plot2D(
+                        Plot2DPane::default(),
+                    ))));
+                }
+            })
+            .response
         });
 
-        self.contains_pointer = parent.response.contains_pointer();
+        self.contains_pointer = parent.contains_pointer();
 
         if parent
-            .response
             .interact(egui::Sense::click_and_drag())
             .on_hover_cursor(egui::CursorIcon::Grab)
             .dragged()
diff --git a/src/ui/utils.rs b/src/ui/utils.rs
new file mode 100644
index 0000000000000000000000000000000000000000..14319ac4e0dd71738ffb7ef88e61f976ce031c42
--- /dev/null
+++ b/src/ui/utils.rs
@@ -0,0 +1,30 @@
+use egui::{Response, Ui};
+
+#[derive(Debug, Default, Clone)]
+pub struct SizingMemo {
+    occupied_height: f32,
+    sizing_pass_done: bool,
+}
+
+pub fn vertically_centered(
+    ui: &mut Ui,
+    memo: &mut SizingMemo,
+    add_contents: impl FnOnce(&mut Ui) -> Response,
+) -> egui::Response {
+    if !memo.sizing_pass_done {
+        let r = add_contents(ui);
+        memo.occupied_height = r.rect.height();
+        memo.sizing_pass_done = true;
+        ui.ctx()
+            .request_discard("horizontally_centered requires a sizing pass");
+        r
+    } else {
+        let spacing = (ui.available_height() - memo.occupied_height) / 2.0;
+        ui.vertical_centered(|ui| {
+            ui.add_space(spacing);
+            add_contents(ui);
+            ui.add_space(spacing);
+        })
+        .response
+    }
+}