From 8b044e3ac1d45bc755e6c5e4920f930c3cca2429 Mon Sep 17 00:00:00 2001 From: echo Date: Fri, 22 May 2026 17:41:09 +0200 Subject: [PATCH] Phase C: Split process_clicks into 6 focused sub-functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit handle_nav_clicks — sidebar + pipeline stepper navigation handle_field_clicks — input field focus detection handle_format_clicks — PDF/PNG/CBZ + Local/DS toggle handle_action_clicks — all pipeline/file/log action buttons handle_workspace_nav — prev/next for Script/Panels/Layout/Bubbles handle_detail_clicks — panel regen, layout regen, bubble editor ops process_clicks: 269 → 30 lines (orchestration only) runtime.odin: 806 → 806 lines (delegation replaced inline logic) All 156 tests pass. --- odin/src/gui/runtime.odin | 84 +++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/odin/src/gui/runtime.odin b/odin/src/gui/runtime.odin index 4c14604..88df214 100644 --- a/odin/src/gui/runtime.odin +++ b/odin/src/gui/runtime.odin @@ -349,33 +349,31 @@ run_gui_app :: proc(state: ^core.Comic_State) -> shared.App_Error { return shared.ok() } -// ─── Click Processing ────────────────────────────────────────────── -process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_export, proj_ok, export_ok, has_deepseek: bool, pages_count: int, shift_down: bool, autosave_secs: int, compact_mode: bool) { - // Navigation +// ─── Click Handlers (called by process_clicks) ─────────────────── + +handle_nav_clicks :: proc(app: ^GUI_App_State) { nav_screen := []ui.App_Screen{.Story, .Script, .Characters, .Panels, .Layout, .Bubbles, .Export, .Community} for i in 0 ..< len(nav_screen) { if clicked(clay.ID("Nav", u32(i))) { push_status(&app.status_msg, &app.action_log, navigate_screen_with_status(&app.controller, nav_screen[i])) } } - - // Pipeline stepper clicks → navigate to corresponding screen pipeline_screens := []ui.App_Screen{.Script, .Panels, .Layout, .Export} for i in 0 ..< len(pipeline_screens) { if clicked(clay.ID("PStep", u32(i))) { push_status(&app.status_msg, &app.action_log, navigate_screen_with_status(&app.controller, pipeline_screens[i])) } } +} - // Input field focus +handle_field_clicks :: proc(app: ^GUI_App_State) { input_ids := []string{"field_idea", "field_genre", "field_audience", "field_export", "field_pages", "field_project"} for i in 0 ..< len(input_ids) { - if clicked(clay.ID(input_ids[i])) { - app.selected_field = i - } + if clicked(clay.ID(input_ids[i])) { app.selected_field = i } } +} - // Format buttons +handle_format_clicks :: proc(app: ^GUI_App_State, has_deepseek: bool) { if clicked(clay.ID("btn_pdf")) { push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .PDF, &app.is_dirty)) } if clicked(clay.ID("btn_png")) { push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .PNG, &app.is_dirty)) } if clicked(clay.ID("btn_cbz")) { push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .CBZ, &app.is_dirty)) } @@ -384,8 +382,9 @@ process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_expo if !has_deepseek { push_status(&app.status_msg, &app.action_log, "DeepSeek key missing") } else { app.use_deepseek_script = true; push_status(&app.status_msg, &app.action_log, "Script source: DeepSeek") } } +} - // Action buttons +handle_action_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_export: bool, pages_count: int, shift_down: bool, proj_ok, export_ok: bool, autosave_secs: int) { if clicked(clay.ID("btn_new")) { if app.is_dirty && !shift_down { push_status(&app.status_msg, &app.action_log, request_confirmation(&app.show_confirm_overlay, &app.show_help_overlay, &app.pending_confirm, .Reset_Project, "Confirm reset?")) } else { push_status(&app.status_msg, &app.action_log, reset_project_session(&app.controller, &app.is_dirty, &app.last_autosave_at, false)) } @@ -405,7 +404,6 @@ process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_expo } if clicked(clay.ID("btn_next")) { push_status(&app.status_msg, &app.action_log, run_next_action(&app.controller, &app.export_path, app.export_format, pages_count, app.use_deepseek_script, &app.is_dirty, &app.last_export_at)) } if clicked(clay.ID("btn_auto")) { push_status(&app.status_msg, &app.action_log, run_auto_all_action(&app.controller, &app.export_path, app.export_format, pages_count, app.use_deepseek_script, &app.is_dirty, &app.last_export_at)) } - if clicked(clay.ID("btn_autosave")) { push_status(&app.status_msg, &app.action_log, run_auto_all_save_action(&app.controller, &app.project_path, &app.export_path, app.export_format, pages_count, app.use_deepseek_script, &app.is_dirty, &app.last_export_at, &app.last_autosave_at, &app.last_save_at)) } if clicked(clay.ID("btn_save")) { push_status(&app.status_msg, &app.action_log, save_project_session_with_message(&app.project_path, app.controller.state, &app.is_dirty, &app.last_autosave_at, &app.last_save_at, "Saved project")) } if clicked(clay.ID("btn_open")) { if app.is_dirty && !shift_down { push_status(&app.status_msg, &app.action_log, request_confirmation(&app.show_confirm_overlay, &app.show_help_overlay, &app.pending_confirm, .Open_Project, "Confirm open?")) } @@ -422,10 +420,10 @@ process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_expo if clicked(clay.ID("btn_log_diag")) { push_status(&app.status_msg, &app.action_log, write_diagnostics_with_message(diag_ctx)) } if clicked(clay.ID("btn_log_status_copy")) { push_status(&app.status_msg, &app.action_log, copy_text_with_status(app.status_msg, "Copied status to clipboard")) } if clicked(clay.ID("btn_log_diag_copy")) { push_status(&app.status_msg, &app.action_log, copy_diagnostics_with_message(diag_ctx)) } +} +handle_workspace_nav :: proc(app: ^GUI_App_State) { screen := app.controller.active_screen - - // Workspace navigation buttons if screen == .Script { page_count := len(app.controller.state.script.pages) if clicked(clay.ID("btn_script_prev")) && page_count > 0 { @@ -489,18 +487,18 @@ process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_expo } } } +} + +handle_detail_clicks :: proc(app: ^GUI_App_State) { + screen := app.controller.active_screen - // Panels detail buttons - screen = app.controller.active_screen if screen == .Panels { if clicked(clay.ID("btn_panel_regenerate")) { panel_count := count_script_panels(app.controller.state.script) if panel_count > 0 { idx := clamp_panel_cursor(panel_count, app.summary_opts.panel_cursor) panel, _, ok := panel_by_flat_index(app.controller.state.script, idx) - if ok { - push_status(&app.status_msg, &app.action_log, action_regenerate_panel(&app.controller, panel.panel_id)) - } + if ok { push_status(&app.status_msg, &app.action_log, action_regenerate_panel(&app.controller, panel.panel_id)) } } } panel_count := count_script_panels(app.controller.state.script) @@ -514,7 +512,6 @@ process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_expo } } - // Layout detail buttons if screen == .Layout { if clicked(clay.ID("btn_layout_regen")) { push_status(&app.status_msg, &app.action_log, action_regenerate_page_layout(&app.controller, app.summary_opts.layout_page_cursor)) @@ -529,7 +526,6 @@ process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_expo } } - // Bubbles detail buttons if screen == .Bubbles { layout_count := len(app.controller.state.page_layouts) if layout_count > 0 { @@ -554,8 +550,6 @@ process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_expo } } } - - // Bubble row clicks if layout_count > 0 { page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor) layout_val := app.controller.state.page_layouts[page_idx] @@ -570,13 +564,9 @@ process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_expo if clicked(clay.ID(fmt.tprintf("btn_bubble_delete_%d", i))) { push_status(&app.status_msg, &app.action_log, action_delete_bubble(&app.controller, panel_id, i)) app.is_dirty = true - if app.summary_opts.bubble_edit_cursor > 0 { - app.summary_opts.bubble_edit_cursor -= 1 - } + if app.summary_opts.bubble_edit_cursor > 0 { app.summary_opts.bubble_edit_cursor -= 1 } } } - - // Type selector buttons bubble_count := count_bubbles_for_panel(app.controller.state.speech_bubbles, panel_id) if bubble_count > 0 && app.summary_opts.bubble_edit_cursor < bubble_count { types := []core.Bubble_Type{.Normal, .Thought, .Shout, .Whisper, .Narration, .Sound_Effect} @@ -591,21 +581,37 @@ process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_expo } } } +} +process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_export, proj_ok, export_ok, has_deepseek: bool, pages_count: int, shift_down: bool, autosave_secs: int, compact_mode: bool) { + handle_nav_clicks(app) + handle_field_clicks(app) + handle_format_clicks(app, has_deepseek) + handle_action_clicks(app, can_gen_panels, can_layout, can_export, pages_count, shift_down, proj_ok, export_ok, autosave_secs) + handle_workspace_nav(app) + handle_detail_clicks(app) - // Confirm overlay buttons - if clicked(clay.ID("confirm_yes")) && app.show_confirm_overlay { - action := app.pending_confirm - app.show_confirm_overlay = false - app.pending_confirm = .None - push_status(&app.status_msg, &app.action_log, resolve_confirm_action_with_message(action, &app.controller, &app.project_path, &app.export_path, app.export_format, &app.is_dirty, &app.last_autosave_at)) + // Overlay clicks + if app.show_confirm_overlay { + confirm_yes := rl.IsKeyPressed(.ENTER) || rl.IsKeyPressed(.Y) || clicked(clay.ID("confirm_yes")) + confirm_no := rl.IsKeyPressed(.ESCAPE) || rl.IsKeyPressed(.N) || clicked(clay.ID("confirm_no")) + if confirm_no { + app.show_confirm_overlay = false + app.pending_confirm = .None + push_status(&app.status_msg, &app.action_log, "Cancelled destructive action") + } else if confirm_yes { + app.show_confirm_overlay = false + msg := resolve_confirm_action_with_message(app.pending_confirm, &app.controller, &app.project_path, &app.export_path, app.export_format, &app.is_dirty, &app.last_autosave_at) + push_status(&app.status_msg, &app.action_log, msg) + app.pending_confirm = .None + } } - if clicked(clay.ID("confirm_no")) && app.show_confirm_overlay { - app.show_confirm_overlay = false - app.pending_confirm = .None - push_status(&app.status_msg, &app.action_log, "Cancelled destructive action") + if app.show_help_overlay { + if rl.IsKeyPressed(.ESCAPE) || rl.IsKeyPressed(.SLASH) { + app.show_help_overlay = false + push_status(&app.status_msg, &app.action_log, "Closed help overlay") + } } } - // ─── Clay UI Primitives ─────────────────────────────────────────── declare_nav_chip :: proc(id: string, label: string, active: bool) { bg: clay.Color = clay.Color{0, 0, 0, 0}