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
@@ -687,6 +687,8 @@
|
||||
"Drive Joint": "Gelenk antreiben",
|
||||
"Edit Profile Sketch": "Profile-Skizze bearbeiten",
|
||||
"Find in Browser": "Im Browser suchen",
|
||||
"Copy Error Message": "Fehlermeldung kopieren",
|
||||
"Copy Warning Message": "Warnmeldung kopieren",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Fehler melden — Screenshot prüfen und einen GitHub-Entwurf öffnen",
|
||||
"Reset Toolbar": "Symbolleiste zurücksetzen",
|
||||
"Select bodies (a face picks its body)": "Körper auswählen (eine Fläche wählt ihren Körper)",
|
||||
|
||||
@@ -744,6 +744,8 @@
|
||||
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "Files from many CAD programs have Y up, so they open lying on their back here (Z up).",
|
||||
"Filter": "Filter",
|
||||
"Find in Browser": "Find in Browser",
|
||||
"Copy Error Message": "Copy Error Message",
|
||||
"Copy Warning Message": "Copy Warning Message",
|
||||
"Flat size": "Flat size",
|
||||
"Follow camera (Look At sketches)": "Follow camera (Look At sketches)",
|
||||
"Font": "Font",
|
||||
|
||||
@@ -744,6 +744,8 @@
|
||||
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "多くのCADではY軸を上向きとしているため、Z軸を上向きとするSolidCraftでは、読み込んだモデルが横倒しの向きで表示されることがあります。",
|
||||
"Filter": "フィルター",
|
||||
"Find in Browser": "ブラウザで検索",
|
||||
"Copy Error Message": "エラーメッセージをコピー",
|
||||
"Copy Warning Message": "警告メッセージをコピー",
|
||||
"Flat size": "展開サイズ",
|
||||
"Follow camera (Look At sketches)": "カメラ追従(スケッチを正面視)",
|
||||
"Font": "フォント",
|
||||
|
||||
@@ -687,6 +687,8 @@
|
||||
"Drive Joint": "조인트 구동",
|
||||
"Edit Profile Sketch": "윤곽 스케치 편집",
|
||||
"Find in Browser": "브라우저에서 찾기",
|
||||
"Copy Error Message": "오류 메시지 복사",
|
||||
"Copy Warning Message": "경고 메시지 복사",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "버그 신고 — 스크린샷을 검토하고 GitHub 초안 열기",
|
||||
"Reset Toolbar": "도구 모음 재설정",
|
||||
"Select bodies (a face picks its body)": "바디 선택(면을 클릭하면 해당 바디 선택)",
|
||||
|
||||
@@ -736,6 +736,8 @@
|
||||
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "Файлы многих CAD-программ ориентированы Y вверх, поэтому здесь они открываются лёжа на спине (Z вверх).",
|
||||
"Filter": "Фильтр",
|
||||
"Find in Browser": "Найти в браузере",
|
||||
"Copy Error Message": "Скопировать сообщение об ошибке",
|
||||
"Copy Warning Message": "Скопировать предупреждение",
|
||||
"Flat size": "Размер развёртки",
|
||||
"Follow camera (Look At sketches)": "Следовать за камерой (эскизы «Взгляд на»)",
|
||||
"Font": "Шрифт",
|
||||
|
||||
@@ -742,6 +742,8 @@
|
||||
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "许多 CAD 程序的文件以 Y 为上轴,因此在此处打开时会平躺(Z 朝上)。",
|
||||
"Filter": "筛选",
|
||||
"Find in Browser": "在浏览器中查找",
|
||||
"Copy Error Message": "复制错误消息",
|
||||
"Copy Warning Message": "复制警告消息",
|
||||
"Flat size": "平板尺寸",
|
||||
"Follow camera (Look At sketches)": "跟随相机(注视草图)",
|
||||
"Font": "字体",
|
||||
|
||||
@@ -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