Browse Source

feat: Implement Edit Maneuver tab (Phase 4)

Added complete edit functionality with:
- load_maneuver_into_dialog() to load existing maneuver data
- Locked spacecraft field display
- Edit all maneuver parameters with real-time preview
- Delete Maneuver button with confirmation dialog
- Delete confirmation overlay on edit tab
- Integrated with existing update_maneuver_from_dialog()
main
cinnaboot 5 months ago
parent
commit
b0d9d99aa8
  1. 56
      docs/planning/maneuver_dialog_progress.md
  2. 154
      src/ui_renderer.cpp
  3. 4
      src/ui_renderer.h

56
docs/planning/maneuver_dialog_progress.md

@ -39,6 +39,19 @@ Implementing a comprehensive modal dialog for creating, editing, and managing sp
- validate_burn_parameters(): Validate burn parameters
- calculate_hohmann_transfer(): Calculate optimal two-burn transfer
### Phase 3: Hohmann Tab (Complete)
- [x] Implement render_maneuver_hohmann_tab() skeleton
- [x] Add body selector widgets
- [x] Implement calculate_hohmann_for_dialog() using calculate_hohmann_transfer()
- [x] Display Hohmann results
- [x] Implement create_hohmann_burns() to add both maneuvers
- [x] Test Hohmann workflow
**Commit:** `94a7dcb` - feat: Implement Hohmann Transfer tab (Phase 3)
- Complete Hohmann transfer workflow with body selection UI
- Auto-generated maneuver names for both burns
- Real-time calculation and display of transfer parameters
### Bug Fixes (Complete)
- [x] Fix segfault at ui_renderer.cpp:503
- [x] Fix widget type for input boxes (GuiValueBoxFloat)
@ -62,14 +75,6 @@ None currently
## Pending Tasks
### Phase 3: Hohmann Tab
- [ ] Implement render_maneuver_hohmann_tab() skeleton
- [ ] Add body selector widgets
- [ ] Implement calculate_hohmann_for_dialog() using calculate_hohmann_transfer()
- [ ] Display Hohmann results
- [ ] Implement create_hohmann_burns() to add both maneuvers
- [ ] Test Hohmann workflow
### Phase 4: Edit Tab
- [ ] Implement render_maneuver_edit_tab() skeleton
- [ ] Load existing maneuver data into fields
@ -95,36 +100,32 @@ None currently
## Commits
### Core Implementation
1. `9c55c7c` - feat: Implement maneuver dialog Create tab (Phase 2)
2. `5ecd880` - feat: Add maneuver UI control functions
1. `94a7dcb` - feat: Implement Hohmann Transfer tab (Phase 3)
2. `9c55c7c` - feat: Implement maneuver dialog Create tab (Phase 2)
3. `5ecd880` - feat: Add maneuver UI control functions
### Bug Fixes
3. `351f647` - fix: Initialize UIState maneuver_dialog fields properly
4. `2874c5e` - feat: Add auto-generated maneuver names with edit capability
5. `f45b111` - fix: Add conditional rendering for GUI dropdowns
4. `351f647` - fix: Initialize UIState maneuver_dialog fields properly
5. `2874c5e` - feat: Add auto-generated maneuver names with edit capability
6. `f45b111` - fix: Add conditional rendering for GUI dropdowns
### Documentation
6. `883695c` - docs: Add maneuver management dialog design
7. `9da7e80` - docs: Add maneuver UI controls implementation plan
7. `883695c` - docs: Add maneuver management dialog design
8. `9da7e80` - docs: Add maneuver UI controls implementation plan
## Next Steps
1. **Phase 3: Hohmann Tab** - Implement Hohmann transfer calculation and UI
- Use existing calculate_hohmann_transfer() function
- Add body selector dropdowns
- Display results and create both burns
2. **Phase 4: Edit Tab** - Enable editing and deleting existing maneuvers
1. **Phase 4: Edit Tab** - Enable editing and deleting existing maneuvers
- Load maneuver data into form fields
- Implement update/delete logic
- Add confirmation dialogs
3. **Phase 5: Enhanced Maneuver List** - Improve list UI
2. **Phase 5: Enhanced Maneuver List** - Improve list UI
- Make maneuvers selectable
- Add preview panel below list
- Add edit/delete buttons for selected maneuver
4. **Phase 6: Polish** - User experience improvements
3. **Phase 6: Polish** - User experience improvements
- Keyboard shortcuts
- Better error messages
- Tooltips and help text
@ -133,10 +134,10 @@ None currently
## Progress Summary
- **Total Tasks:** 35
- **Completed:** 13 (Phase 1: 6, Phase 2: 7)
- **Completed:** 19 (Phase 1: 6, Phase 2: 7, Phase 3: 6)
- **In Progress:** 0
- **Pending:** 22 (Phase 3: 6, Phase 4: 6, Phase 5: 5, Phase 6: 5)
- **Completion:** 37.1%
- **Pending:** 16 (Phase 4: 6, Phase 5: 5, Phase 6: 5)
- **Completion:** 54.3%
## Implementation Notes
@ -145,3 +146,6 @@ None currently
- Dialog framework complete with tab system
- Create workflow fully functional with auto-generated names
- Real-time orbital preview implemented and validated
- Hohmann transfer tab implemented with full UI for spacecraft/parent/body selection
- create_hohmann_burns() creates both Hohmann maneuvers with auto-generated names
- Proper initialization of current_body_index from spacecraft's parent

