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
25 changes: 6 additions & 19 deletions crates/app/src/ui/canvas/interactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ pub(crate) fn handle_data_tool_target(
.and_then(|o| o.plot())
.is_some()
{
select_object_datasets(app, ci, id);
app.focus_object_datasets(ci, id);
}
}

Expand Down Expand Up @@ -309,7 +309,7 @@ pub(crate) fn handle_object_interactions(
if matches!(app.interaction(), Interaction::PanelLabel(_)) {
app.reset_interaction();
}
select_object_datasets(app, ci, id);
app.focus_object_datasets(ci, id);
if let Some(object) = app.doc.canvases[ci].object(id).filter(|o| !o.locked) {
let before = object.frame;
let start = page_pos.map(|p| [p.x, p.y]).unwrap_or([before.x, before.y]);
Expand Down Expand Up @@ -477,23 +477,6 @@ fn finish_marquee(app: &mut PlotxApp, ci: usize, marq: MarqueeDrag) {
);
}

fn select_object_datasets(app: &mut PlotxApp, ci: usize, id: ObjectId) {
let Some(object) = app.doc.canvases[ci].object(id) else {
return;
};
let active = object.dataset().and_then(|id| app.doc.dataset_index(id));
let datasets = object
.dataset_ids()
.into_iter()
.filter_map(|id| app.doc.dataset_index(id))
.collect::<Vec<_>>();
if !datasets.is_empty() {
app.focus_datasets(&datasets, active);
} else {
app.set_active_dataset(active);
}
}

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 Down Expand Up @@ -578,6 +561,10 @@ pub(crate) fn arrange_context_menu(app: &mut PlotxApp, ci: usize, ui: &mut Ui) {
if ui.checkbox(&mut snap, "Snap to grid & objects").clicked() {
app.set_snap_enabled(snap);
}
// 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);
Expand Down
3 changes: 3 additions & 0 deletions crates/app/src/ui/canvas/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ mod painting;
mod panel_notes;
mod peaks;
mod phase;
mod readout;
mod regions;
mod slices;
mod snap;
Expand All @@ -57,6 +58,7 @@ pub(crate) use painting::*;
pub(crate) use panel_notes::*;
pub(crate) use peaks::*;
pub(crate) use phase::*;
pub(crate) use readout::*;
pub(crate) use regions::*;
pub(crate) use slices::*;
pub(crate) use snap::*;
Expand Down Expand Up @@ -229,6 +231,7 @@ pub fn render_central(app: &mut PlotxApp, ui: &mut Ui) {
paint_marquee(app, ci, rect, &painter, chrome);
paint_panel_label_selection(app, ci, rect, &painter, chrome);
paint_object_selection(app, ci, rect, page, &painter, chrome);
paint_property_readouts(app, ci, rect, &painter, chrome, ui.visuals().dark_mode);
paint_tile_ghost(app, &painter, chrome);
paint_tile_preview(app, rect, &painter, chrome);
super::canvas_size::page_size_chrome(app, ci, page, rect, ui);
Expand Down
77 changes: 77 additions & 0 deletions crates/app/src/ui/canvas/readout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//! The in-place readout for canvas-steppable settings (§8.5 channel 3).
//!
//! "The best parameter is the one you never have to look for" only holds if the
//! plot says what its threshold currently is. This paints that value in the
//! corner of every plot the `+` / `-` gesture would act on, so the number the
//! keys move is visible where the keys are pressed.
//!
//! It is a label and nothing more: it reads a cached value through
//! [`PlotxApp::contour_base_readout`] and never resolves, measures or queues
//! anything. A plot whose estimate has not arrived says so.

use super::{object_screen_rect, plot_rect};
use crate::ui::properties;
use egui::{Align2, Color32, FontId};
use plotx_core::state::PlotxApp;

const READOUT_FONT_PT: f32 = 11.0;
const READOUT_INSET_PX: f32 = 6.0;

/// Paint the corner readout for each plot the canvas gesture currently targets.
pub(crate) fn paint_property_readouts(
app: &PlotxApp,
ci: usize,
rect: egui::Rect,
painter: &egui::Painter,
chrome: super::ChromeStyle,
dark_mode: bool,
) {
// Nothing to read out unless a steppable property applies right now — the
// same condition that enables the gesture, asked once, so the label cannot
// appear on a plot the keys would not move.
let Some((property, _)) = properties::discovery::step_target(app) else {
return;
};
let Some(canvas) = app.doc.canvases.get(ci) else {
return;
};
for object in properties::discovery::selection_objects(app) {
// The series the gesture edits, not the first one the plot happens to
// hold. A contour stacked over a heatmap is drawn second, and reading
// the first series would caption the plot with a heatmap that has no
// level while the keys moved the contour that does — or, with several
// contours, present one of them as the whole plot's threshold.
let targets = app.series_targets(ci, object);
let readouts: Vec<_> = app
.resolve_property_set(property, &targets)
.applicable_targets
.iter()
.filter_map(|address| app.contour_base_readout(&address.target))
.collect();
let Some(text) = properties::readout::aggregate_summary(&readouts) else {
continue;
};
let Some(frame) = object_screen_rect(app.session.board, canvas, object, rect) else {
continue;
};
let frame = plot_rect(frame);
if !frame.is_positive() {
continue;
}
let anchor = frame.right_top() + egui::vec2(-READOUT_INSET_PX, READOUT_INSET_PX);
let galley = painter.layout_no_wrap(
text,
FontId::proportional(READOUT_FONT_PT),
chrome.selection_stroke,
);
let text_rect = Align2::RIGHT_TOP.anchor_size(anchor, galley.size());
// A plot's own ink runs right up to its frame, so the label needs a
// backing plate to stay legible over dense contours.
painter.rect_filled(
text_rect.expand(3.0),
3.0,
Color32::from_black_alpha(if dark_mode { 150 } else { 20 }),
);
painter.galley(text_rect.min, galley, chrome.selection_stroke);
}
}
18 changes: 16 additions & 2 deletions crates/app/src/ui/command_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,21 @@ pub fn execute(
app.apply_theme(&theme);
}
}
// Channels 2 and 4 navigate; they never edit. `reveal_property` opens
// the home the presentation names and asks the panel to scroll and
// highlight — the same route a palette hit takes.
CommandId::PropertyGroup(section) => {
let now = ctx.input(|input| input.time);
if let Some(property) = super::properties::discovery::entry_property(
section,
super::properties::PRESENTATIONS,
) {
super::command_palette::reveal_property(app, property, now);
ctx.request_repaint();
}
}
// Channel 3 edits, and does so through the property planner.
CommandId::StepProperty(step) => super::properties::discovery::step_selection(app, step),
CommandId::Tool(tool) => app.toggle_tool(tool),
}
}
Expand Down Expand Up @@ -203,8 +218,7 @@ fn open_chart_type(app: &mut PlotxApp) {
let Some((ci, object)) = commands::chart_plot_target(app, dataset) else {
return;
};
app.session.active_canvas = Some(ci);
app.select_object(ci, object);
app.reveal_object(ci, object);
app.session.secondary_sidebar_visible = true;
}

Expand Down
Loading
Loading