Fortran · 21052 bytes Raw Blame History
1 ! Cairo-rendered Filing Cabinet Tab Bar for Sniffly
2 ! Custom tab bar with squarrounded tabs that resemble filing cabinet tabs
3 module cairo_tab_bar
4 use, intrinsic :: iso_c_binding
5 use gtk, only: gtk_drawing_area_new, gtk_drawing_area_set_draw_func, &
6 gtk_widget_set_size_request, gtk_event_controller_motion_new, &
7 gtk_widget_add_controller, gtk_gesture_click_new, &
8 gtk_widget_queue_draw, gtk_widget_set_hexpand, g_signal_connect
9 use cairo, only: cairo_set_source_rgb, cairo_set_source_rgba, cairo_move_to, &
10 cairo_line_to, cairo_curve_to, cairo_fill, cairo_stroke, &
11 cairo_set_line_width, cairo_arc
12 use pango, only: pango_cairo_create_layout, pango_layout_set_text, &
13 pango_font_description_from_string, pango_layout_set_font_description, &
14 pango_cairo_show_layout, pango_layout_get_pixel_size, &
15 pango_font_description_free
16 implicit none
17 private
18
19 public :: create_cairo_tab_bar, refresh_cairo_tab_bar, get_cairo_tab_bar_widget, &
20 register_cairo_tab_switch_callback, register_cairo_tab_close_callback, &
21 register_cairo_new_tab_callback
22
23 ! Tab dimensions
24 integer, parameter :: TAB_HEIGHT = 32
25 integer, parameter :: TAB_MIN_WIDTH = 120
26 integer, parameter :: TAB_MAX_WIDTH = 200
27 integer, parameter :: TAB_CORNER_RADIUS = 8
28 integer, parameter :: TAB_OVERLAP = 10
29 integer, parameter :: PLUS_BUTTON_WIDTH = 32
30 integer, parameter :: CLOSE_BUTTON_SIZE = 16
31 integer, parameter :: CLOSE_BUTTON_MARGIN = 8
32
33 ! Tab bar state
34 type(c_ptr), save :: tab_bar_widget = c_null_ptr
35 integer, save :: hovered_tab = 0 ! 0 = none, -1 = plus button, 1+ = tab index
36 integer, save :: hovered_close_button = 0 ! Which tab's close button is hovered
37
38 ! Tab bounds for hit testing
39 type :: tab_bounds
40 integer :: x, y, width, height
41 integer :: close_x, close_y ! Close button position
42 end type tab_bounds
43
44 type(tab_bounds), dimension(5), save :: tab_rects ! Max 5 tabs
45 integer :: plus_button_x, plus_button_y
46
47 ! Callback interfaces
48 abstract interface
49 subroutine tab_switch_callback(tab_index)
50 integer, intent(in) :: tab_index
51 end subroutine tab_switch_callback
52
53 subroutine tab_close_callback(tab_index)
54 integer, intent(in) :: tab_index
55 end subroutine tab_close_callback
56
57 subroutine new_tab_callback()
58 end subroutine new_tab_callback
59 end interface
60
61 ! Registered callbacks
62 procedure(tab_switch_callback), pointer, save :: switch_cb => null()
63 procedure(tab_close_callback), pointer, save :: close_cb => null()
64 procedure(new_tab_callback), pointer, save :: new_tab_cb => null()
65
66 contains
67
68 ! Create the Cairo-rendered tab bar
69 function create_cairo_tab_bar() result(widget)
70 type(c_ptr) :: widget, motion_controller, click_controller
71
72 ! Create drawing area
73 widget = gtk_drawing_area_new()
74 tab_bar_widget = widget
75
76 if (.not. c_associated(widget)) then
77 print *, "ERROR: Failed to create cairo tab bar drawing area"
78 return
79 end if
80
81 ! Set fixed height, expand horizontally
82 call gtk_widget_set_size_request(widget, -1_c_int, TAB_HEIGHT)
83 call gtk_widget_set_hexpand(widget, 1_c_int)
84
85 ! Set draw function
86 call gtk_drawing_area_set_draw_func(widget, c_funloc(on_draw_tabs), &
87 c_null_ptr, c_null_ptr)
88
89 ! Add motion controller for hover effects
90 motion_controller = gtk_event_controller_motion_new()
91 call g_signal_connect(motion_controller, "motion"//c_null_char, &
92 c_funloc(on_tab_motion), c_null_ptr)
93 call g_signal_connect(motion_controller, "leave"//c_null_char, &
94 c_funloc(on_tab_leave), c_null_ptr)
95 call gtk_widget_add_controller(widget, motion_controller)
96
97 ! Add click controller
98 click_controller = gtk_gesture_click_new()
99 call g_signal_connect(click_controller, "pressed"//c_null_char, &
100 c_funloc(on_tab_click), c_null_ptr)
101 call gtk_widget_add_controller(widget, click_controller)
102
103 print *, "Cairo tab bar created"
104 end function create_cairo_tab_bar
105
106 ! Draw the tab bar
107 subroutine on_draw_tabs(area, cr, width, height, user_data) bind(c)
108 use tab_manager, only: num_tabs, active_tab_index, get_tab, tab_state
109 type(c_ptr), value :: area, cr, user_data
110 integer(c_int), value :: width, height
111 type(tab_state), pointer :: tab
112 integer :: i, tab_x, tab_width, total_tabs_width
113 real(c_double) :: r, g, b, alpha
114
115 ! Clear background
116 call cairo_set_source_rgb(cr, 0.95_c_double, 0.95_c_double, 0.95_c_double)
117 call cairo_move_to(cr, 0.0_c_double, 0.0_c_double)
118 call cairo_line_to(cr, real(width, c_double), 0.0_c_double)
119 call cairo_line_to(cr, real(width, c_double), real(height, c_double))
120 call cairo_line_to(cr, 0.0_c_double, real(height, c_double))
121 call cairo_fill(cr)
122
123 ! Calculate starting position (flush to right edge, no overlap between tabs)
124 total_tabs_width = num_tabs * TAB_MIN_WIDTH + PLUS_BUTTON_WIDTH
125 tab_x = width - total_tabs_width
126
127 ! Draw plus button first (leftmost)
128 call draw_plus_button(cr, tab_x, 0)
129 plus_button_x = tab_x
130 plus_button_y = 0
131 tab_x = tab_x + PLUS_BUTTON_WIDTH
132
133 ! First pass: calculate positions and store in tab_rects
134 do i = 1, num_tabs
135 tab => get_tab(i)
136 if (.not. associated(tab)) cycle
137
138 tab_width = TAB_MIN_WIDTH
139
140 ! Store bounds for hit testing
141 tab_rects(i)%x = tab_x
142 tab_rects(i)%y = 0
143 tab_rects(i)%width = tab_width
144 tab_rects(i)%height = TAB_HEIGHT
145
146 ! No overlap - tabs sit flush next to each other
147 tab_x = tab_x + tab_width
148 end do
149
150 ! Second pass: draw inactive tabs first (so they appear behind)
151 do i = 1, num_tabs
152 if (i == active_tab_index) cycle ! Skip active tab
153 tab => get_tab(i)
154 if (.not. associated(tab)) cycle
155
156 call draw_inactive_tab(cr, tab_rects(i)%x, TAB_MIN_WIDTH, trim(tab%label), i == hovered_tab, i)
157 end do
158
159 ! Third pass: draw active tab last (so it appears on top)
160 if (active_tab_index >= 1 .and. active_tab_index <= num_tabs) then
161 tab => get_tab(active_tab_index)
162 if (associated(tab)) then
163 call draw_active_tab(cr, tab_rects(active_tab_index)%x, TAB_MIN_WIDTH, &
164 trim(tab%label), active_tab_index == hovered_tab, active_tab_index)
165 end if
166 end if
167 end subroutine on_draw_tabs
168
169 ! Draw an active tab (full brightness, merges into canvas)
170 subroutine draw_active_tab(cr, x, width, label, is_hovered, tab_index)
171 type(c_ptr), intent(in) :: cr
172 integer, intent(in) :: x, width, tab_index
173 character(len=*), intent(in) :: label
174 logical, intent(in) :: is_hovered
175 real(c_double) :: x_d, y_d, w_d, h_d, radius
176
177 x_d = real(x, c_double)
178 y_d = 0.0_c_double
179 w_d = real(width, c_double)
180 h_d = real(TAB_HEIGHT, c_double)
181 radius = real(TAB_CORNER_RADIUS, c_double)
182
183 ! Draw squarrounded shape (rounded top, flat bottom)
184 call cairo_move_to(cr, x_d, h_d) ! Bottom left
185 call cairo_line_to(cr, x_d, y_d + radius) ! Left edge
186 call cairo_arc(cr, x_d + radius, y_d + radius, radius, 3.14159_c_double, -1.57080_c_double) ! Top left corner
187 call cairo_line_to(cr, x_d + w_d - radius, y_d) ! Top edge
188 call cairo_arc(cr, x_d + w_d - radius, y_d + radius, radius, -1.57080_c_double, 0.0_c_double) ! Top right corner
189 call cairo_line_to(cr, x_d + w_d, h_d) ! Right edge to bottom
190
191 ! Fill with white/light gray (active color)
192 if (is_hovered) then
193 call cairo_set_source_rgb(cr, 1.0_c_double, 1.0_c_double, 1.0_c_double)
194 else
195 call cairo_set_source_rgb(cr, 0.98_c_double, 0.98_c_double, 0.98_c_double)
196 end if
197 call cairo_fill(cr)
198
199 ! Draw border (but not bottom for "seeping" effect)
200 call draw_tab_border_no_bottom(cr, x_d, y_d, w_d, h_d, radius)
201
202 ! Draw label
203 call draw_tab_label(cr, x, 0, width, TAB_HEIGHT, label, .false.)
204
205 ! Draw close button
206 call draw_close_button(cr, x, width, .false., tab_index)
207 end subroutine draw_active_tab
208
209 ! Draw an inactive tab (dimmed, with bottom border)
210 subroutine draw_inactive_tab(cr, x, width, label, is_hovered, tab_index)
211 type(c_ptr), intent(in) :: cr
212 integer, intent(in) :: x, width, tab_index
213 character(len=*), intent(in) :: label
214 logical, intent(in) :: is_hovered
215 real(c_double) :: x_d, y_d, w_d, h_d, radius
216
217 x_d = real(x, c_double)
218 y_d = 2.0_c_double ! Slightly lower than active
219 w_d = real(width, c_double)
220 h_d = real(TAB_HEIGHT - 2, c_double)
221 radius = real(TAB_CORNER_RADIUS, c_double)
222
223 ! Draw squarrounded shape
224 call cairo_move_to(cr, x_d, y_d + h_d) ! Bottom left
225 call cairo_line_to(cr, x_d, y_d + radius) ! Left edge
226 call cairo_arc(cr, x_d + radius, y_d + radius, radius, 3.14159_c_double, -1.57080_c_double)
227 call cairo_line_to(cr, x_d + w_d - radius, y_d)
228 call cairo_arc(cr, x_d + w_d - radius, y_d + radius, radius, -1.57080_c_double, 0.0_c_double)
229 call cairo_line_to(cr, x_d + w_d, y_d + h_d)
230
231 ! Fill with darker gray (inactive)
232 if (is_hovered) then
233 call cairo_set_source_rgb(cr, 0.88_c_double, 0.88_c_double, 0.88_c_double)
234 else
235 call cairo_set_source_rgb(cr, 0.82_c_double, 0.82_c_double, 0.82_c_double)
236 end if
237 call cairo_fill(cr)
238
239 ! Draw full border including bottom
240 call draw_tab_border_full(cr, x_d, y_d, w_d, h_d, radius)
241
242 ! Draw label (dimmed)
243 call draw_tab_label(cr, x, 2, width, TAB_HEIGHT - 2, label, .true.)
244
245 ! Draw close button
246 call draw_close_button(cr, x, width, .true., tab_index)
247 end subroutine draw_inactive_tab
248
249 ! Draw tab border without bottom (for active tab)
250 subroutine draw_tab_border_no_bottom(cr, x, y, width, height, radius)
251 type(c_ptr), intent(in) :: cr
252 real(c_double), intent(in) :: x, y, width, height, radius
253
254 call cairo_set_source_rgb(cr, 0.7_c_double, 0.7_c_double, 0.7_c_double)
255 call cairo_set_line_width(cr, 1.0_c_double)
256
257 call cairo_move_to(cr, x, y + height)
258 call cairo_line_to(cr, x, y + radius)
259 call cairo_arc(cr, x + radius, y + radius, radius, 3.14159_c_double, -1.57080_c_double)
260 call cairo_line_to(cr, x + width - radius, y)
261 call cairo_arc(cr, x + width - radius, y + radius, radius, -1.57080_c_double, 0.0_c_double)
262 call cairo_line_to(cr, x + width, y + height)
263 call cairo_stroke(cr)
264 end subroutine draw_tab_border_no_bottom
265
266 ! Draw full tab border (for inactive tabs)
267 subroutine draw_tab_border_full(cr, x, y, width, height, radius)
268 type(c_ptr), intent(in) :: cr
269 real(c_double), intent(in) :: x, y, width, height, radius
270
271 call cairo_set_source_rgb(cr, 0.6_c_double, 0.6_c_double, 0.6_c_double)
272 call cairo_set_line_width(cr, 1.0_c_double)
273
274 call cairo_move_to(cr, x, y + height)
275 call cairo_line_to(cr, x, y + radius)
276 call cairo_arc(cr, x + radius, y + radius, radius, 3.14159_c_double, -1.57080_c_double)
277 call cairo_line_to(cr, x + width - radius, y)
278 call cairo_arc(cr, x + width - radius, y + radius, radius, -1.57080_c_double, 0.0_c_double)
279 call cairo_line_to(cr, x + width, y + height)
280 call cairo_line_to(cr, x, y + height)
281 call cairo_stroke(cr)
282 end subroutine draw_tab_border_full
283
284 ! Draw tab label text
285 subroutine draw_tab_label(cr, x, y, width, height, text, is_dimmed)
286 use pango, only: pango_layout_set_width, pango_layout_set_ellipsize
287 type(c_ptr), intent(in) :: cr
288 integer, intent(in) :: x, y, width, height
289 character(len=*), intent(in) :: text
290 logical, intent(in) :: is_dimmed
291 type(c_ptr) :: layout, font_desc
292 integer(c_int), target :: text_width, text_height
293 integer(c_int) :: max_text_width
294 real(c_double) :: text_x, text_y
295
296 ! Pango constants (not available in gtk-fortran bindings)
297 integer(c_int), parameter :: PANGO_SCALE = 1024
298 integer(c_int), parameter :: PANGO_ELLIPSIZE_END = 3
299
300 ! Create pango layout
301 layout = pango_cairo_create_layout(cr)
302 call pango_layout_set_text(layout, trim(text)//c_null_char, -1_c_int)
303
304 ! Set font
305 font_desc = pango_font_description_from_string("Sans 10"//c_null_char)
306 call pango_layout_set_font_description(layout, font_desc)
307
308 ! Calculate maximum width for text (leave room for close button and some padding)
309 max_text_width = width - CLOSE_BUTTON_SIZE - CLOSE_BUTTON_MARGIN - 10
310
311 ! Enable ellipsization to truncate long text
312 call pango_layout_set_width(layout, max_text_width * PANGO_SCALE)
313 call pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END)
314
315 ! Get text size (after ellipsization)
316 call pango_layout_get_pixel_size(layout, c_loc(text_width), c_loc(text_height))
317
318 ! Center text in tab (leaving room for close button)
319 text_x = real(x, c_double) + real(width - CLOSE_BUTTON_SIZE - CLOSE_BUTTON_MARGIN - text_width, c_double) / 2.0_c_double
320 text_y = real(y, c_double) + real(height - text_height, c_double) / 2.0_c_double
321
322 ! Set text color
323 if (is_dimmed) then
324 call cairo_set_source_rgb(cr, 0.4_c_double, 0.4_c_double, 0.4_c_double)
325 else
326 call cairo_set_source_rgb(cr, 0.2_c_double, 0.2_c_double, 0.2_c_double)
327 end if
328
329 call cairo_move_to(cr, text_x, text_y)
330 call pango_cairo_show_layout(cr, layout)
331
332 call pango_font_description_free(font_desc)
333 end subroutine draw_tab_label
334
335 ! Draw close button (×)
336 subroutine draw_close_button(cr, tab_x, tab_width, is_dimmed, tab_index)
337 type(c_ptr), intent(in) :: cr
338 integer, intent(in) :: tab_x, tab_width, tab_index
339 logical, intent(in) :: is_dimmed
340 real(c_double) :: btn_x, btn_y, btn_size, center_x, center_y
341 logical :: is_hovered
342
343 btn_x = real(tab_x + tab_width - CLOSE_BUTTON_SIZE - CLOSE_BUTTON_MARGIN, c_double)
344 btn_y = real((TAB_HEIGHT - CLOSE_BUTTON_SIZE) / 2, c_double)
345 btn_size = real(CLOSE_BUTTON_SIZE, c_double)
346 center_x = btn_x + btn_size / 2.0_c_double
347 center_y = btn_y + btn_size / 2.0_c_double
348
349 ! Check if this close button is hovered
350 is_hovered = (hovered_close_button == tab_index)
351
352 ! Draw circular background when hovered
353 if (is_hovered) then
354 call cairo_set_source_rgb(cr, 0.6_c_double, 0.6_c_double, 0.6_c_double)
355 call cairo_arc(cr, center_x, center_y, btn_size / 2.0_c_double, 0.0_c_double, 6.28319_c_double)
356 call cairo_fill(cr)
357 end if
358
359 ! Draw × symbol
360 call cairo_set_line_width(cr, 1.5_c_double)
361 if (is_hovered) then
362 ! White × when hovered
363 call cairo_set_source_rgb(cr, 1.0_c_double, 1.0_c_double, 1.0_c_double)
364 else if (is_dimmed) then
365 call cairo_set_source_rgb(cr, 0.5_c_double, 0.5_c_double, 0.5_c_double)
366 else
367 call cairo_set_source_rgb(cr, 0.3_c_double, 0.3_c_double, 0.3_c_double)
368 end if
369
370 ! Draw X
371 call cairo_move_to(cr, btn_x + 4.0_c_double, btn_y + 4.0_c_double)
372 call cairo_line_to(cr, btn_x + btn_size - 4.0_c_double, btn_y + btn_size - 4.0_c_double)
373 call cairo_stroke(cr)
374
375 call cairo_move_to(cr, btn_x + btn_size - 4.0_c_double, btn_y + 4.0_c_double)
376 call cairo_line_to(cr, btn_x + 4.0_c_double, btn_y + btn_size - 4.0_c_double)
377 call cairo_stroke(cr)
378 end subroutine draw_close_button
379
380 ! Draw plus button
381 subroutine draw_plus_button(cr, x, y)
382 type(c_ptr), intent(in) :: cr
383 integer, intent(in) :: x, y
384 real(c_double) :: btn_x, btn_y, btn_size
385 logical :: is_hovered
386
387 is_hovered = (hovered_tab == -1)
388
389 btn_x = real(x, c_double) + 4.0_c_double
390 btn_y = real(y, c_double) + 4.0_c_double
391 btn_size = real(PLUS_BUTTON_WIDTH - 8, c_double)
392
393 ! Draw circle background
394 if (is_hovered) then
395 call cairo_set_source_rgb(cr, 0.9_c_double, 0.9_c_double, 0.9_c_double)
396 else
397 call cairo_set_source_rgb(cr, 0.85_c_double, 0.85_c_double, 0.85_c_double)
398 end if
399 call cairo_arc(cr, btn_x + btn_size / 2.0_c_double, btn_y + btn_size / 2.0_c_double, &
400 btn_size / 2.0_c_double, 0.0_c_double, 6.28319_c_double)
401 call cairo_fill(cr)
402
403 ! Draw + symbol
404 call cairo_set_line_width(cr, 2.0_c_double)
405 call cairo_set_source_rgb(cr, 0.3_c_double, 0.3_c_double, 0.3_c_double)
406
407 ! Horizontal line
408 call cairo_move_to(cr, btn_x + 6.0_c_double, btn_y + btn_size / 2.0_c_double)
409 call cairo_line_to(cr, btn_x + btn_size - 6.0_c_double, btn_y + btn_size / 2.0_c_double)
410 call cairo_stroke(cr)
411
412 ! Vertical line
413 call cairo_move_to(cr, btn_x + btn_size / 2.0_c_double, btn_y + 6.0_c_double)
414 call cairo_line_to(cr, btn_x + btn_size / 2.0_c_double, btn_y + btn_size - 6.0_c_double)
415 call cairo_stroke(cr)
416 end subroutine draw_plus_button
417
418 ! Handle mouse motion for hover effects
419 subroutine on_tab_motion(controller, x, y, user_data) bind(c)
420 use tab_manager, only: num_tabs
421 type(c_ptr), value :: controller, user_data
422 real(c_double), value :: x, y
423 integer :: i, old_hovered, old_hovered_close
424 integer :: close_btn_x, close_btn_y, close_btn_right, close_btn_bottom
425
426 old_hovered = hovered_tab
427 old_hovered_close = hovered_close_button
428 hovered_tab = 0
429 hovered_close_button = 0
430
431 ! Check plus button
432 if (x >= plus_button_x .and. x < plus_button_x + PLUS_BUTTON_WIDTH .and. &
433 y >= plus_button_y .and. y < plus_button_y + TAB_HEIGHT) then
434 hovered_tab = -1
435 else
436 ! Check tabs
437 do i = 1, num_tabs
438 if (x >= tab_rects(i)%x .and. x < tab_rects(i)%x + tab_rects(i)%width .and. &
439 y >= tab_rects(i)%y .and. y < tab_rects(i)%y + tab_rects(i)%height) then
440 hovered_tab = i
441
442 ! Check if mouse is over close button for this tab
443 close_btn_x = tab_rects(i)%x + tab_rects(i)%width - CLOSE_BUTTON_SIZE - CLOSE_BUTTON_MARGIN
444 close_btn_y = (TAB_HEIGHT - CLOSE_BUTTON_SIZE) / 2
445 close_btn_right = close_btn_x + CLOSE_BUTTON_SIZE
446 close_btn_bottom = close_btn_y + CLOSE_BUTTON_SIZE
447
448 if (x >= close_btn_x .and. x < close_btn_right .and. &
449 y >= close_btn_y .and. y < close_btn_bottom) then
450 hovered_close_button = i
451 end if
452
453 exit
454 end if
455 end do
456 end if
457
458 ! Redraw if hover state changed
459 if ((hovered_tab /= old_hovered .or. hovered_close_button /= old_hovered_close) .and. &
460 c_associated(tab_bar_widget)) then
461 call gtk_widget_queue_draw(tab_bar_widget)
462 end if
463 end subroutine on_tab_motion
464
465 ! Handle mouse leave (clear hover state)
466 subroutine on_tab_leave(controller, user_data) bind(c)
467 type(c_ptr), value :: controller, user_data
468
469 ! Clear hover state when mouse leaves the tab bar
470 if (hovered_tab /= 0 .and. c_associated(tab_bar_widget)) then
471 hovered_tab = 0
472 hovered_close_button = 0
473 call gtk_widget_queue_draw(tab_bar_widget)
474 end if
475 end subroutine on_tab_leave
476
477 ! Handle clicks
478 subroutine on_tab_click(gesture, n_press, x, y, user_data) bind(c)
479 use tab_manager, only: num_tabs
480 type(c_ptr), value :: gesture, user_data
481 integer(c_int), value :: n_press
482 real(c_double), value :: x, y
483 integer :: i
484
485 ! Check plus button
486 if (x >= plus_button_x .and. x < plus_button_x + PLUS_BUTTON_WIDTH .and. &
487 y >= plus_button_y .and. y < plus_button_y + TAB_HEIGHT) then
488 if (associated(new_tab_cb)) call new_tab_cb()
489 return
490 end if
491
492 ! Check tabs
493 do i = 1, num_tabs
494 if (x >= tab_rects(i)%x .and. x < tab_rects(i)%x + tab_rects(i)%width .and. &
495 y >= tab_rects(i)%y .and. y < tab_rects(i)%y + tab_rects(i)%height) then
496
497 ! Check if close button was clicked
498 if (x >= tab_rects(i)%x + tab_rects(i)%width - CLOSE_BUTTON_SIZE - CLOSE_BUTTON_MARGIN .and. &
499 x < tab_rects(i)%x + tab_rects(i)%width - CLOSE_BUTTON_MARGIN) then
500 if (associated(close_cb)) call close_cb(i)
501 else
502 ! Tab body clicked
503 if (associated(switch_cb)) call switch_cb(i)
504 end if
505 return
506 end if
507 end do
508 end subroutine on_tab_click
509
510 ! Refresh (trigger redraw)
511 subroutine refresh_cairo_tab_bar()
512 if (c_associated(tab_bar_widget)) then
513 call gtk_widget_queue_draw(tab_bar_widget)
514 end if
515 end subroutine refresh_cairo_tab_bar
516
517 ! Get widget
518 function get_cairo_tab_bar_widget() result(widget)
519 type(c_ptr) :: widget
520 widget = tab_bar_widget
521 end function get_cairo_tab_bar_widget
522
523 ! Register callbacks
524 subroutine register_cairo_tab_switch_callback(callback)
525 procedure(tab_switch_callback) :: callback
526 switch_cb => callback
527 end subroutine register_cairo_tab_switch_callback
528
529 subroutine register_cairo_tab_close_callback(callback)
530 procedure(tab_close_callback) :: callback
531 close_cb => callback
532 end subroutine register_cairo_tab_close_callback
533
534 subroutine register_cairo_new_tab_callback(callback)
535 procedure(new_tab_callback) :: callback
536 new_tab_cb => callback
537 end subroutine register_cairo_new_tab_callback
538
539 end module cairo_tab_bar
540