Fortran · 6197 bytes Raw Blame History
1 module tab_bar_mod
2 use renderer_mod
3 use tab_manager_mod
4 implicit none
5 private
6
7 public :: tab_bar_render
8 public :: tab_bar_get_close_button_bounds
9 public :: CLOSE_BTN_SIZE, CLOSE_BTN_MARGIN
10
11 ! Tab bar styling constants
12 real, parameter :: TAB_PADDING = 8.0 ! Horizontal padding inside tab
13 real, parameter :: TAB_MIN_WIDTH = 80.0 ! Minimum tab width
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
17
18 contains
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
37 ! Render the tab bar at the top of the window
38 subroutine tab_bar_render(ren, mgr, win_width, bar_height, cell_width, ascender, &
39 hover_tab, hover_close_btn)
40 type(renderer_t), intent(inout) :: ren
41 type(tab_manager_t), intent(in) :: mgr
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?
45 integer :: i, title_len
46 real :: x, y, tab_width
47 real :: bg_r, bg_g, bg_b
48 real :: active_bg_r, active_bg_g, active_bg_b
49 real :: inactive_bg_r, inactive_bg_g, inactive_bg_b
50 real :: text_r, text_g, text_b
51 real :: divider_r, divider_g, divider_b
52 real :: close_btn_x, close_btn_y
53 real :: close_r, close_g, close_b
54 character(len=256) :: display_title
55 integer :: max_chars
56
57 if (mgr%count <= 1) return
58
59 ! Colors (slightly darker than terminal background for contrast)
60 ! Tab bar background
61 bg_r = 0.08
62 bg_g = 0.08
63 bg_b = 0.10
64
65 ! Active tab (lighter)
66 active_bg_r = 0.15
67 active_bg_g = 0.15
68 active_bg_b = 0.18
69
70 ! Inactive tab (darker)
71 inactive_bg_r = 0.10
72 inactive_bg_g = 0.10
73 inactive_bg_b = 0.12
74
75 ! Text color
76 text_r = 0.85
77 text_g = 0.85
78 text_b = 0.85
79
80 ! Divider color
81 divider_r = 0.25
82 divider_g = 0.25
83 divider_b = 0.28
84
85 ! Draw tab bar background
86 call renderer_draw_rect(ren, 0.0, 0.0, real(win_width), real(bar_height), &
87 bg_r, bg_g, bg_b, 1.0)
88
89 ! Calculate tab width (evenly distributed, with min/max constraints)
90 if (mgr%count > 0) then
91 tab_width = real(win_width) / real(mgr%count)
92 if (tab_width > TAB_MAX_WIDTH) tab_width = TAB_MAX_WIDTH
93 if (tab_width < TAB_MIN_WIDTH) tab_width = TAB_MIN_WIDTH
94 else
95 tab_width = TAB_MIN_WIDTH
96 end if
97
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))
100 if (max_chars < 3) max_chars = 3
101
102 x = 0.0
103 y = 0.0
104
105 ! Draw each tab
106 do i = 1, mgr%count
107 ! Tab background
108 if (i == mgr%active_index) then
109 call renderer_draw_rect(ren, x + 1.0, y, tab_width - 2.0, real(bar_height), &
110 active_bg_r, active_bg_g, active_bg_b, 1.0)
111 else
112 call renderer_draw_rect(ren, x + 1.0, y, tab_width - 2.0, real(bar_height), &
113 inactive_bg_r, inactive_bg_g, inactive_bg_b, 1.0)
114 end if
115
116 ! Prepare display title (truncate if needed)
117 title_len = len_trim(mgr%tabs(i)%title)
118 if (title_len > max_chars) then
119 display_title = mgr%tabs(i)%title(1:max_chars-2) // '..'
120 title_len = max_chars
121 else
122 display_title = mgr%tabs(i)%title
123 end if
124
125 ! Draw tab title
126 ! Center text vertically: y + (bar_height - cell_height) / 2 + ascender
127 ! But we use ascender as baseline offset
128 call renderer_draw_string(ren, x + TAB_PADDING, &
129 real(bar_height) / 2.0 + real(ascender) / 2.0, &
130 trim(display_title), text_r, text_g, text_b, 1.0)
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
157 ! Draw vertical divider after tab (except last)
158 if (i < mgr%count) then
159 call renderer_draw_rect(ren, x + tab_width - 1.0, 4.0, 1.0, real(bar_height - 8), &
160 divider_r, divider_g, divider_b, 1.0)
161 end if
162
163 x = x + tab_width
164 end do
165
166 ! Draw bottom border line
167 call renderer_draw_rect(ren, 0.0, real(bar_height - 1), real(win_width), 1.0, &
168 divider_r, divider_g, divider_b, 1.0)
169
170 end subroutine tab_bar_render
171
172 end module tab_bar_mod
173