Copy a feature's error or warning message from the history menu (#1226)
The feature menu in the timeline and Feature History gets a Copy Message item for a feature's error or warning, translated, with a test.
This commit is contained in:
1 parent
b6e87f0c44
commit
0ddd660089
8 files changed
+75
-1
No files matched your search
@@ -118,6 +118,35 @@ pub(crate) fn clipboard_buttons(app: &mut SolveApp, ui: &mut egui::Ui, id: u64)
|
||||
}
|
||||
}
|
||||
|
||||
/// What a feature's error or warning mark says, as its tooltip shows it (name and type, then
|
||||
/// the error and the warning), for the clipboard. `None` when it has neither.
|
||||
pub(crate) fn feature_message(app: &SolveApp, id: u64) -> Option<String> {
|
||||
let result = app.session.model.result(id)?;
|
||||
if result.error.is_none() && result.warning.is_none() {
|
||||
return None;
|
||||
}
|
||||
let feature = app.session.doc.features.iter().find(|f| f.id == id)?;
|
||||
let mut text = format!("{} ({})", feature.name, feature.kind.type_name());
|
||||
if let Some(error) = &result.error {
|
||||
text.push_str(&format!("\nError: {error}"));
|
||||
}
|
||||
if let Some(warning) = &result.warning {
|
||||
text.push_str(&format!("\nWarning: {warning}"));
|
||||
}
|
||||
Some(text)
|
||||
}
|
||||
|
||||
/// Copy Error Message (or Copy Warning Message) in the menu of a feature that has one.
|
||||
pub(crate) fn message_button(app: &SolveApp, ui: &mut egui::Ui, id: u64) {
|
||||
let Some(text) = feature_message(app, id) else { return };
|
||||
let failed = app.session.model.result(id).is_some_and(|r| r.error.is_some());
|
||||
let label = if failed { "Copy Error Message" } else { "Copy Warning Message" };
|
||||
if ui.button(crate::i18n::tr_label(label)).clicked() {
|
||||
ui.ctx().copy_text(text);
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
|
||||
/// Ctrl+click on a feature: add it to the selection, or take it out.
|
||||
fn toggle_feature(app: &mut SolveApp, id: u64) {
|
||||
let selected = selected_feature_ids(app);
|
||||
@@ -323,6 +352,7 @@ pub fn timeline(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
}
|
||||
ui.separator();
|
||||
clipboard_buttons(app, ui, *id);
|
||||
message_button(app, ui, *id);
|
||||
ui.separator();
|
||||
if ui.button("Delete").clicked() {
|
||||
delete_feature(app, *id);
|
||||
@@ -450,6 +480,37 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// A feature with a warning mark offers Copy Warning Message, which copies what its tooltip
|
||||
/// says; a feature with nothing to report doesn't offer it. In both timelines.
|
||||
#[test]
|
||||
fn the_feature_menu_copies_a_warning_message() {
|
||||
for left in [false, true] {
|
||||
let mut h = two_boxes(left);
|
||||
// Too large for the box: reduced to what fits, which the feature's warning says.
|
||||
h.app.run("solid.fillet", json!({"edges": [[5, 0, 10]], "radius": 20, "fit": "reduce", "body": "One"})).unwrap();
|
||||
h.frames(4);
|
||||
let id = h.app.session.doc.features.last().map(|f| f.id).unwrap();
|
||||
let name = h.app.session.doc.features.last().map(|f| f.name.clone()).unwrap();
|
||||
let warning = h.app.session.model.result(id).and_then(|r| r.warning.clone()).unwrap();
|
||||
h.step(&json!({"click": {"handle": "timeline:First"}, "button": "right"})).unwrap();
|
||||
assert!(h.widget("Copy Warning Message").is_none(), "left: {left}");
|
||||
h.step(&json!({"key": "Escape"})).unwrap();
|
||||
h.step(&json!({"click": {"handle": format!("timeline:{name}")}, "button": "right"})).unwrap();
|
||||
let [x, y] = h.widget("Copy Warning Message").unwrap();
|
||||
let (request, _reply) = crate::control::ControlRequest::new("ui.click", json!({"x": x, "y": y}));
|
||||
crate::control::handle(&mut h.app, &h.ctx, &request);
|
||||
let mut copied = Vec::new();
|
||||
for _ in 0..8 {
|
||||
h.frame();
|
||||
copied.extend(h.output.commands.iter().filter_map(|c| match c {
|
||||
egui::OutputCommand::CopyText(text) => Some(text.clone()),
|
||||
_ => None,
|
||||
}));
|
||||
}
|
||||
assert!(copied.iter().any(|t| t.starts_with(&name) && t.contains(&format!("Warning: {warning}"))), "left: {left}: {copied:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy on a feature outside the selection copies that feature alone.
|
||||
#[test]
|
||||
fn copy_on_an_unselected_feature_copies_only_it() {
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde_json::json;
|
||||
use solidcraft_engine::doc::FeatureKind;
|
||||
|
||||
use crate::theme::Tokens;
|
||||
use crate::timeline::{clipboard_buttons, delete_feature, delete_features, icon_of, selected_feature_ids, transport_glyph};
|
||||
use crate::timeline::{clipboard_buttons, delete_feature, delete_features, icon_of, message_button, selected_feature_ids, transport_glyph};
|
||||
use crate::{SolveApp, icons};
|
||||
|
||||
const FOLDER_INDENT: f32 = 14.0;
|
||||
@@ -791,6 +791,7 @@ fn vertical_features(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
}
|
||||
ui.separator();
|
||||
clipboard_buttons(app, ui, *id);
|
||||
message_button(app, ui, *id);
|
||||
ui.separator();
|
||||
selection_actions(app, ui, &groups);
|
||||
ui.separator();
|
||||
|
||||
Reference in new issue
Block a user