mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
Merge pull request #2953 from nrq6993-a11y/stream/vc-feat3-1753-workspace-dock-width
#1753: saved workspaces restore dock panel width
This commit is contained in:
@@ -16,6 +16,55 @@ const ICON_COL: f32 = 38.0;
|
||||
const HEADER: f32 = 14.0;
|
||||
/// The tabbed group's width until the dock has been laid out (its default size).
|
||||
const DOCK_WIDTH: f32 = 300.0;
|
||||
pub(crate) const LEGACY_DOCK_PANEL: &str = "dock";
|
||||
pub(crate) const SHARED_DOCK_PANEL: &str = "shared-panel-dock";
|
||||
const LEGACY_DOCK_MIN: f32 = 230.0;
|
||||
const LEGACY_DOCK_MAX: f32 = 520.0;
|
||||
const SHARED_DOCK_MIN: f32 = 220.0;
|
||||
const SHARED_DOCK_MAX: f32 = 720.0;
|
||||
|
||||
pub(crate) fn clamp_legacy_dock_width(width: f32) -> f32 {
|
||||
width.clamp(LEGACY_DOCK_MIN, LEGACY_DOCK_MAX)
|
||||
}
|
||||
|
||||
pub(crate) fn clamp_shared_dock_width(width: f32) -> f32 {
|
||||
width.clamp(SHARED_DOCK_MIN, SHARED_DOCK_MAX)
|
||||
}
|
||||
|
||||
fn seed_dock_panel_width(ctx: &egui::Context, panel: &str, width: f32) {
|
||||
let screen = ctx.content_rect();
|
||||
let w = width.max(1.0);
|
||||
let outer_rect = egui::Rect::from_min_size(egui::pos2(screen.right() - w, screen.top()), egui::vec2(w, screen.height()));
|
||||
let state = egui::containers::panel::PanelState { outer_rect };
|
||||
ctx.data_mut(|data| data.insert_persisted(egui::Id::new(panel), state));
|
||||
}
|
||||
|
||||
pub(crate) fn apply_pending_dock_width(app: &mut VectorcraftApp, ctx: &egui::Context, panel: &str, clamp: fn(f32) -> f32) {
|
||||
if !app.ui.dock_width_pending {
|
||||
return;
|
||||
}
|
||||
if let Some(width) = app.ui.dock_width.filter(|w| w.is_finite() && *w > 0.0) {
|
||||
seed_dock_panel_width(ctx, panel, clamp(width));
|
||||
}
|
||||
app.ui.dock_width_pending = false;
|
||||
}
|
||||
|
||||
pub(crate) fn note_dock_width(app: &mut VectorcraftApp, ctx: &egui::Context, panel: &str) {
|
||||
if let Some(state) = egui::containers::panel::PanelState::load(ctx, egui::Id::new(panel)) {
|
||||
let width = state.size().x;
|
||||
if width.is_finite() && width > 0.0 {
|
||||
app.ui.dock_width = Some(width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn legacy_dock_default_width(ui: &crate::state::UiState) -> f32 {
|
||||
ui.dock_width.filter(|w| w.is_finite()).map(clamp_legacy_dock_width).unwrap_or(DOCK_WIDTH)
|
||||
}
|
||||
|
||||
pub(crate) fn shared_dock_default_width(ui: &crate::state::UiState) -> f32 {
|
||||
ui.dock_width.filter(|w| w.is_finite()).map(clamp_shared_dock_width).unwrap_or(310.0)
|
||||
}
|
||||
/// Top of a popped-out panel.
|
||||
const FLYOUT_TOP: f32 = 110.0;
|
||||
|
||||
@@ -81,14 +130,15 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
if crate::panel_docking::show(app, ui) {
|
||||
return;
|
||||
}
|
||||
apply_pending_dock_width(app, ui.ctx(), LEGACY_DOCK_PANEL, clamp_legacy_dock_width);
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let shown = floating::shown_tab(&app.ui);
|
||||
// Main tabbed group, unless it is collapsed to icons or all its panels float.
|
||||
let group = shown.filter(|_| !app.ui.dock_collapsed).map(|active| {
|
||||
egui::Panel::right("dock")
|
||||
egui::Panel::right(LEGACY_DOCK_PANEL)
|
||||
.resizable(true)
|
||||
.default_size(DOCK_WIDTH)
|
||||
.size_range(230.0..=520.0)
|
||||
.default_size(legacy_dock_default_width(&app.ui))
|
||||
.size_range(LEGACY_DOCK_MIN..=LEGACY_DOCK_MAX)
|
||||
.frame(egui::Frame::NONE)
|
||||
.show(ui, |ui| {
|
||||
crate::theme::card(ui, &t, egui::Margin { left: 0, right: 6, top: 6, bottom: 6 }, |ui| {
|
||||
@@ -114,6 +164,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
.response
|
||||
.rect
|
||||
});
|
||||
note_dock_width(app, ui.ctx(), LEGACY_DOCK_PANEL);
|
||||
// Collapsed icon-panel strip, left of the expanded panel group (like Illustrator's dock). A
|
||||
// collapsed group's panels head it, under the « that expands them again. Floating panels leave
|
||||
// it.
|
||||
|
||||
@@ -651,6 +651,8 @@ pub(crate) fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) -> bool {
|
||||
Some((layer, layout_rect, fill))
|
||||
})
|
||||
.collect();
|
||||
crate::dock::apply_pending_dock_width(app, ui.ctx(), crate::dock::SHARED_DOCK_PANEL, crate::dock::clamp_shared_dock_width);
|
||||
let shared_dock_default = crate::dock::shared_dock_default_width(&app.ui);
|
||||
let mut draw = |ui: &mut egui::Ui| {
|
||||
if layout.root.is_some() && crate::dock::collapse_header(ui, collapsed) {
|
||||
let _ = app.run("window.collapseDock", json!({"collapsed":!collapsed}));
|
||||
@@ -717,10 +719,15 @@ pub(crate) fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) -> bool {
|
||||
output
|
||||
};
|
||||
let output = if layout.root.is_some() {
|
||||
let panel = egui::Panel::right("shared-panel-dock").default_size(310.0).size_range(220.0..=720.0).resizable(true).frame(egui::Frame::NONE);
|
||||
let panel = egui::Panel::right(crate::dock::SHARED_DOCK_PANEL)
|
||||
.default_size(shared_dock_default)
|
||||
.size_range(220.0..=720.0)
|
||||
.resizable(true)
|
||||
.frame(egui::Frame::NONE);
|
||||
let panel = if collapsed { panel.exact_size(38.0).resizable(false) } else { panel };
|
||||
let shown = panel.show(ui, |ui| crate::theme::card(ui, &t, egui::Margin { left: 0, right: 6, top: 6, bottom: 6 }, &mut draw));
|
||||
ui.ctx().data_mut(|data| data.insert_temp(crate::floating::dock_rect_id(), shown.response.rect));
|
||||
crate::dock::note_dock_width(app, ui.ctx(), crate::dock::SHARED_DOCK_PANEL);
|
||||
shown.inner
|
||||
} else {
|
||||
draw(ui)
|
||||
|
||||
@@ -354,6 +354,12 @@ pub struct UiState {
|
||||
/// Invalidates in-flight gestures when a workspace is replaced.
|
||||
#[serde(skip)]
|
||||
pub docking_generation: u64,
|
||||
/// The main dock column's outer width in screen points (legacy or shared docking).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub dock_width: Option<f32>,
|
||||
/// Next frame should seed egui [`PanelState`] from [`Self::dock_width`] (workspace switch).
|
||||
#[serde(skip)]
|
||||
pub dock_width_pending: bool,
|
||||
/// The Tools panel floats with its top-left corner here (dragged out by its title bar); `None`:
|
||||
/// docked at the window's left edge.
|
||||
#[serde(default)]
|
||||
@@ -772,6 +778,8 @@ impl Default for UiState {
|
||||
docking_hidden: Default::default(),
|
||||
docking_icons: Default::default(),
|
||||
docking_generation: 0,
|
||||
dock_width: None,
|
||||
dock_width_pending: false,
|
||||
toolbar_pos: None,
|
||||
status_bar: true,
|
||||
dock: true,
|
||||
|
||||
@@ -41,6 +41,9 @@ pub struct Workspace {
|
||||
pub docking_icons: std::collections::BTreeSet<String>,
|
||||
/// Where the Tools panel floats (built-in workspaces: docked).
|
||||
pub toolbar_pos: Option<[f32; 2]>,
|
||||
/// Main dock column width in screen points (#1753).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub dock_width: Option<f32>,
|
||||
}
|
||||
|
||||
impl Default for Workspace {
|
||||
@@ -140,6 +143,7 @@ pub fn capture(ui: &UiState, name: &str) -> Workspace {
|
||||
docking_hidden: ui.docking_hidden.clone(),
|
||||
docking_icons: ui.docking_icons.clone(),
|
||||
toolbar_pos: ui.toolbar_pos,
|
||||
dock_width: ui.dock_width,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +169,8 @@ pub fn apply(ui: &mut UiState, w: &Workspace) {
|
||||
ui.docking_icons =
|
||||
w.docking_icons.iter().filter(|id| crate::state::ICON_PANELS.iter().any(|entry| entry.0 == id.as_str())).take(64).cloned().collect();
|
||||
ui.toolbar_pos = w.toolbar_pos;
|
||||
ui.dock_width = w.dock_width;
|
||||
ui.dock_width_pending = true;
|
||||
FloatingPanels::sanitize(&mut ui.floating_panels, &mut ui.toolbar_pos);
|
||||
ui.flyout = None;
|
||||
ui.workspace = w.name.clone();
|
||||
@@ -491,4 +497,21 @@ mod tests {
|
||||
assert!(find(&ui, n).is_some(), "{n}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_workspaces_restore_dock_width() {
|
||||
let mut ui = UiState { dock_width: Some(420.0), ..Default::default() };
|
||||
save_as(&mut ui, "Wide").unwrap();
|
||||
ui.dock_width = Some(260.0);
|
||||
save_as(&mut ui, "Narrow").unwrap();
|
||||
let wide = find(&ui, "Wide").unwrap();
|
||||
apply(&mut ui, &wide);
|
||||
assert_eq!(ui.dock_width, Some(420.0));
|
||||
assert!(ui.dock_width_pending);
|
||||
let narrow = find(&ui, "Narrow").unwrap();
|
||||
apply(&mut ui, &narrow);
|
||||
assert_eq!(ui.dock_width, Some(260.0));
|
||||
let back: UiState = serde_json::from_value(serde_json::to_value(&ui).unwrap()).unwrap();
|
||||
assert_eq!(back.custom_workspaces[1].dock_width, Some(260.0));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user