154
src/ui_renderer.cpp

@ -386,6 +386,7 @@ void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UI
ui_state->maneuver_dialog.show_preview = false;
ui_state->maneuver_dialog.show_error = false;
ui_state->maneuver_dialog.error_message[0] = '\0';
ui_state->maneuver_dialog.show_delete_confirm = false;
}
return;
}
@ -471,6 +472,7 @@ void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UI
ui_state->maneuver_dialog.show_preview = false;
ui_state->maneuver_dialog.show_error = false;
ui_state->maneuver_dialog.error_message[0] = '\0';
ui_state->maneuver_dialog.show_delete_confirm = false;
}
free(maneuver_list_text);
@ -891,6 +893,26 @@ void render_maneuver_dialog(SimulationState* sim, RenderState* render_state, UIS
} else if (ui_state->maneuver_dialog.active_tab == MD_TAB_HOHMANN) {
render_maneuver_hohmann_tab(sim, ui_state, x + 8, y + 60, width - 16, height - 100);
} else if (ui_state->maneuver_dialog.active_tab == MD_TAB_EDIT) {
if (ui_state->maneuver_dialog.show_delete_confirm) {
Rectangle confirm_rect = {(float)x + width / 4, (float)y + height / 3, (float)width / 2, 120};
GuiWindowBox(confirm_rect, "Confirm Delete");
Rectangle confirm_label = {(float)x + width / 4 + 10, (float)y + height / 3 + 25, (float)width / 2 - 20, 40};
GuiLabel(confirm_label, "Are you sure you want to delete this maneuver?");
Rectangle cancel_confirm_btn = {(float)x + width / 4 + 10, (float)y + height / 3 + 80, 100, 30};
if (GuiButton(cancel_confirm_btn, "Cancel")) {
ui_state->maneuver_dialog.show_delete_confirm = false;
}
Rectangle confirm_btn = {(float)x + width - 10 - 110, (float)y + height / 3 + 80, 100, 30};
if (GuiButton(confirm_btn, "Delete")) {
delete_maneuver_from_dialog(sim, ui_state);
}
} else {
render_maneuver_edit_tab(sim, ui_state, x + 8, y + 60, width - 16, height - 100);
}
}
}
@ -1103,3 +1125,135 @@ void create_hohmann_burns(SimulationState* sim, UIState* ui_state, bool create_b
ui_state->maneuver_dialog.open = false;
}
void load_maneuver_into_dialog(SimulationState* sim, UIState* ui_state, int maneuver_index) {
if (maneuver_index < 0 || maneuver_index >= sim->maneuver_count) {
return;
}
Maneuver* maneuver = &sim->maneuvers[maneuver_index];
ui_state->maneuver_dialog.edit_maneuver_index = maneuver_index;
ui_state->maneuver_dialog.craft_index = maneuver->craft_index;
strncpy(ui_state->maneuver_dialog.name, maneuver->name, 64);
ui_state->maneuver_dialog.direction_active = (int)maneuver->direction;
ui_state->maneuver_dialog.delta_v = maneuver->delta_v;
ui_state->maneuver_dialog.trigger_type_active = (int)maneuver->trigger_type;
ui_state->maneuver_dialog.trigger_value = maneuver->trigger_value;
ui_state->maneuver_dialog.show_delete_confirm = false;
}
void render_maneuver_edit_tab(SimulationState* sim, UIState* ui_state, int x, int y, int width, int height) {
int current_y = y;
if (ui_state->maneuver_dialog.edit_maneuver_index < 0 ||
ui_state->maneuver_dialog.edit_maneuver_index >= sim->maneuver_count) {
GuiLabel({(float)x, (float)current_y, (float)width, 25}, "No maneuver selected for editing");
return;
}
Maneuver* maneuver = &sim->maneuvers[ui_state->maneuver_dialog.edit_maneuver_index];
char title[128];
snprintf(title, sizeof(title), "Edit: %s", maneuver->name);
GuiLabel({(float)x, (float)current_y, (float)width, 25}, title);
current_y += 30;
GuiLabel({(float)x, (float)current_y, 80, 25}, "Spacecraft:");
if (maneuver->craft_index >= 0 && maneuver->craft_index < sim->craft_count) {
char craft_name[64];
snprintf(craft_name, sizeof(craft_name), "%s (locked)", sim->spacecraft[maneuver->craft_index].name);
Rectangle craft_label = {(float)x + 85, (float)current_y, (float)(width - 95), 25};
GuiLabel(craft_label, craft_name);
}
current_y += 35;
Rectangle name_label = {(float)x, (float)current_y, 80, 25};
GuiLabel(name_label, "Name:");
Rectangle name_text = {(float)x + 85, (float)current_y, (float)(width - 95), 25};
GuiTextBox(name_text, ui_state->maneuver_dialog.name, 64, true);
current_y += 35;
Rectangle dir_label = {(float)x, (float)current_y, 80, 25};
GuiLabel(dir_label, "Direction:");
if (ui_state->maneuver_dialog.craft_index >= 0 && ui_state->maneuver_dialog.craft_index < sim->craft_count) {
Rectangle dir_combo = {(float)x + 85, (float)current_y, (float)(width - 95), 25};
GuiDropdownBox(dir_combo, "Prograde;Retrograde;Normal;Antinormal;Radial In;Radial Out;Custom", &ui_state->maneuver_dialog.direction_active, false);
}
current_y += 35;
Rectangle dv_label = {(float)x, (float)current_y, 80, 25};
GuiLabel(dv_label, "Delta-V:");
Rectangle dv_slider = {(float)x + 85, (float)current_y, (float)(width - 180), 20};
float dv_float = (float)ui_state->maneuver_dialog.delta_v;
GuiSlider(dv_slider, "0", "50000", &dv_float, 0, 50000);
ui_state->maneuver_dialog.delta_v = dv_float;
char dv_text[32];
snprintf(dv_text, sizeof(dv_text), "%.1f m/s", ui_state->maneuver_dialog.delta_v);
Rectangle dv_value = {(float)x + width - 90, (float)current_y, 85, 25};
GuiLabel(dv_value, dv_text);
current_y += 35;
Rectangle trigger_label = {(float)x, (float)current_y, 80, 25};
GuiLabel(trigger_label, "Trigger:");
if (ui_state->maneuver_dialog.craft_index >= 0 && ui_state->maneuver_dialog.craft_index < sim->craft_count) {
Rectangle trigger_combo = {(float)x + 85, (float)current_y, (float)(width - 95), 25};
GuiDropdownBox(trigger_combo, "Time;True Anomaly", &ui_state->maneuver_dialog.trigger_type_active, false);
}
current_y += 35;
Rectangle value_label = {(float)x, (float)current_y, 80, 25};
GuiLabel(value_label, "Value:");
Rectangle value_slider = {(float)x + 85, (float)current_y, (float)(width - 180), 20};
float value_float = (float)ui_state->maneuver_dialog.trigger_value;
if (ui_state->maneuver_dialog.trigger_type_active == 0) {
GuiSlider(value_slider, "0", "1000000", &value_float, 0, 1000000);
} else {
GuiSlider(value_slider, "0", "6.28", &value_float, 0, 2 * M_PI);
}
ui_state->maneuver_dialog.trigger_value = value_float;
char value_text[32];
if (ui_state->maneuver_dialog.trigger_type_active == 0) {
snprintf(value_text, sizeof(value_text), "%.1f s", ui_state->maneuver_dialog.trigger_value);
} else {
snprintf(value_text, sizeof(value_text), "%.2f rad", ui_state->maneuver_dialog.trigger_value);
}
Rectangle value_text_label = {(float)x + width - 90, (float)current_y, 85, 25};
GuiLabel(value_text_label, value_text);
update_orbital_preview(sim, ui_state);
if (ui_state->maneuver_dialog.show_preview) {
Rectangle preview_bounds = {(float)x + 8, (float)current_y + 10, (float)width - 16, 110};
render_orbital_preview(sim, ui_state, preview_bounds);
}
current_y += 130;
Rectangle delete_btn = {(float)x, (float)current_y, (float)(width - 10) / 2, 30};
if (GuiButton(delete_btn, "Delete Maneuver")) {
ui_state->maneuver_dialog.show_delete_confirm = true;
}
}
void delete_maneuver_from_dialog(SimulationState* sim, UIState* ui_state) {
int edit_index = ui_state->maneuver_dialog.edit_maneuver_index;
if (edit_index < 0 || edit_index >= sim->maneuver_count) {
ui_state->maneuver_dialog.show_delete_confirm = false;
return;
}
bool success = remove_maneuver_by_index(sim, edit_index);
if (success) {
ui_state->maneuver_dialog.show_delete_confirm = false;
ui_state->maneuver_dialog.open = false;
}
}

