@@ -5,19 +5,43 @@ module tab_bar_mod |
| 5 | 5 | private |
| 6 | 6 | |
| 7 | 7 | public :: tab_bar_render |
| 8 | + public :: tab_bar_get_close_button_bounds |
| 9 | + public :: CLOSE_BTN_SIZE, CLOSE_BTN_MARGIN |
| 8 | 10 | |
| 9 | 11 | ! Tab bar styling constants |
| 10 | 12 | real, parameter :: TAB_PADDING = 8.0 ! Horizontal padding inside tab |
| 11 | 13 | real, parameter :: TAB_MIN_WIDTH = 80.0 ! Minimum tab width |
| 12 | 14 | real, parameter :: TAB_MAX_WIDTH = 200.0 ! Maximum tab width |
| 15 | + real, parameter :: CLOSE_BTN_SIZE = 14.0 ! Close button size (square) |
| 16 | + real, parameter :: CLOSE_BTN_MARGIN = 6.0 ! Margin from tab right edge |
| 13 | 17 | |
| 14 | 18 | contains |
| 15 | 19 | |
| 20 | + ! Get close button bounds for a specific tab (for hit testing) |
| 21 | + subroutine tab_bar_get_close_button_bounds(tab_index, tab_count, win_width, bar_height, & |
| 22 | + btn_x, btn_y, btn_size) |
| 23 | + integer, intent(in) :: tab_index, tab_count, win_width, bar_height |
| 24 | + real, intent(out) :: btn_x, btn_y, btn_size |
| 25 | + real :: tab_width, tab_start_x |
| 26 | + |
| 27 | + tab_width = real(win_width) / real(tab_count) |
| 28 | + if (tab_width > TAB_MAX_WIDTH) tab_width = TAB_MAX_WIDTH |
| 29 | + if (tab_width < TAB_MIN_WIDTH) tab_width = TAB_MIN_WIDTH |
| 30 | + |
| 31 | + tab_start_x = real(tab_index - 1) * tab_width |
| 32 | + btn_size = CLOSE_BTN_SIZE |
| 33 | + btn_x = tab_start_x + tab_width - CLOSE_BTN_MARGIN - CLOSE_BTN_SIZE |
| 34 | + btn_y = (real(bar_height) - CLOSE_BTN_SIZE) / 2.0 |
| 35 | + end subroutine tab_bar_get_close_button_bounds |
| 36 | + |
| 16 | 37 | ! Render the tab bar at the top of the window |
| 17 | | - subroutine tab_bar_render(ren, mgr, win_width, bar_height, cell_width, ascender) |
| 38 | + subroutine tab_bar_render(ren, mgr, win_width, bar_height, cell_width, ascender, & |
| 39 | + hover_tab, hover_close_btn) |
| 18 | 40 | type(renderer_t), intent(inout) :: ren |
| 19 | 41 | type(tab_manager_t), intent(in) :: mgr |
| 20 | 42 | integer, intent(in) :: win_width, bar_height, cell_width, ascender |
| 43 | + integer, intent(in) :: hover_tab ! Which tab mouse is over (0 = none) |
| 44 | + logical, intent(in) :: hover_close_btn ! Is mouse over close button? |
| 21 | 45 | integer :: i, title_len |
| 22 | 46 | real :: x, y, tab_width |
| 23 | 47 | real :: bg_r, bg_g, bg_b |
@@ -25,6 +49,8 @@ contains |
| 25 | 49 | real :: inactive_bg_r, inactive_bg_g, inactive_bg_b |
| 26 | 50 | real :: text_r, text_g, text_b |
| 27 | 51 | real :: divider_r, divider_g, divider_b |
| 52 | + real :: close_btn_x, close_btn_y |
| 53 | + real :: close_r, close_g, close_b |
| 28 | 54 | character(len=256) :: display_title |
| 29 | 55 | integer :: max_chars |
| 30 | 56 | |
@@ -69,8 +95,8 @@ contains |
| 69 | 95 | tab_width = TAB_MIN_WIDTH |
| 70 | 96 | end if |
| 71 | 97 | |
| 72 | | - ! Maximum characters that fit in a tab (rough estimate) |
| 73 | | - max_chars = int((tab_width - 2.0 * TAB_PADDING) / real(cell_width)) |
| 98 | + ! Maximum characters that fit in a tab (account for close button) |
| 99 | + max_chars = int((tab_width - 2.0 * TAB_PADDING - CLOSE_BTN_SIZE - CLOSE_BTN_MARGIN) / real(cell_width)) |
| 74 | 100 | if (max_chars < 3) max_chars = 3 |
| 75 | 101 | |
| 76 | 102 | x = 0.0 |
@@ -103,6 +129,31 @@ contains |
| 103 | 129 | real(bar_height) / 2.0 + real(ascender) / 2.0, & |
| 104 | 130 | trim(display_title), text_r, text_g, text_b, 1.0) |
| 105 | 131 | |
| 132 | + ! Draw close button (X) |
| 133 | + close_btn_x = x + tab_width - CLOSE_BTN_MARGIN - CLOSE_BTN_SIZE |
| 134 | + close_btn_y = (real(bar_height) - CLOSE_BTN_SIZE) / 2.0 |
| 135 | + |
| 136 | + ! Close button color: red on hover, gray otherwise |
| 137 | + if (i == hover_tab .and. hover_close_btn) then |
| 138 | + ! Hovered - show red background circle and white X |
| 139 | + call renderer_draw_rect(ren, close_btn_x, close_btn_y, & |
| 140 | + CLOSE_BTN_SIZE, CLOSE_BTN_SIZE, & |
| 141 | + 0.8, 0.2, 0.2, 1.0) |
| 142 | + close_r = 1.0 |
| 143 | + close_g = 1.0 |
| 144 | + close_b = 1.0 |
| 145 | + else |
| 146 | + ! Not hovered - subtle gray X |
| 147 | + close_r = 0.5 |
| 148 | + close_g = 0.5 |
| 149 | + close_b = 0.5 |
| 150 | + end if |
| 151 | + |
| 152 | + ! Draw X character centered in the button |
| 153 | + call renderer_draw_string(ren, close_btn_x + 2.0, & |
| 154 | + close_btn_y + real(ascender) - 2.0, & |
| 155 | + 'x', close_r, close_g, close_b, 1.0) |
| 156 | + |
| 106 | 157 | ! Draw vertical divider after tab (except last) |
| 107 | 158 | if (i < mgr%count) then |
| 108 | 159 | call renderer_draw_rect(ren, x + tab_width - 1.0, 4.0, 1.0, real(bar_height - 8), & |