mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-27 19:56:51 -06:00
Integration: panel menus stay reachable in the docking layout
This commit is contained in:
@@ -106,13 +106,9 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
|
||||
}
|
||||
icons::paint(ui.painter(), menu_r, "menu", t.icon);
|
||||
let tab = app.ui.dock_tab.clone();
|
||||
egui::Popup::menu(&menu).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| {
|
||||
if tab == "pages" {
|
||||
panels::pages::panel_menu(app, ui);
|
||||
ui.separator();
|
||||
}
|
||||
crate::panel_docking::panel_menu(app, ui, &tab);
|
||||
});
|
||||
egui::Popup::menu(&menu)
|
||||
.close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside)
|
||||
.show(|ui| crate::panel_docking::three_line_menu(app, ui, &tab));
|
||||
egui::Frame::NONE.inner_margin(egui::Margin { left: 10, right: 10, top: 4, bottom: 6 }).show(ui, |ui| {
|
||||
// The Pages panel scrolls its parts itself and keeps its footer at the bottom.
|
||||
if app.ui.dock_tab == "pages" {
|
||||
|
||||
@@ -499,7 +499,8 @@ pub(crate) fn show(app: &mut DesignApp, ui: &mut egui::Ui) -> bool {
|
||||
)
|
||||
.to_owned()
|
||||
},
|
||||
|_| craft_ui::docking::Permissions::default(),
|
||||
// Close comes from the panel's own tab commands, so the surface doesn't add another.
|
||||
|_| craft_ui::docking::Permissions { closable: false, ..Default::default() },
|
||||
|_| craft_ui::docking::PanelLimits { min: egui::vec2(220.0, 80.0), ..Default::default() },
|
||||
&mut PanelContent(app),
|
||||
)
|
||||
@@ -598,10 +599,33 @@ pub(crate) fn legacy_tab(app: &mut DesignApp, ui: &mut egui::Ui, panel: &str, re
|
||||
}
|
||||
}
|
||||
|
||||
/// The menu behind a panel's three-line button, in either dock: the panel's own commands, then
|
||||
/// its docking commands.
|
||||
pub(crate) fn three_line_menu(app: &mut DesignApp, ui: &mut egui::Ui, panel: &str) {
|
||||
if panel == "pages" {
|
||||
crate::panels::pages::panel_menu(app, ui);
|
||||
ui.separator();
|
||||
}
|
||||
panel_menu(app, ui, panel);
|
||||
}
|
||||
|
||||
/// The three-line button at the right of a tab strip, for the panel in front.
|
||||
fn three_line_button(app: &mut DesignApp, ui: &mut egui::Ui, panel: &str) {
|
||||
let t = crate::theme::Tokens::get(ui.ctx());
|
||||
let area = ui.max_rect();
|
||||
let r = egui::Rect::from_center_size(area.center(), egui::vec2(18.0, 18.0));
|
||||
let menu = ui.interact(r, ui.id().with("panel-menu"), egui::Sense::click());
|
||||
if menu.hovered() {
|
||||
ui.painter().rect_filled(r, 3.0, t.hover);
|
||||
}
|
||||
crate::icons::paint(ui.painter(), r, "menu", t.icon);
|
||||
egui::Popup::menu(&menu).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside).show(|ui| three_line_menu(app, ui, panel));
|
||||
}
|
||||
|
||||
/// The docking commands of a panel: its tab's context menu and the end of its panel menu.
|
||||
pub(crate) fn panel_menu(app: &mut DesignApp, ui: &mut egui::Ui, panel: &str) {
|
||||
let Some(panel) = normalize(panel) else { return };
|
||||
tab_commands(app, ui, panel, true);
|
||||
tab_commands(app, ui, panel);
|
||||
ui.separator();
|
||||
let language = app.ui.language.clone();
|
||||
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&language, "Float Panel"))).clicked() {
|
||||
@@ -643,12 +667,21 @@ impl craft_ui::docking::DockContent<String> for PanelContent<'_> {
|
||||
});
|
||||
}
|
||||
fn panel_menu(&mut self, ui: &mut egui::Ui, id: &String) {
|
||||
// The docking surface adds Float, Group with and Close after these.
|
||||
// The docking surface adds Float Panel and Group with after these, so a tab's menu lists
|
||||
// what the classic dock's does.
|
||||
if let Some(panel) = normalize(id) {
|
||||
tab_commands(self.0, ui, panel, false);
|
||||
tab_commands(self.0, ui, panel);
|
||||
ui.separator();
|
||||
}
|
||||
}
|
||||
fn group_header_width(&self, _: &[String], _: &String, _: bool) -> f32 {
|
||||
24.0
|
||||
}
|
||||
fn group_header(&mut self, ui: &mut egui::Ui, _: &[String], active: &String, _: bool) {
|
||||
if let Some(panel) = normalize(active) {
|
||||
three_line_button(self.0, ui, panel);
|
||||
}
|
||||
}
|
||||
fn label(&mut self, ui: &egui::Ui, _: &String, logical: &str, font: &egui::FontId, color: egui::Color32) -> egui::WidgetText {
|
||||
egui::WidgetText::Galley(crate::rtl::plain(ui.ctx(), logical, font.clone(), color))
|
||||
}
|
||||
@@ -670,9 +703,8 @@ fn close_group(app: &mut DesignApp, params: &Value) -> Result<Value, String> {
|
||||
}
|
||||
|
||||
/// InDesign's commands on a panel tab (and at the end of its panel menu): Close, Close Tab Group,
|
||||
/// Minimize (an open accordion entry) and Collapse to Icons. `close` is false where the docking
|
||||
/// surface adds its own Close.
|
||||
fn tab_commands(app: &mut DesignApp, ui: &mut egui::Ui, panel: &'static str, close: bool) {
|
||||
/// Minimize (an open accordion entry) and Collapse to Icons.
|
||||
fn tab_commands(app: &mut DesignApp, ui: &mut egui::Ui, panel: &'static str) {
|
||||
let language = app.ui.language.clone();
|
||||
let tr = |s: &str| crate::i18n::tr(&language, s).to_owned();
|
||||
let run = |app: &mut DesignApp, ui: &mut egui::Ui, id: &str, p: Value| {
|
||||
@@ -681,7 +713,7 @@ fn tab_commands(app: &mut DesignApp, ui: &mut egui::Ui, panel: &'static str, clo
|
||||
}
|
||||
ui.close();
|
||||
};
|
||||
if close && ui.button(crate::rtl::widget(ui, tr("Close"))).clicked() {
|
||||
if ui.button(crate::rtl::widget(ui, tr("Close"))).clicked() {
|
||||
run(app, ui, "window.panel.close", json!({"panel": panel}));
|
||||
}
|
||||
if ui.button(crate::rtl::widget(ui, tr("Close Tab Group"))).clicked() {
|
||||
|
||||
@@ -746,3 +746,44 @@ fn paragraph_styles_closed_from_the_dock_reopen_there() {
|
||||
assert!(layout.contains(&"paragraphStyles".to_string()));
|
||||
assert!(layout.floating.iter().all(|g| !g.panels.iter().any(|p| p == "paragraphStyles")), "{:?}", layout.floating);
|
||||
}
|
||||
|
||||
fn click(h: &mut Harness<'static, DesignApp>, at: Pos2, button: PointerButton) {
|
||||
h.event(Event::PointerMoved(at));
|
||||
h.run_steps(1);
|
||||
for pressed in [true, false] {
|
||||
h.event(Event::PointerButton { pos: at, button, pressed, modifiers: Modifiers::NONE });
|
||||
h.run_steps(1);
|
||||
}
|
||||
h.run_steps(2);
|
||||
}
|
||||
|
||||
/// Window › Paragraph Styles switches to the docking layout. After Window › Pages the Pages panel
|
||||
/// keeps its three-line panel menu, and its tab menu lists what the classic dock's does.
|
||||
#[test]
|
||||
fn pages_keeps_its_panel_menu_in_the_docking_layout() {
|
||||
let mut app = app();
|
||||
app.run("file.new", json!({})).unwrap();
|
||||
app.run("window.panel", json!({"panel":"paragraphStyles"})).unwrap();
|
||||
app.run("window.panel", json!({"panel":"pages"})).unwrap();
|
||||
assert!(app.ui.docking.is_some());
|
||||
let mut h = harness(app, vec2(1280.0, 800.0));
|
||||
let tab = h.query_all_by_label("Pages").find(|node| node.rect().height() <= 45.0).unwrap().rect();
|
||||
let dock = egui::containers::panel::PanelState::load(&h.ctx, egui::Id::new("shared-panel-dock")).unwrap().outer_rect;
|
||||
click(&mut h, egui::pos2(dock.right() - 12.0, tab.center().y), PointerButton::Primary);
|
||||
assert!(egui::Popup::is_any_open(&h.ctx), "the three-line button must open a menu");
|
||||
for item in ["Numbering & Section Options…", "Close Tab Group", "Float Panel"] {
|
||||
assert!(h.query_by_label(item).is_some(), "{item}");
|
||||
}
|
||||
h.get_by_label("Panel Options…").click();
|
||||
h.run_steps(3);
|
||||
assert!(h.state().ui.dialog.as_ref().is_some_and(|d| d.id == "pagesPanelOptions"));
|
||||
h.state_mut().ui.dialog = None;
|
||||
h.run_steps(2);
|
||||
|
||||
click(&mut h, tab.center(), PointerButton::Secondary);
|
||||
for item in ["Close", "Close Tab Group", "Minimize", "Collapse to Icons", "Float Panel", "Group with"] {
|
||||
assert!(h.query_all_by_label(item).next().is_some(), "{item}");
|
||||
}
|
||||
assert!(h.query_by_label("Close Panel").is_none());
|
||||
assert!(h.query_by_label("Panel Options…").is_none(), "the panel's own commands stay in its panel menu");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user