Browse Source

feat: Add keyboard shortcuts and improved error messages (Phase 6)

Added user experience improvements:
- Keyboard shortcuts: ESC to cancel, ENTER to confirm
- Enhanced error messages with detailed descriptions
- Hohmann tab input validation
- Error message display in Hohmann tab
- Edge case handling for all input scenarios
- Clear error messages when tab switches

Keyboard shortcuts work for all dialog tabs and delete confirmation.
main
cinnaboot 5 months ago
parent
commit
479f234e0f
  1. 30
      docs/planning/maneuver_dialog_progress.md
  2. 40
      src/main.cpp
  3. 74
      src/ui_renderer.cpp

30
docs/planning/maneuver_dialog_progress.md

@ -89,13 +89,6 @@ None currently
## Pending Tasks ## Pending Tasks
### Phase 5: Enhanced Maneuver List
- [ ] Modify render_maneuver_list_ui() for selectable items
- [ ] Add maneuver preview panel to list
- [ ] Add action buttons (Create, Hohmann, Edit, Delete)
- [ ] Connect buttons to dialog
- [ ] Test enhanced list interaction
### Phase 6: Polish ### Phase 6: Polish
- [ ] Add keyboard shortcuts (Enter to confirm, Escape to cancel) - [ ] Add keyboard shortcuts (Enter to confirm, Escape to cancel)
- [ ] Improve error messages - [ ] Improve error messages
@ -122,26 +115,25 @@ None currently
### Phase 4 ### Phase 4
9. `b0d9d99` - feat: Implement Edit Maneuver tab (Phase 4) 9. `b0d9d99` - feat: Implement Edit Maneuver tab (Phase 4)
## Next Steps ### Phase 5
10. `0af8ece` - feat: Enhance maneuver list (Phase 5)
1. **Phase 5: Enhanced Maneuver List** - Improve list UI ## Next Steps
- Make maneuvers selectable
- Add preview panel below list
- Add edit/delete buttons for selected maneuver
2. **Phase 6: Polish** - User experience improvements 1. **Phase 6: Polish** - User experience improvements
- Keyboard shortcuts - Keyboard shortcuts
- Better error messages - Better error messages
- Tooltips and help text - Tooltips and help text
- Edge case handling - Edge case handling
- Testing with various configurations
## Progress Summary ## Progress Summary
- **Total Tasks:** 35 - **Total Tasks:** 35
- **Completed:** 25 (Phase 1: 6, Phase 2: 7, Phase 3: 6, Phase 4: 6) - **Completed:** 30 (Phase 1: 6, Phase 2: 7, Phase 3: 6, Phase 4: 6, Phase 5: 5)
- **In Progress:** 0 - **In Progress:** 0
- **Pending:** 10 (Phase 5: 5, Phase 6: 5) - **Pending:** 5 (Phase 6: 5)
- **Completion:** 71.4% - **Completion:** 85.7%
## Implementation Notes ## Implementation Notes
@ -155,4 +147,8 @@ None currently
- Proper initialization of current_body_index from spacecraft's parent - Proper initialization of current_body_index from spacecraft's parent
- Edit tab with locked spacecraft field and full parameter editing - Edit tab with locked spacecraft field and full parameter editing
- load_maneuver_into_dialog() loads existing maneuver data - load_maneuver_into_dialog() loads existing maneuver data
- Delete confirmation dialog overlay on edit tab - Delete confirmation dialog overlay on edit tab
- Enhanced maneuver list with selectable items
- Preview panel shows selected maneuver details and orbital preview
- Action buttons for Create, Hohmann, Edit, Delete
- selected_maneuver_index tracking in UIState

40
src/main.cpp

