The screenshot controls, the submission steps and the Open GitHub draft button stay outside the scrolling review; the clipboard fallback is explained before a long draft opens; text fields leave a pixel of room so the window no longer widens while idle at 140% scale. Instructions in en/de/ja/ko/zh.
This commit is contained in:
1 parent
a6fbc63746
commit
575c037f39
8 files changed
+129
-37
No files matched your search
@@ -507,45 +507,53 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
let mut retake = false;
|
||||
let mut close = false;
|
||||
let screen = ctx.content_rect();
|
||||
let max_width = (screen.width() - 48.0).clamp(100.0, crate::frame::WINDOW_WIDE_MAX_W);
|
||||
crate::frame::window(ctx, crate::i18n::tr_label("Create Issue — review draft"), crate::frame::Width::Wide)
|
||||
.id(egui::Id::new("sc_issue_report"))
|
||||
.open(&mut open)
|
||||
.resizable(true)
|
||||
.default_width((screen.width() - 32.0).clamp(100.0, 640.0))
|
||||
.max_width((screen.width() - 24.0).max(100.0))
|
||||
.max_width(max_width)
|
||||
.max_height((screen.height() - 32.0).max(100.0))
|
||||
.vscroll(true)
|
||||
.show(ctx, |ui| {
|
||||
ui.label(crate::i18n::tr_label("Review the report and screenshot before opening GitHub."));
|
||||
ui.add(
|
||||
egui::TextEdit::singleline(&mut draft.title)
|
||||
.id(egui::Id::new("issue_report_title"))
|
||||
.hint_text(crate::i18n::tr_label("Issue title"))
|
||||
.char_limit(160)
|
||||
.desired_width(f32::INFINITY),
|
||||
);
|
||||
egui::ScrollArea::vertical().id_salt("issue-report-body").max_height((screen.height() * 0.35).clamp(72.0, 240.0)).show(ui, |ui| {
|
||||
// Only the editable report and evidence scroll. Keep the submission guide
|
||||
// and actions visible even when a screenshot or long report needs more room.
|
||||
egui::ScrollArea::vertical().id_salt("issue-report-review").max_height((screen.height() - 260.0).clamp(80.0, 520.0)).show(ui, |ui| {
|
||||
ui.label(crate::i18n::tr_label("Review the report and screenshot before opening GitHub."));
|
||||
// TextEdit rounds its allocation to physical pixels. Leaving one pixel of
|
||||
// room prevents that rounding from increasing the window width every frame
|
||||
// at fractional display/UI scales (for example 140%).
|
||||
let editor_width = |ui: &egui::Ui| (ui.available_width() - ui.ctx().pixels_per_point().recip()).max(24.0);
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut draft.body)
|
||||
.id(egui::Id::new("issue_report_body"))
|
||||
.char_limit(MAX_BODY)
|
||||
.desired_rows(5)
|
||||
.desired_width(f32::INFINITY),
|
||||
egui::TextEdit::singleline(&mut draft.title)
|
||||
.id(egui::Id::new("issue_report_title"))
|
||||
.hint_text(crate::i18n::tr_label("Issue title"))
|
||||
.char_limit(160)
|
||||
.desired_width(editor_width(ui)),
|
||||
);
|
||||
egui::ScrollArea::vertical().id_salt("issue-report-body").max_height((screen.height() * 0.35).clamp(72.0, 240.0)).show(ui, |ui| {
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut draft.body)
|
||||
.id(egui::Id::new("issue_report_body"))
|
||||
.char_limit(MAX_BODY)
|
||||
.desired_rows(5)
|
||||
.desired_width(editor_width(ui)),
|
||||
);
|
||||
});
|
||||
truncate(&mut draft.title, 160);
|
||||
truncate(&mut draft.body, MAX_BODY);
|
||||
ui.label(RichText::new(crate::i18n::tr_label("Remove private design details or paths before sharing.")).small());
|
||||
ui.add(egui::Label::new(&draft.note).wrap());
|
||||
if let Some(image) = draft.image.as_ref() {
|
||||
let texture =
|
||||
draft.texture.get_or_insert_with(|| ctx.load_texture("issue-report-frame", image.clone(), egui::TextureOptions::LINEAR));
|
||||
ui.add(
|
||||
egui::Image::new(&*texture)
|
||||
.max_size(vec2(ui.available_width(), (screen.height() * 0.25).clamp(60.0, 220.0)))
|
||||
.maintain_aspect_ratio(true),
|
||||
);
|
||||
}
|
||||
});
|
||||
truncate(&mut draft.title, 160);
|
||||
truncate(&mut draft.body, MAX_BODY);
|
||||
ui.label(RichText::new(crate::i18n::tr_label("Remove private design details or paths before sharing.")).small());
|
||||
ui.label(&draft.note);
|
||||
if let Some(image) = draft.image.as_ref() {
|
||||
let texture =
|
||||
draft.texture.get_or_insert_with(|| ctx.load_texture("issue-report-frame", image.clone(), egui::TextureOptions::LINEAR));
|
||||
ui.add(
|
||||
egui::Image::new(&*texture)
|
||||
.max_size(vec2(ui.available_width(), (screen.height() * 0.25).clamp(60.0, 220.0)))
|
||||
.maintain_aspect_ratio(true),
|
||||
);
|
||||
}
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
retake = ui.button(crate::i18n::tr_label("Retake screenshot")).clicked();
|
||||
if ui.add_enabled(draft.image.is_some(), egui::Button::new(crate::i18n::tr_label("Copy screenshot"))).clicked()
|
||||
@@ -575,24 +583,44 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
draft.note = "Screenshot removed from this draft.".into();
|
||||
}
|
||||
});
|
||||
ui.separator();
|
||||
ui.label(RichText::new(crate::i18n::tr_label("Send your report on GitHub")).strong());
|
||||
let (url, copy_needed) = draft_url(&draft.title, &draft.body);
|
||||
ui.add(
|
||||
egui::Label::new(crate::i18n::tr_label(if copy_needed {
|
||||
"1. Open GitHub draft below, then paste the report copied to your clipboard."
|
||||
} else {
|
||||
"1. Open GitHub draft below. Your title and report will be filled in."
|
||||
}))
|
||||
.wrap(),
|
||||
);
|
||||
ui.add(egui::Label::new(crate::i18n::tr_label("2. Attach your screenshot: copy and paste it, or save and upload the PNG.")).wrap());
|
||||
ui.add(egui::Label::new(crate::i18n::tr_label("3. Click Submit new issue on GitHub to send your report.")).wrap());
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
if ui.button(crate::i18n::tr_label("Copy report")).clicked() {
|
||||
ctx.copy_text(draft.body.clone());
|
||||
draft.note = "Report copied. Paste it into the GitHub draft if needed.".into();
|
||||
}
|
||||
let (url, copy_needed) = draft_url(&draft.title, &draft.body);
|
||||
if ui.button(crate::i18n::tr_label("Open GitHub draft")).clicked() {
|
||||
let t = crate::theme::Tokens::get();
|
||||
if ui
|
||||
.add(
|
||||
egui::Button::new(RichText::new(crate::i18n::tr_label("Open GitHub draft")).strong().color(t.text))
|
||||
.fill(t.accent_soft)
|
||||
.stroke(egui::Stroke::new(1.0, t.accent)),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
if copy_needed {
|
||||
ctx.copy_text(draft.body.clone());
|
||||
}
|
||||
ctx.open_url(egui::OpenUrl::new_tab(url));
|
||||
draft.note = if copy_needed {
|
||||
"Report copied. Paste into the GitHub draft, then attach the screenshot."
|
||||
"Report copied. Paste into the GitHub draft, attach the screenshot, then click Submit new issue on GitHub."
|
||||
} else {
|
||||
"Review the GitHub draft and attach the screenshot. GitHub's Submit new issue button publishes it."
|
||||
}
|
||||
.into();
|
||||
}
|
||||
if ui.button(crate::i18n::tr_label("Copy report")).clicked() {
|
||||
ctx.copy_text(draft.body.clone());
|
||||
draft.note = "Report copied. Paste it into the GitHub draft if needed.".into();
|
||||
}
|
||||
close = ui.button(crate::i18n::tr_label("Close draft")).clicked();
|
||||
});
|
||||
});
|
||||
@@ -743,6 +771,8 @@ mod tests {
|
||||
let reviewed = draft.body.clone();
|
||||
h.app.issue_report.draft = Some(draft);
|
||||
h.frames(4);
|
||||
assert!(h.widget("1. Open GitHub draft below, then paste the report copied to your clipboard.").is_some());
|
||||
assert!(h.widget("3. Click Submit new issue on GitHub to send your report.").is_some());
|
||||
let [x, y] = h.widget("Open GitHub draft").unwrap();
|
||||
assert_eq!(h.call("ui.move", json!({"x":x,"y":y}))["ok"], true);
|
||||
let (request, _reply) = crate::control::ControlRequest::new("ui.click", json!({"x":x,"y":y}));
|
||||
@@ -758,6 +788,35 @@ mod tests {
|
||||
);
|
||||
assert_eq!(h.app.issue_report.draft.as_ref().unwrap().body, reviewed);
|
||||
assert!(h.app.issue_report.draft.as_ref().unwrap().note.starts_with("Report copied. Paste into the GitHub draft"));
|
||||
assert!(h.app.issue_report.draft.as_ref().unwrap().note.contains("Submit new issue"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_report_idle_window_does_not_grow_with_a_screenshot() {
|
||||
for size in [[1600, 1000], [900, 700], [640, 480]] {
|
||||
for scale in [1.0, 1.25, 1.4, 2.0] {
|
||||
let mut h = crate::scenario::Harness::new();
|
||||
h.app.preferences.ui_scale = scale;
|
||||
h.app.home.open = false;
|
||||
h.call("ui.resize", json!({"width":size[0],"height":size[1]}));
|
||||
let mut draft = draft_for(&h.app, None, None);
|
||||
draft.note = "Screenshot captured locally. Review it before attaching it on GitHub.".into();
|
||||
draft.image = Some(Arc::new(ColorImage::new([1600, 1000], vec![egui::Color32::GREEN; 1_600_000])));
|
||||
h.app.issue_report.draft = Some(draft);
|
||||
h.frames(20);
|
||||
let rect = |h: &crate::scenario::Harness| egui::containers::AreaState::load(&h.ctx, egui::Id::new("sc_issue_report")).unwrap().rect();
|
||||
let settled = rect(&h);
|
||||
h.frames(120);
|
||||
let idle = rect(&h);
|
||||
assert!((idle.width() - settled.width()).abs() < 1.0, "size={size:?}: {settled:?} grew to {idle:?}");
|
||||
assert!(idle.width() <= 790.0, "report must retain its width limit: {idle:?}");
|
||||
assert!(h.ctx.content_rect().contains_rect(idle), "size={size:?} scale={scale}: window must fit: {idle:?}");
|
||||
for label in ["Copy screenshot", "Save screenshot…", "Open GitHub draft", "Copy report", "Close draft"] {
|
||||
let [x, y] = h.widget(label).unwrap();
|
||||
assert!(idle.contains(egui::pos2(x as f32, y as f32)), "{label} must stay visible: {idle:?}, {x}, {y}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user