4
src/ui_renderer.h

@ -37,6 +37,7 @@ struct ManeuverDialogState {
char error_message[256];
bool show_error;
bool show_delete_confirm;
};
// UI state
@ -59,6 +60,7 @@ void render_body_info_ui(SimulationState* sim, RenderState* render_state, UIStat
void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UIState* ui_state);
void render_maneuver_dialog(SimulationState* sim, RenderState* render_state, UIState* ui_state);
void render_maneuver_create_tab(SimulationState* sim, UIState* ui_state, int x, int y, int width, int height);
void render_maneuver_edit_tab(SimulationState* sim, UIState* ui_state, int x, int y, int width, int height);
void update_orbital_preview(SimulationState* sim, UIState* ui_state);
void render_orbital_preview(SimulationState* sim, UIState* ui_state, Rectangle bounds);
bool validate_maneuver_inputs(SimulationState* sim, UIState* ui_state);
@ -68,5 +70,7 @@ void update_maneuver_from_dialog(SimulationState* sim, UIState* ui_state);
void render_maneuver_hohmann_tab(SimulationState* sim, UIState* ui_state, int x, int y, int width, int height);
void calculate_hohmann_for_dialog(SimulationState* sim, UIState* ui_state);
void create_hohmann_burns(SimulationState* sim, UIState* ui_state, bool create_both_burns);
void load_maneuver_into_dialog(SimulationState* sim, UIState* ui_state, int maneuver_index);
void delete_maneuver_from_dialog(SimulationState* sim, UIState* ui_state);
#endif

Loading…
Cancel
Save