@ -131,6 +131,46 @@ void run_gui_simulation(SimulationState* sim) {
render_body_info_ui(sim, &render_state, &ui_state); render_body_info_ui(sim, &render_state, &ui_state);
render_maneuver_list_ui(sim, &render_state, &ui_state); render_maneuver_list_ui(sim, &render_state, &ui_state);
render_maneuver_dialog(sim, &render_state, &ui_state); render_maneuver_dialog(sim, &render_state, &ui_state);
// Handle keyboard shortcuts for maneuver dialog
if (ui_state.maneuver_dialog.open) {
if (IsKeyPressed(KEY_ESCAPE)) {
if (ui_state.maneuver_dialog.show_delete_confirm) {
ui_state.maneuver_dialog.show_delete_confirm = false;
} else {
ui_state.maneuver_dialog.open = false;
}
}
if (IsKeyPressed(KEY_ENTER)) {
if (ui_state.maneuver_dialog.show_delete_confirm) {
// Confirm delete
int edit_index = ui_state.maneuver_dialog.edit_maneuver_index;
if (edit_index >= 0 && edit_index < sim->maneuver_count) {
remove_maneuver_by_index(sim, edit_index);
ui_state.maneuver_dialog.show_delete_confirm = false;
ui_state.maneuver_dialog.open = false;
}
} else {
// Confirm action based on tab
switch (ui_state.maneuver_dialog.active_tab) {
case MD_TAB_CREATE:
create_maneuver_from_dialog(sim, &ui_state);
break;
case MD_TAB_HOHMANN:
// Calculate button functionality - trigger calculation
if (ui_state.maneuver_dialog.craft_index >= 0 &&
ui_state.maneuver_dialog.craft_index < sim->craft_count) {
calculate_hohmann_for_dialog(sim, &ui_state);
}
break;
case MD_TAB_EDIT:
update_maneuver_from_dialog(sim, &ui_state);
break;
}
}
}
}
end_frame(); end_frame();
} }

74
src/ui_renderer.cpp

