LCOV - code coverage report
Current view: top level - src/tui - app.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 85.4 % 103 88
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file tui/app.c
       6              :  * @brief TUI state machine implementation (US-11 v2).
       7              :  */
       8              : 
       9              : #include "tui/app.h"
      10              : 
      11              : #include <string.h>
      12              : 
      13           26 : int tui_app_init(TuiApp *app, int rows, int cols) {
      14           26 :     if (!app) return -1;
      15           26 :     memset(app, 0, sizeof(*app));
      16           26 :     if (screen_init(&app->screen, rows, cols) != 0) return -1;
      17           24 :     layout_compute(&app->layout, rows, cols, 28);
      18           24 :     dialog_pane_init(&app->dialogs);
      19           24 :     history_pane_init(&app->history);
      20           24 :     status_row_init(&app->status);
      21           24 :     app->focus = TUI_FOCUS_DIALOGS;
      22           24 :     app->rows = rows;
      23           24 :     app->cols = cols;
      24              :     /* Wire viewport heights so list_views know how much they render. */
      25           24 :     app->dialogs.lv.rows_visible = app->layout.dialogs.rows;
      26           24 :     app->history.lv.rows_visible = app->layout.history.rows;
      27           24 :     return 0;
      28              : }
      29              : 
      30           24 : void tui_app_free(TuiApp *app) {
      31           24 :     if (!app) return;
      32           24 :     screen_free(&app->screen);
      33              : }
      34              : 
      35            3 : int tui_app_resize(TuiApp *app, int rows, int cols) {
      36            3 :     if (!app) return -1;
      37            3 :     screen_free(&app->screen);
      38            3 :     if (screen_init(&app->screen, rows, cols) != 0) return -1;
      39            3 :     layout_compute(&app->layout, rows, cols, 28);
      40            3 :     list_view_set_viewport(&app->dialogs.lv, app->layout.dialogs.rows);
      41            3 :     list_view_set_viewport(&app->history.lv, app->layout.history.rows);
      42            3 :     app->rows = rows;
      43            3 :     app->cols = cols;
      44            3 :     return 0;
      45              : }
      46              : 
      47            9 : static ListView *focused_list_view(TuiApp *app) {
      48            9 :     return (app->focus == TUI_FOCUS_DIALOGS)
      49              :          ? &app->dialogs.lv
      50            9 :          : &app->history.lv;
      51              : }
      52              : 
      53           22 : TuiEvent tui_app_handle_key(TuiApp *app, TermKey key) {
      54           22 :     if (!app) return TUI_EVENT_NONE;
      55              : 
      56           22 :     switch (key) {
      57            3 :     case TERM_KEY_QUIT:
      58              :     case TERM_KEY_ESC:
      59            3 :         return TUI_EVENT_QUIT;
      60              : 
      61            2 :     case TERM_KEY_LEFT:
      62            2 :         if (app->focus != TUI_FOCUS_DIALOGS) {
      63            2 :             app->focus = TUI_FOCUS_DIALOGS;
      64            2 :             app->status.mode = STATUS_MODE_DIALOGS;
      65            2 :             return TUI_EVENT_REDRAW;
      66              :         }
      67            0 :         return TUI_EVENT_NONE;
      68              : 
      69            4 :     case TERM_KEY_RIGHT:
      70            4 :         if (app->focus != TUI_FOCUS_HISTORY) {
      71            3 :             app->focus = TUI_FOCUS_HISTORY;
      72            3 :             app->status.mode = STATUS_MODE_HISTORY;
      73            3 :             return TUI_EVENT_REDRAW;
      74              :         }
      75            1 :         return TUI_EVENT_NONE;
      76              : 
      77            2 :     case TERM_KEY_PREV_LINE:
      78            2 :         list_view_move_up(focused_list_view(app));
      79            2 :         return TUI_EVENT_REDRAW;
      80              : 
      81            5 :     case TERM_KEY_NEXT_LINE:
      82            5 :         list_view_move_down(focused_list_view(app));
      83            5 :         return TUI_EVENT_REDRAW;
      84              : 
      85            0 :     case TERM_KEY_PREV_PAGE:
      86            0 :         list_view_page_up(focused_list_view(app));
      87            0 :         return TUI_EVENT_REDRAW;
      88              : 
      89            0 :     case TERM_KEY_NEXT_PAGE:
      90            0 :         list_view_page_down(focused_list_view(app));
      91            0 :         return TUI_EVENT_REDRAW;
      92              : 
      93            1 :     case TERM_KEY_HOME:
      94            1 :         list_view_home(focused_list_view(app));
      95            1 :         return TUI_EVENT_REDRAW;
      96              : 
      97            1 :     case TERM_KEY_END:
      98            1 :         list_view_end(focused_list_view(app));
      99            1 :         return TUI_EVENT_REDRAW;
     100              : 
     101            4 :     case TERM_KEY_ENTER:
     102            4 :         if (app->focus == TUI_FOCUS_DIALOGS
     103            4 :             && dialog_pane_selected(&app->dialogs) != NULL) {
     104              :             /* Caller loads history for the selection and then flips focus
     105              :              * to the history pane. We switch focus here pre-emptively so
     106              :              * the status-row hint updates even before the network call. */
     107            3 :             app->focus = TUI_FOCUS_HISTORY;
     108            3 :             app->status.mode = STATUS_MODE_HISTORY;
     109            3 :             return TUI_EVENT_OPEN_DIALOG;
     110              :         }
     111            1 :         return TUI_EVENT_NONE;
     112              : 
     113            0 :     default:
     114            0 :         return TUI_EVENT_NONE;
     115              :     }
     116              : }
     117              : 
     118           13 : TuiEvent tui_app_handle_char(TuiApp *app, int ch) {
     119           13 :     if (!app) return TUI_EVENT_NONE;
     120           13 :     switch (ch) {
     121            7 :     case 'q': case 'Q':
     122            7 :         return TUI_EVENT_QUIT;
     123            3 :     case 'j':
     124            3 :         return tui_app_handle_key(app, TERM_KEY_NEXT_LINE);
     125            1 :     case 'k':
     126            1 :         return tui_app_handle_key(app, TERM_KEY_PREV_LINE);
     127            1 :     case 'h':
     128            1 :         return tui_app_handle_key(app, TERM_KEY_LEFT);
     129            1 :     case 'l':
     130            1 :         return tui_app_handle_key(app, TERM_KEY_RIGHT);
     131            0 :     case 'g':
     132            0 :         return tui_app_handle_key(app, TERM_KEY_HOME);
     133            0 :     case 'G':
     134            0 :         return tui_app_handle_key(app, TERM_KEY_END);
     135            0 :     default:
     136            0 :         return TUI_EVENT_NONE;
     137              :     }
     138              : }
     139              : 
     140           14 : void tui_app_paint(TuiApp *app) {
     141           14 :     if (!app) return;
     142           14 :     screen_clear_back(&app->screen);
     143           14 :     if (pane_is_valid(&app->layout.dialogs)) {
     144           14 :         dialog_pane_render(&app->dialogs, &app->layout.dialogs,
     145              :                            &app->screen,
     146           14 :                            app->focus == TUI_FOCUS_DIALOGS);
     147              :     }
     148           14 :     if (pane_is_valid(&app->layout.history)) {
     149           14 :         history_pane_render(&app->history, &app->layout.history,
     150              :                             &app->screen);
     151              :     }
     152           14 :     if (pane_is_valid(&app->layout.status)) {
     153           14 :         status_row_render(&app->status, &app->layout.status,
     154              :                           &app->screen);
     155              :     }
     156              : }
        

Generated by: LCOV version 2.0-1