mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-30 05:37:00 -06:00
UI: the macro step editor fits a narrow window
The editor's window had a minimum size of 660 by 380 and its toolbar was one row of about 680 pixels, so in a window 600 pixels wide Run to Here and Stop were off screen, and at 480 the inspector's tabs were cut off. The window is now never larger than the app's window, the name row, the toolbar and the inspector's tabs wrap (the toolbar's buttons are disabled in place rather than in a scope of their own, which kept them from wrapping), the inspector keeps room for its tabs, and its content scrolls sideways instead of widening the window. Tests check the editor at 600 and 480 pixels, and the Macros and Record Macro dialogs, which already fit.
This commit is contained in:
@@ -790,14 +790,16 @@ pub fn window(app: &mut SheetApp, ctx: &egui::Context, d: &mut Dialog, open: &mu
|
||||
let home_title = home_title(app, &ed);
|
||||
let dirty_mark = if ed.dirty { " •" } else { "" };
|
||||
let screen = ctx.content_rect();
|
||||
let fit = (screen.size() - vec2(16.0, 16.0)).max(vec2(240.0, 200.0));
|
||||
let mut win_open = true;
|
||||
ed.refs.drawn = false;
|
||||
egui::Window::new(format!("Macro: {}{dirty_mark}", ed.draft.name))
|
||||
.id(Id::new("gridcraft.macro_editor"))
|
||||
.collapsible(false)
|
||||
.resizable(true)
|
||||
.default_size(vec2(840.0, 560.0))
|
||||
.min_size(vec2(660.0, 380.0))
|
||||
// Never larger than the app's window, so a narrow one keeps every control on screen.
|
||||
.default_size(vec2(840.0, 560.0).min(fit))
|
||||
.min_size(vec2(660.0, 380.0).min(fit))
|
||||
// To the right, so the data the overlays mark (usually top left) stays in view.
|
||||
.default_pos(pos2((screen.right() - 860.0).max(screen.left() + 20.0), screen.top() + 190.0))
|
||||
.open(&mut win_open)
|
||||
@@ -1034,8 +1036,8 @@ fn prompts(app: &mut SheetApp, ctx: &egui::Context, ed: &mut Editor, open: &mut
|
||||
fn body(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog, ed: &mut Editor) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let debugging = app.session.macro_debug.is_some();
|
||||
// Name, description, Save.
|
||||
ui.horizontal(|ui| {
|
||||
// Name, description, Save; they wrap in a narrow window.
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label("Name:");
|
||||
let name = ui.add_enabled(!debugging, egui::TextEdit::singleline(&mut ed.draft.name).desired_width(180.0).id_salt("me_name"));
|
||||
ui.label("Description:");
|
||||
@@ -1055,7 +1057,8 @@ fn body(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog, ed: &mut Editor) {
|
||||
status_line(app, ui, ed, &t);
|
||||
ui.separator();
|
||||
let avail = ui.available_size();
|
||||
let left_w = (avail.x * 0.52).max(300.0);
|
||||
// The step list takes about half; in a narrow window the inspector keeps room for its tabs.
|
||||
let left_w = (avail.x * 0.52).max(300.0).min((avail.x - 190.0).max(160.0));
|
||||
ui.horizontal_top(|ui| {
|
||||
ui.allocate_ui_with_layout(vec2(left_w, avail.y), egui::Layout::top_down(egui::Align::Min), |ui| {
|
||||
ui.set_min_size(vec2(left_w, avail.y));
|
||||
@@ -1067,7 +1070,7 @@ fn body(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog, ed: &mut Editor) {
|
||||
});
|
||||
ui.separator();
|
||||
ui.allocate_ui_with_layout(vec2(ui.available_width(), avail.y), egui::Layout::top_down(egui::Align::Min), |ui| {
|
||||
egui::ScrollArea::vertical().id_salt("me_inspector").auto_shrink([false, false]).show(ui, |ui| {
|
||||
egui::ScrollArea::both().id_salt("me_inspector").auto_shrink([false, false]).show(ui, |ui| {
|
||||
inspector(app, ui, d, ed, debugging);
|
||||
});
|
||||
});
|
||||
@@ -1076,34 +1079,33 @@ fn body(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog, ed: &mut Editor) {
|
||||
|
||||
fn toolbar(app: &mut SheetApp, ui: &mut Ui, ed: &mut Editor, debugging: bool) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
ui.horizontal(|ui| {
|
||||
// The buttons wrap onto a second row in a narrow window.
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 2.0;
|
||||
let editing = !debugging;
|
||||
if ui.add_enabled_ui(editing, |ui| small_button(ui, Icon::Plus, "Add Step", "Add a step after the selected one", false)).inner.clicked() {
|
||||
if ui.add_enabled(editing, |ui: &mut Ui| small_button(ui, Icon::Plus, "Add Step", "Add a step after the selected one", false)).clicked() {
|
||||
ed.picker = Some(Picker::default());
|
||||
}
|
||||
let has_step = ed.selected.as_deref().and_then(split_path).is_some();
|
||||
if ui
|
||||
.add_enabled_ui(editing && has_step, |ui| small_button(ui, Icon::Copy, "Duplicate", "Duplicate the selected step", false))
|
||||
.inner
|
||||
.add_enabled(editing && has_step, |ui: &mut Ui| small_button(ui, Icon::Copy, "Duplicate", "Duplicate the selected step", false))
|
||||
.clicked()
|
||||
{
|
||||
duplicate(ed);
|
||||
}
|
||||
if ui.add_enabled_ui(editing && has_step, |ui| small_button(ui, Icon::Delete, "Delete", "Delete the selected step", false)).inner.clicked() {
|
||||
if ui.add_enabled(editing && has_step, |ui: &mut Ui| small_button(ui, Icon::Delete, "Delete", "Delete the selected step", false)).clicked() {
|
||||
delete(ed);
|
||||
}
|
||||
sep(ui, &t);
|
||||
if ui
|
||||
.add_enabled_ui(editing, |ui| {
|
||||
.add_enabled(editing, |ui: &mut Ui| {
|
||||
small_button(ui, Icon::Check, "Check", "Check the inputs and show on the grid what the macro would change", false)
|
||||
})
|
||||
.inner
|
||||
.clicked()
|
||||
{
|
||||
check(app, ed);
|
||||
}
|
||||
if ui.add_enabled_ui(editing, |ui| small_button(ui, Icon::Play, "Run", "Run the macro as one undo step", false)).inner.clicked() {
|
||||
if ui.add_enabled(editing, |ui: &mut Ui| small_button(ui, Icon::Play, "Run", "Run the macro as one undo step", false)).clicked() {
|
||||
run(app, ed);
|
||||
}
|
||||
sep(ui, &t);
|
||||
@@ -1116,10 +1118,10 @@ fn toolbar(app: &mut SheetApp, ui: &mut Ui, ed: &mut Editor, debugging: bool) {
|
||||
debug_action(app, ed, "continue");
|
||||
}
|
||||
let can_run_to = ed.selected.as_deref().and_then(split_path).is_some();
|
||||
if ui.add_enabled_ui(can_run_to, |ui| small_button(ui, Icon::RunTo, "Run to Here", "Run until the selected step", false)).inner.clicked() {
|
||||
if ui.add_enabled(can_run_to, |ui: &mut Ui| small_button(ui, Icon::RunTo, "Run to Here", "Run until the selected step", false)).clicked() {
|
||||
debug_action(app, ed, "runTo");
|
||||
}
|
||||
if ui.add_enabled_ui(debugging, |ui| small_button(ui, Icon::StopSquare, "Stop", "Stop debugging", false)).inner.clicked() {
|
||||
if ui.add_enabled(debugging, |ui: &mut Ui| small_button(ui, Icon::StopSquare, "Stop", "Stop debugging", false)).clicked() {
|
||||
ed.ask_stop = true;
|
||||
}
|
||||
});
|
||||
@@ -1486,7 +1488,7 @@ fn picker(ui: &mut Ui, ed: &mut Editor) {
|
||||
// ---------------------------------------------------------------- the inspector
|
||||
|
||||
fn inspector(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog, ed: &mut Editor, debugging: bool) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
for (tab, label) in [(Tab::Step, "Details"), (Tab::Inputs, "Inputs"), (Tab::Output, "Output")] {
|
||||
if ui.selectable_label(ed.tab == tab, label).clicked() {
|
||||
ed.tab = tab;
|
||||
|
||||
@@ -502,3 +502,31 @@ fn the_control_channels_cancel_closes_the_editor_as_its_own_close_does() {
|
||||
assert!(h.state().macros.editor.is_none());
|
||||
assert!(h.state().grid.macro_overlay.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_editor_fits_a_narrow_window() {
|
||||
for size in [egui::vec2(600.0, 480.0), egui::vec2(480.0, 400.0)] {
|
||||
let mut session = Session::new();
|
||||
session.new_workbook();
|
||||
let app = SheetApp::new(session, Default::default());
|
||||
let mut h = egui_kittest::Harness::builder().with_size(size).build_ui_state(
|
||||
|ui, app: &mut SheetApp| {
|
||||
let ctx = ui.ctx().clone();
|
||||
app.logic(&ctx);
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
h.run_steps(4);
|
||||
three(&mut h);
|
||||
h.run_steps(4);
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, size);
|
||||
let window = h.get_by_label_contains("Macro: Three").rect();
|
||||
assert!(screen.contains_rect(window), "the editor at {window:?} is outside the {size:?} window");
|
||||
// The editor's own controls (Delete is left out: the ribbon has one too).
|
||||
for label in ["Add Step", "Duplicate", "Check", "Run", "Step", "Continue", "Run to Here", "Stop", "Details", "Inputs", "Output", "Save"] {
|
||||
let r = h.get_by_label(label).rect();
|
||||
assert!(screen.contains_rect(r), "{label} at {r:?} is outside the {size:?} window");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,3 +283,34 @@ fn the_macros_dialog_offers_trust_for_scripts_that_came_with_a_file() {
|
||||
h.run_steps(3);
|
||||
assert_eq!(value(&h, "D4"), Value::Text("script".into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_macros_and_record_macro_dialogs_fit_a_narrow_window() {
|
||||
for size in [egui::vec2(600.0, 480.0), egui::vec2(480.0, 400.0)] {
|
||||
let mut s = Session::new();
|
||||
s.new_workbook();
|
||||
s.execute("macro.save", json!({"name": "Tidy", "steps": []})).unwrap();
|
||||
let app = SheetApp::new(s, Default::default());
|
||||
let mut h = egui_kittest::Harness::builder().with_size(size).build_ui_state(
|
||||
|ui, app: &mut SheetApp| {
|
||||
let ctx = ui.ctx().clone();
|
||||
app.logic(&ctx);
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
h.run_steps(3);
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, size);
|
||||
for (dialog, labels) in [("macros", &["Run", "Delete", "Options…", "Cancel"][..]), ("recordMacro", &["OK", "Cancel"][..])] {
|
||||
h.state_mut().open_dialog(dialog, json!({}));
|
||||
h.run_steps(4);
|
||||
for label in labels {
|
||||
// The dialog's own button: the last one drawn (the ribbon has a Delete too).
|
||||
let r = h.get_all_by_label(label).last().map(|n| n.rect()).unwrap_or(egui::Rect::NOTHING);
|
||||
assert!(screen.contains_rect(r), "{dialog}: {label} at {r:?} is outside the {size:?} window");
|
||||
}
|
||||
h.state_mut().dialog = None;
|
||||
h.run_steps(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -281,7 +281,8 @@ other macros open; a macro that was left out is not in the saved file.
|
||||
Run runs the selected macro on the selection or the selected script; Step Into, Edit and Create
|
||||
open the step editor; Delete asks first; Options sets a macro's shortcut letter and description. The dialog keeps the result of `view.macros`,
|
||||
as before macros existed.
|
||||
- **Step editor.** A resizable window that works on a draft and saves it with `macro.save`. The
|
||||
- **Step editor.** A resizable window that works on a draft and saves it with `macro.save`; in a
|
||||
narrow app window it is no larger than the window and its toolbar wraps onto a second row. The
|
||||
step list shows each step's number, a breakpoint dot in the gutter (click to set or clear), a
|
||||
category icon, a summary, an error marker and an enabled checkbox; Repeat bodies and If branches
|
||||
are indented under their step, and rows drag to reorder within their list. Add Step opens a
|
||||
|
||||
Reference in New Issue
Block a user