@ -693,6 +693,9 @@ void render_orbital_preview(SimulationState* sim, UIState* ui_state, Rectangle b
void render_maneuver_hohmann_tab(SimulationState* sim, UIState* ui_state, int x, int y, int width, int height) { void render_maneuver_hohmann_tab(SimulationState* sim, UIState* ui_state, int x, int y, int width, int height) {
int current_y = y; int current_y = y;
ui_state->maneuver_dialog.show_error = false;
ui_state->maneuver_dialog.error_message[0] = '\0';
if (ui_state->maneuver_dialog.craft_index >= 0 && if (ui_state->maneuver_dialog.craft_index >= 0 &&
ui_state->maneuver_dialog.craft_index < sim->craft_count) { ui_state->maneuver_dialog.craft_index < sim->craft_count) {
Spacecraft* craft = &sim->spacecraft[ui_state->maneuver_dialog.craft_index]; Spacecraft* craft = &sim->spacecraft[ui_state->maneuver_dialog.craft_index];
@ -802,6 +805,12 @@ void render_maneuver_hohmann_tab(SimulationState* sim, UIState* ui_state, int x,
} }
current_y += 40; current_y += 40;
if (ui_state->maneuver_dialog.show_error) {
Rectangle error_bounds = {(float)x + 8, (float)current_y, (float)width - 16, 25};
GuiLabel(error_bounds, TextFormat("Error: %s", ui_state->maneuver_dialog.error_message));
current_y += 30;
}
if (ui_state->maneuver_dialog.show_hohmann_preview) { if (ui_state->maneuver_dialog.show_hohmann_preview) {
HohmannTransfer* ht = &ui_state->maneuver_dialog.last_calc; HohmannTransfer* ht = &ui_state->maneuver_dialog.last_calc;
@ -865,30 +874,85 @@ void render_maneuver_hohmann_tab(SimulationState* sim, UIState* ui_state, int x,
void calculate_hohmann_for_dialog(SimulationState* sim, UIState* ui_state) { void calculate_hohmann_for_dialog(SimulationState* sim, UIState* ui_state) {
ui_state->maneuver_dialog.show_hohmann_preview = false; ui_state->maneuver_dialog.show_hohmann_preview = false;
ui_state->maneuver_dialog.show_error = false;
ui_state->maneuver_dialog.error_message[0] = '\0';
if (ui_state->maneuver_dialog.craft_index < 0 ||
ui_state->maneuver_dialog.craft_index >= sim->craft_count) {
snprintf(ui_state->maneuver_dialog.error_message, sizeof(ui_state->maneuver_dialog.error_message),
"No spacecraft selected");
ui_state->maneuver_dialog.show_error = true;
return;
}
if (ui_state->maneuver_dialog.transfer_body_index < 0 || if (ui_state->maneuver_dialog.transfer_body_index < 0 ||
ui_state->maneuver_dialog.transfer_body_index >= sim->body_count || ui_state->maneuver_dialog.transfer_body_index >= sim->body_count) {
ui_state->maneuver_dialog.target_body_index < 0 || snprintf(ui_state->maneuver_dialog.error_message, sizeof(ui_state->maneuver_dialog.error_message),
"Invalid transfer parent body selected");
ui_state->maneuver_dialog.show_error = true;
return;
}
if (ui_state->maneuver_dialog.current_body_index < 0 ||
ui_state->maneuver_dialog.current_body_index >= sim->body_count) {
snprintf(ui_state->maneuver_dialog.error_message, sizeof(ui_state->maneuver_dialog.error_message),
"Invalid current body selected");
ui_state->maneuver_dialog.show_error = true;
return;
}
if (ui_state->maneuver_dialog.target_body_index < 0 ||
ui_state->maneuver_dialog.target_body_index >= sim->body_count) { ui_state->maneuver_dialog.target_body_index >= sim->body_count) {
snprintf(ui_state->maneuver_dialog.error_message, sizeof(ui_state->maneuver_dialog.error_message),
"Invalid target body selected");
ui_state->maneuver_dialog.show_error = true;
return; return;
} }
if (ui_state->maneuver_dialog.transfer_body_index == ui_state->maneuver_dialog.target_body_index) { if (ui_state->maneuver_dialog.transfer_body_index == ui_state->maneuver_dialog.target_body_index) {
snprintf(ui_state->maneuver_dialog.error_message, sizeof(ui_state->maneuver_dialog.error_message),
"Current and target bodies must be different");
ui_state->maneuver_dialog.show_error = true;
return;
}
Spacecraft* craft = &sim->spacecraft[ui_state->maneuver_dialog.craft_index];
if (craft->parent_index != ui_state->maneuver_dialog.current_body_index) {
snprintf(ui_state->maneuver_dialog.error_message, sizeof(ui_state->maneuver_dialog.error_message),
"Spacecraft is not orbiting the selected current body");
ui_state->maneuver_dialog.show_error = true;
return; return;
} }
CelestialBody* central_body = &sim->bodies[ui_state->maneuver_dialog.transfer_body_index]; CelestialBody* transfer_body = &sim->bodies[ui_state->maneuver_dialog.transfer_body_index];
CelestialBody* current_body = &sim->bodies[ui_state->maneuver_dialog.current_body_index]; CelestialBody* current_body = &sim->bodies[ui_state->maneuver_dialog.current_body_index];
CelestialBody* target_body = &sim->bodies[ui_state->maneuver_dialog.target_body_index]; CelestialBody* target_body = &sim->bodies[ui_state->maneuver_dialog.target_body_index];
double r1 = current_body->orbit.semi_major_axis; double r1 = current_body->orbit.semi_major_axis;
double r2 = target_body->orbit.semi_major_axis; double r2 = target_body->orbit.semi_major_axis;
if (r1 <= 0 || r2 <= 0) { if (r1 <= 0) {
snprintf(ui_state->maneuver_dialog.error_message, sizeof(ui_state->maneuver_dialog.error_message),
"Current body has invalid orbit (semi-major axis <= 0)");
ui_state->maneuver_dialog.show_error = true;
return;
}
if (r2 <= 0) {
snprintf(ui_state->maneuver_dialog.error_message, sizeof(ui_state->maneuver_dialog.error_message),
"Target body has invalid orbit (semi-major axis <= 0)");
ui_state->maneuver_dialog.show_error = true;
return;
}
if (transfer_body->mass <= 0) {
snprintf(ui_state->maneuver_dialog.error_message, sizeof(ui_state->maneuver_dialog.error_message),
"Transfer parent has invalid mass");
ui_state->maneuver_dialog.show_error = true;
return; return;
} }
ui_state->maneuver_dialog.last_calc = calculate_hohmann_transfer(r1, r2, central_body->mass); ui_state->maneuver_dialog.last_calc = calculate_hohmann_transfer(r1, r2, transfer_body->mass);
ui_state->maneuver_dialog.show_hohmann_preview = true; ui_state->maneuver_dialog.show_hohmann_preview = true;
} }

Loading…
Cancel
Save