@@ -23,6 +23,7 @@ module tab_widget |
| 23 | 23 | |
| 24 | 24 | ! Track button pointers to determine which tab was clicked |
| 25 | 25 | type(c_ptr), dimension(MAX_TABS), save :: tab_buttons = c_null_ptr |
| 26 | + type(c_ptr), dimension(MAX_TABS), save :: close_buttons = c_null_ptr |
| 26 | 27 | |
| 27 | 28 | ! Tab click callback interface |
| 28 | 29 | abstract interface |
@@ -75,7 +76,7 @@ contains |
| 75 | 76 | |
| 76 | 77 | ! Refresh tab bar (rebuild all tab buttons) |
| 77 | 78 | subroutine refresh_tab_bar() |
| 78 | | - type(c_ptr) :: plus_btn, tab_btn, child, next_child |
| 79 | + type(c_ptr) :: plus_btn, tab_btn, close_btn, tab_container, child, next_child |
| 79 | 80 | type(tab_state), pointer :: tab |
| 80 | 81 | integer :: i |
| 81 | 82 | character(len=256) :: label_text |
@@ -96,8 +97,9 @@ contains |
| 96 | 97 | child = next_child |
| 97 | 98 | end do |
| 98 | 99 | |
| 99 | | - ! Clear button pointer array |
| 100 | + ! Clear button pointer arrays |
| 100 | 101 | tab_buttons(:) = c_null_ptr |
| 102 | + close_buttons(:) = c_null_ptr |
| 101 | 103 | |
| 102 | 104 | print *, "Cleared old tab bar widgets" |
| 103 | 105 | |
@@ -113,12 +115,15 @@ contains |
| 113 | 115 | tab => get_tab(i) |
| 114 | 116 | if (.not. associated(tab)) cycle |
| 115 | 117 | |
| 118 | + ! Create horizontal container for this tab (label + close button) |
| 119 | + tab_container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2_c_int) |
| 120 | + |
| 116 | 121 | ! Format label as ".../basename" |
| 117 | 122 | label_text = ".../" // trim(tab%label) |
| 118 | 123 | |
| 119 | | - ! Create tab button |
| 124 | + ! Create tab label button |
| 120 | 125 | tab_btn = gtk_button_new_with_label(trim(label_text)//c_null_char) |
| 121 | | - call gtk_widget_set_size_request(tab_btn, 120_c_int, 28_c_int) |
| 126 | + call gtk_widget_set_size_request(tab_btn, 110_c_int, 28_c_int) |
| 122 | 127 | |
| 123 | 128 | ! Store button pointer so we can identify which tab was clicked |
| 124 | 129 | tab_buttons(i) = tab_btn |
@@ -128,11 +133,27 @@ contains |
| 128 | 133 | call gtk_widget_add_css_class(tab_btn, "active-tab"//c_null_char) |
| 129 | 134 | end if |
| 130 | 135 | |
| 131 | | - ! Connect click handler |
| 136 | + ! Connect click handler for tab selection |
| 132 | 137 | call g_signal_connect(tab_btn, "clicked"//c_null_char, & |
| 133 | 138 | c_funloc(on_tab_clicked), c_null_ptr) |
| 134 | 139 | |
| 135 | | - call gtk_box_append(tab_bar_container, tab_btn) |
| 140 | + call gtk_box_append(tab_container, tab_btn) |
| 141 | + |
| 142 | + ! Create close button (small × button) |
| 143 | + close_btn = gtk_button_new_with_label("×"//c_null_char) |
| 144 | + call gtk_widget_set_size_request(close_btn, 24_c_int, 28_c_int) |
| 145 | + |
| 146 | + ! Store close button pointer |
| 147 | + close_buttons(i) = close_btn |
| 148 | + |
| 149 | + ! Connect click handler for closing tab |
| 150 | + call g_signal_connect(close_btn, "clicked"//c_null_char, & |
| 151 | + c_funloc(on_close_clicked), c_null_ptr) |
| 152 | + |
| 153 | + call gtk_box_append(tab_container, close_btn) |
| 154 | + |
| 155 | + ! Add the tab container to the tab bar |
| 156 | + call gtk_box_append(tab_bar_container, tab_container) |
| 136 | 157 | print *, "Added tab button ", i, ": ", trim(label_text) |
| 137 | 158 | end do |
| 138 | 159 | |
@@ -252,6 +273,50 @@ contains |
| 252 | 273 | print *, "Switched to tab ", clicked_tab_index |
| 253 | 274 | end subroutine on_tab_clicked |
| 254 | 275 | |
| 276 | + ! Callback when a close button is clicked |
| 277 | + subroutine on_close_clicked(button, user_data) bind(c) |
| 278 | + type(c_ptr), value :: button, user_data |
| 279 | + integer :: i, clicked_tab_index |
| 280 | + |
| 281 | + ! Find which close button was clicked |
| 282 | + clicked_tab_index = -1 |
| 283 | + do i = 1, num_tabs |
| 284 | + if (c_associated(close_buttons(i), button)) then |
| 285 | + clicked_tab_index = i |
| 286 | + exit |
| 287 | + end if |
| 288 | + end do |
| 289 | + |
| 290 | + if (clicked_tab_index == -1) then |
| 291 | + print *, "WARNING: Could not determine which close button was clicked" |
| 292 | + return |
| 293 | + end if |
| 294 | + |
| 295 | + print *, "Close button clicked for tab ", clicked_tab_index |
| 296 | + |
| 297 | + ! Prevent closing last tab |
| 298 | + if (num_tabs <= 1) then |
| 299 | + print *, "ERROR: Cannot close last tab" |
| 300 | + return |
| 301 | + end if |
| 302 | + |
| 303 | + ! Close the tab |
| 304 | + call close_tab(clicked_tab_index) |
| 305 | + |
| 306 | + ! Rebuild tab bar |
| 307 | + call refresh_tab_bar() |
| 308 | + |
| 309 | + ! Update visual states |
| 310 | + call update_tab_visual_states() |
| 311 | + |
| 312 | + ! Update UI for the new active tab |
| 313 | + if (associated(tab_switch_cb)) then |
| 314 | + call tab_switch_cb() |
| 315 | + end if |
| 316 | + |
| 317 | + print *, "Tab ", clicked_tab_index, " closed - now ", num_tabs, " tabs remaining" |
| 318 | + end subroutine on_close_clicked |
| 319 | + |
| 255 | 320 | ! Register tab click callback |
| 256 | 321 | subroutine register_tab_click_callback(callback) |
| 257 | 322 | procedure(tab_click_callback) :: callback |