Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 38 additions & 51 deletions crates/app/src/ui/canvas/interactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,17 @@ pub(crate) fn handle_object_interactions(
}
}

/// One catalog-backed row of the canvas context menu, rendered by the shared
/// menu helper so its label, enabled state, and checkmark can never drift
/// from the menu bar, the Ribbon, or the palette. The catalog commands act on
/// the active canvas, which is the canvas this menu belongs to.
fn command_row(app: &mut PlotxApp, ui: &mut Ui, id: crate::ui::commands::CommandId) {
if crate::ui::menus::command_row(app, ui, id, None) {
crate::ui::commands::execute_without_clipboard(id, app, ui.ctx());
ui.close();
}
}

pub(crate) fn arrange_context_menu(app: &mut PlotxApp, ci: usize, ui: &mut Ui) {
if ui.button("Copy figure").clicked() {
let ctx = ui.ctx().clone();
Expand All @@ -664,57 +675,42 @@ pub(crate) fn arrange_context_menu(app: &mut PlotxApp, ci: usize, ui: &mut Ui) {
}
frame_zoom_menu(app, ui);
ui.menu_button("Arrange into grid", |ui| {
for &(label, rows, cols) in layout::GRID_PRESETS {
if ui.button(label).clicked() {
app.arrange_active_canvas_grid(rows, cols);
ui.close();
}
for &(_, rows, cols) in layout::GRID_PRESETS {
command_row(
app,
ui,
crate::ui::commands::CommandId::ArrangeGrid(rows, cols),
);
}
});
if ui.button("Simplify inner axes").clicked() {
app.simplify_inner_axes();
ui.close();
}
command_row(app, ui, crate::ui::commands::CommandId::SimplifyInnerAxes);
ui.menu_button("Spacing basis", |ui| {
for (label, mode) in [
("Frame", layout::SpacingMode::Frame),
("Visual", layout::SpacingMode::Visual),
] {
let checked = app.doc.canvases[ci].layout.spacing_mode == mode;
if ui.selectable_label(checked, label).clicked() {
app.set_spacing_mode(mode);
ui.close();
}
for mode in [layout::SpacingMode::Frame, layout::SpacingMode::Visual] {
command_row(
app,
ui,
crate::ui::commands::CommandId::SetSpacingMode(mode),
);
}
});
ui.menu_button("Minimum spacing", |ui| {
for preset in layout::GutterPreset::ALL {
let checked =
(app.doc.canvases[ci].layout.gutter_mm - preset.millimetres()).abs() < 0.001;
if ui
.selectable_label(
checked,
format!("{} ({} mm)", preset.label(), preset.millimetres()),
)
.clicked()
{
app.set_gutter_preset(preset);
ui.close();
}
command_row(
app,
ui,
crate::ui::commands::CommandId::SetGutterPreset(preset),
);
}
});
if !app.session.ui.selection.objects().is_empty() {
ui.menu_button("Order", |ui| {
for (label, op) in [
("Bring to Front", plotx_core::actions::ZOrder::Front),
("Bring Forward", plotx_core::actions::ZOrder::Forward),
("Send Backward", plotx_core::actions::ZOrder::Backward),
("Send to Back", plotx_core::actions::ZOrder::Back),
for op in [
plotx_core::actions::ZOrder::Front,
plotx_core::actions::ZOrder::Forward,
plotx_core::actions::ZOrder::Backward,
plotx_core::actions::ZOrder::Back,
] {
if ui.button(label).clicked() {
app.z_order_selected(op);
ui.close();
}
command_row(app, ui, crate::ui::commands::CommandId::ZOrder(op));
}
});
let ids: Vec<ObjectId> = app.session.ui.selection.objects().to_vec();
Expand All @@ -732,23 +728,14 @@ pub(crate) fn arrange_context_menu(app: &mut PlotxApp, ci: usize, ui: &mut Ui) {
}
}
ui.separator();
let mut show_grid = app.doc.canvases[ci].layout.show_grid;
if ui.checkbox(&mut show_grid, "Show layout grid").clicked() {
app.set_show_grid(ci, show_grid);
}
let mut snap = app.settings.general.snap_enabled;
if ui.checkbox(&mut snap, "Snap objects & frames").clicked() {
app.set_snap_enabled(snap);
}
command_row(app, ui, crate::ui::commands::CommandId::ToggleGrid);
command_row(app, ui, crate::ui::commands::CommandId::ToggleSnap);
// Channel 4: whatever the selection draws, its settings are one click from
// here. Navigation only — the entries jump to the panel section that owns
// the controls, and are derived from the catalog rather than listed again.
crate::ui::properties::discovery::context_menu(app, ui);
ui.separator();
if ui.button("Canvas settings…").clicked() {
app.session.ui.canvas_settings = Some(ci);
ui.close();
}
command_row(app, ui, crate::ui::commands::CommandId::CanvasSettings);
}

pub(crate) fn finish_object_drag(app: &mut PlotxApp, ci: usize, drag: ObjectDrag) {
Expand Down
1 change: 1 addition & 0 deletions crates/app/src/ui/command_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ fn execute_inner(
}
// Channel 3 edits, and does so through the property planner.
CommandId::StepProperty(step) => super::properties::discovery::step_selection(app, step),
CommandId::XpsWorkbench(tab) => super::tools::open_xps_workbench(app, tab),
CommandId::CycleCursor => cycle_cursor(app),
CommandId::Tool(Tool::Symmetry) => {
reveal_tool_group(app, Tool::Symmetry, ToolGroup::Nmr2dExperiment);
Expand Down
7 changes: 7 additions & 0 deletions crates/app/src/ui/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ pub enum CommandId {
/// Move the canvas-steppable property one rung (§8.5 channel 3). The
/// property is derived from the catalog, so the binding does not name one.
StepProperty(PropertyStep),
/// Open the XPS workbench in the right sidebar on one of its pages.
/// Navigation only: the workbench owns the controls.
XpsWorkbench(plotx_core::state::XpsWorkbenchTab),
CycleCursor,
Tool(Tool),
}
Expand Down Expand Up @@ -497,6 +500,10 @@ pub fn describe(app: &PlotxApp, id: CommandId) -> CommandDescriptor {
)
})
}
CommandId::XpsWorkbench(_) => requires(
dataset().is_some_and(|dataset| dataset.as_xps().is_some()),
"Open an XPS dataset before using the XPS workbench.",
),
CommandId::Statistics => requires(
is_table(),
"Select a data table before calculating statistics.",
Expand Down
28 changes: 28 additions & 0 deletions crates/app/src/ui/commands/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impl CommandId {
Self::PropertyGroup(section) => format!("properties.group.{section}"),
Self::StepProperty(step) => format!("properties.step.{}", step.as_str()),
Self::Tool(tool) => format!("tool.{}", tool_slug(tool)),
Self::XpsWorkbench(tab) => format!("xps.workbench.{}", xps_workbench_slug(tab)),
Self::SetPanelLayout(layout) => {
format!("panel.layout.{}", panel_layout_slug(layout))
}
Expand Down Expand Up @@ -312,6 +313,23 @@ pub(super) fn command_identity(
PropertyStep::Lower => (format!("Lower {setting}"), Some(icon::MINUS), None),
}
}
CommandId::XpsWorkbench(tab) => {
use plotx_core::state::XpsWorkbenchTab;
let (label, glyph) = match tab {
XpsWorkbenchTab::Acquisition => ("XPS Acquisition", icon::INFO),
XpsWorkbenchTab::Background => ("XPS Background", icon::CHART_LINE_DOWN),
XpsWorkbenchTab::Components => ("XPS Components", icon::PUZZLE_PIECE),
XpsWorkbenchTab::Diagnostics => ("XPS Diagnostics", icon::GAUGE),
};
(
label.to_owned(),
Some(glyph),
Some(
app.session.ui.xps_workbench_tab == tab
&& app.session.secondary_sidebar_visible,
),
)
}
CommandId::CycleCursor => plain("Next Cursor", Some(icon::CROSSHAIR)),
CommandId::Tool(tool) => (
format!("Tool: {}", tool.label()),
Expand Down Expand Up @@ -581,6 +599,16 @@ fn zorder_slug(mode: ZOrder) -> &'static str {
ZOrder::Back => "back",
}
}
fn xps_workbench_slug(tab: plotx_core::state::XpsWorkbenchTab) -> &'static str {
use plotx_core::state::XpsWorkbenchTab;
match tab {
XpsWorkbenchTab::Acquisition => "acquisition",
XpsWorkbenchTab::Background => "background",
XpsWorkbenchTab::Components => "components",
XpsWorkbenchTab::Diagnostics => "diagnostics",
}
}

fn tool_slug(tool: Tool) -> &'static str {
match tool {
Tool::Select => "select",
Expand Down
3 changes: 2 additions & 1 deletion crates/app/src/ui/commands/ribbon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ pub(super) fn ribbon_placement(id: CommandId) -> Option<RibbonPlacement> {
Applicability::ToolGroup(ToolGroup::Peaks),
),
CommandId::Tool(Tool::Symmetry) => (Analyze, "Review", 1, Homonuclear2dOnly),
CommandId::AlignTraces => (Analyze, "Align", 1, LineAlignmentOnly),
CommandId::AlignTraces => (Analyze, "Overlay", 1, LineAlignmentOnly),
CommandId::XpsWorkbench(_) => (Analyze, "XPS", 0, Applicability::ToolGroup(ToolGroup::Xps)),
CommandId::Tool(Tool::ManualPhase) => (Process, "Correct", 0, Always),
CommandId::SpectrumArithmetic | CommandId::AlignSpectra | CommandId::Craft => {
(Process, "Transform", 1, Always)
Expand Down
9 changes: 9 additions & 0 deletions crates/app/src/ui/commands/roster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,15 @@ pub(super) fn command_ids(recent_files: usize) -> Vec<CommandId> {
.map(|group| CommandId::PropertyGroup(group.section)),
);
ids.extend([PropertyStep::Lower, PropertyStep::Raise].map(CommandId::StepProperty));
ids.extend(
[
plotx_core::state::XpsWorkbenchTab::Acquisition,
plotx_core::state::XpsWorkbenchTab::Background,
plotx_core::state::XpsWorkbenchTab::Components,
plotx_core::state::XpsWorkbenchTab::Diagnostics,
]
.map(CommandId::XpsWorkbench),
);
ids.push(CommandId::CycleCursor);
ids.extend(tool_commands().into_iter().map(CommandId::Tool));
ids
Expand Down
2 changes: 1 addition & 1 deletion crates/app/src/ui/commands_alignment_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ fn trace_alignment_has_one_contextual_ribbon_command() {
command.ribbon,
Some(RibbonPlacement {
tab: WorkflowTab::Analyze,
group: "Align",
group: "Overlay",
priority: 1,
applicability: Applicability::LineAlignmentOnly,
})
Expand Down
22 changes: 18 additions & 4 deletions crates/app/src/ui/menus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,23 @@ fn command_item_labeled(
id: CommandId,
label_override: Option<&str>,
) {
if command_row(app, ui, id, label_override) {
commands::execute(id, app, clipboard, ui.ctx());
ui.close();
}
}

/// Renders one catalog command as a menu row and reports a click. Label,
/// enabled state, checkmark, shortcut, and the unblock reason all come from
/// `describe`, so every menu surface (the in-window bar, the canvas context
/// menu) shows the same state as the Ribbon and the palette. The caller
/// executes, because the surfaces differ in whether they hold the clipboard.
pub(crate) fn command_row(
app: &PlotxApp,
ui: &mut Ui,
id: CommandId,
label_override: Option<&str>,
) -> bool {
let command = commands::describe(app, id);
let label = label_override.unwrap_or(command.label.as_str());
let mut button = egui::Button::new(label).selected(command.checked == Some(true));
Expand All @@ -265,10 +282,7 @@ fn command_item_labeled(
{
response.on_disabled_hover_text(reason);
}
if clicked {
commands::execute(id, app, clipboard, ui.ctx());
ui.close();
}
clicked
}

pub(crate) fn about_window(app: &mut PlotxApp, ctx: &egui::Context) {
Expand Down
3 changes: 2 additions & 1 deletion crates/app/src/ui/ribbon/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,12 @@ pub(super) fn group_order(tab: WorkflowTab, group: &str) -> u8 {
WorkflowTab::Process => &["Processing", "Correct", "Transform", "Recipes"],
WorkflowTab::Analyze => &[
"Range",
"XPS",
"Extract",
"Regions",
"Peaks",
"Review",
"Align",
"Overlay",
"Peak Fit",
"Curve Fit",
"Statistics",
Expand Down
1 change: 1 addition & 0 deletions crates/app/src/ui/tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use slice::slice_group;
use symmetry::symmetry_group;

pub(super) use line_fit::line_fit_shape_id;
pub(super) use xps::open_workbench as open_xps_workbench;

#[derive(Clone, Copy, Default)]
struct DeferredReferenceValue {
Expand Down
8 changes: 8 additions & 0 deletions crates/app/src/ui/tools/xps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ struct WindowDraft {
high_ev: f64,
}

/// Ensure-on navigation from the catalog: reveal the workbench on `tab`,
/// never toggle it away.
pub(crate) fn open_workbench(app: &mut PlotxApp, tab: XpsWorkbenchTab) {
app.session.ui.xps_workbench_tab = tab;
app.session.secondary_sidebar_visible = true;
app.session.ui.requested_tool_group = Some(plotx_core::state::ToolGroup::Xps);
}

pub(super) fn xps_group(app: &mut PlotxApp, dataset_index: usize, ui: &mut Ui) -> bool {
let Some(xps) = app
.doc
Expand Down
1 change: 1 addition & 0 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export default defineConfig({
{ slug: 'reference/shortcuts' },
{ slug: 'reference/command-palette' },
{ slug: 'reference/ui-overview' },
{ slug: 'reference/ribbon' },
{ slug: 'reference/preferences' },
{ slug: 'reference/file-formats' },
{ slug: 'reference/updates' },
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/getting-started/first-figure.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ and click the canvas. See [Annotations](/guides/annotations/).

## 5. Export

Open the export menu in the toolbar and choose a vector format — SVG for
Use the **Figure** tab's **Output** group (or **File → Export**) and choose a vector format — SVG for
further editing, PDF for manuscripts — or a raster preset such as
*Single column · 89 mm · 600 dpi · TIFF*. The export precheck warns if font
sizes or line widths violate the chosen preset before anything is written.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/guides/exporting.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Export publication-quality graphics and the numbers behind them.

## Export a figure

Export via the toolbar's export menu — the scope is the current page, all
Export from the **Figure** tab's **Output** group or the **File** menu — the scope is the current page, all
pages, or a page range.

| Format | Use |
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/guides/importing-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ no conversion step is needed.

## Opening files

Drag a file onto the PlotX window, or use the toolbar's open menu:
Drag a file onto the PlotX window, or use the **Data** tab's **Import** group:
*Open File…*, *Open Folder…* (for acquisition directories such as Bruker
TopSpin, Varian/Agilent VnmrJ, and Waters MassLynx RAW), *Open Project…*, or
*Import Table…*.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/guides/layout-and-export.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: Arranging figures on the infinite board and sizing pages for journa

Plots live on an infinite board organized into pages. Dragging a frame snaps
it to the page grid, margins, and the edges of neighboring frames; snapping
can be toggled off from the toolbar. The arrange menu in the toolbar offers
can be toggled off on the **Arrange** tab. The **Arrange** tab also offers
alignment (with two or more frames selected), horizontal / vertical
distribution (three or more), z-ordering, and a *Tidy up frames* command.

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/guides/present-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ meetings and quick reviews without exporting slides.

## Start presenting

Choose **Present Full Screen** from the toolbar or the command palette
Choose **Present Full Screen** from the **View** tab or the command palette
(<kbd>Ctrl</kbd>+<kbd>K</kbd>). Presenting starts from the page you are
currently on. If nothing is open yet, PlotX tells you there is nothing to
present.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/reference/preferences.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ everything except your recent-files list.

- **Object snapping** — snap plots and shapes to page and object guides, and
snap whole pages and table sheets to nearby frame edges and the standard gap
between frames. You can also toggle this from the toolbar. Hold `Alt` to
between frames. You can also toggle this from the **Arrange** tab. Hold `Alt` to
bypass snapping for one drag.
- **Equal scale for homonuclear 2D imports** — when both axes are frequency
axes of the same nucleus, start an imported spectrum with equal F1/F2 scale
Expand Down
Loading
Loading