@@ -18,7 +18,8 @@ module gtk_app |
| 18 | 18 | gtk_entry_new, gtk_entry_buffer_set_text, gtk_entry_get_buffer, & |
| 19 | 19 | gtk_editable_set_editable, gtk_editable_get_text, & |
| 20 | 20 | gtk_entry_set_placeholder_text, gtk_widget_add_css_class, & |
| 21 | | - gtk_widget_remove_css_class |
| 21 | + gtk_widget_remove_css_class, gtk_css_provider_new, & |
| 22 | + gtk_css_provider_load_from_string, gtk_style_context_add_provider_for_display |
| 22 | 23 | use gdk, only: gdk_display_get_default, gdk_display_get_clipboard, gdk_clipboard_set_text |
| 23 | 24 | use g, only: g_application_run, g_idle_add, g_timeout_add_seconds_once |
| 24 | 25 | use treemap_widget, only: create_treemap_widget, set_scan_path, register_navigation_callback, & |
@@ -31,8 +32,9 @@ module gtk_app |
| 31 | 32 | use treemap_renderer, only: register_progress_callback, scan_directory, set_redraw_widget, & |
| 32 | 33 | register_scan_completion_callback |
| 33 | 34 | use tab_manager, only: tab_state, init_tab_manager, create_tab, get_active_tab, & |
| 34 | | - switch_to_tab, num_tabs, active_tab_index |
| 35 | | - use tab_widget, only: create_tab_bar, refresh_tab_bar, register_tab_switch_callback |
| 35 | + switch_to_tab, num_tabs, active_tab_index, get_path_basename |
| 36 | + use tab_widget, only: create_tab_bar, refresh_tab_bar, register_tab_switch_callback, & |
| 37 | + update_tab_visual_states |
| 36 | 38 | implicit none |
| 37 | 39 | private |
| 38 | 40 | |
@@ -478,6 +480,9 @@ contains |
| 478 | 480 | ! Register keyboard handler on window (not widget) for global keyboard capture |
| 479 | 481 | call register_key_handler(main_window_ptr) |
| 480 | 482 | |
| 483 | + ! Load custom CSS (including pulsing animation for suggested-action) |
| 484 | + call load_custom_css() |
| 485 | + |
| 481 | 486 | ! Show the window first with "Scanning..." status |
| 482 | 487 | call gtk_window_present(main_window_ptr) |
| 483 | 488 | |
@@ -491,6 +496,37 @@ contains |
| 491 | 496 | print *, "Window size: ", DEFAULT_WIDTH, "x", DEFAULT_HEIGHT |
| 492 | 497 | end subroutine on_activate |
| 493 | 498 | |
| 499 | + ! Load custom CSS for animations and styling |
| 500 | + subroutine load_custom_css() |
| 501 | + type(c_ptr) :: css_provider, display |
| 502 | + character(len=:), allocatable :: css_data |
| 503 | + |
| 504 | + ! CSS with pulsing animation for suggested-action class (empty tabs) |
| 505 | + css_data = & |
| 506 | + "@keyframes pulse { " // & |
| 507 | + "0% { opacity: 1.0; } " // & |
| 508 | + "50% { opacity: 0.5; } " // & |
| 509 | + "100% { opacity: 1.0; } " // & |
| 510 | + "} " // & |
| 511 | + ".suggested-action { " // & |
| 512 | + "animation: pulse 1.5s ease-in-out infinite; " // & |
| 513 | + "}" |
| 514 | + |
| 515 | + ! Create CSS provider |
| 516 | + css_provider = gtk_css_provider_new() |
| 517 | + |
| 518 | + ! Load CSS from string |
| 519 | + call gtk_css_provider_load_from_string(css_provider, trim(css_data)//c_null_char) |
| 520 | + |
| 521 | + ! Get default display |
| 522 | + display = gdk_display_get_default() |
| 523 | + |
| 524 | + ! Add CSS provider to display (600 = GTK_STYLE_PROVIDER_PRIORITY_APPLICATION) |
| 525 | + call gtk_style_context_add_provider_for_display(display, css_provider, 600_c_int) |
| 526 | + |
| 527 | + print *, "Custom CSS loaded (pulsing animation for empty tabs)" |
| 528 | + end subroutine load_custom_css |
| 529 | + |
| 494 | 530 | ! Callback when Open Directory button is clicked |
| 495 | 531 | ! NOTE: Uses system command for file picking until GTK4 file dialog bindings are available |
| 496 | 532 | subroutine on_open_dir_clicked(button, user_data) bind(c) |
@@ -914,6 +950,11 @@ contains |
| 914 | 950 | ! Update status bar to guide user |
| 915 | 951 | call sniffly_update_status("No directory selected - click the folder icon to choose a directory") |
| 916 | 952 | |
| 953 | + ! Clear the treemap drawing (redraw with no data will show blank) |
| 954 | + if (c_associated(drawing_area_ptr)) then |
| 955 | + call gtk_widget_queue_draw(drawing_area_ptr) |
| 956 | + end if |
| 957 | + |
| 917 | 958 | return |
| 918 | 959 | end if |
| 919 | 960 | |
@@ -1687,7 +1728,12 @@ contains |
| 1687 | 1728 | current_view => get_current_view_node() |
| 1688 | 1729 | if (associated(current_view) .and. allocated(current_view%path)) then |
| 1689 | 1730 | tab%scan_path = trim(current_view%path) |
| 1731 | + |
| 1732 | + ! Update tab label to reflect new path |
| 1733 | + tab%label = get_path_basename(trim(tab%scan_path)) |
| 1734 | + |
| 1690 | 1735 | print *, " Synced tab scan_path to: ", trim(tab%scan_path) |
| 1736 | + print *, " Updated tab label to: ", trim(tab%label) |
| 1691 | 1737 | print *, " Current nav_history_pos: ", tab%nav_history_pos, " nav_history_count: ", tab%nav_history_count |
| 1692 | 1738 | |
| 1693 | 1739 | ! Check for breadcrumb-based lookahead first (when clicking up in breadcrumb) |
@@ -1778,6 +1824,10 @@ contains |
| 1778 | 1824 | |
| 1779 | 1825 | ! Update button states now that history may have changed |
| 1780 | 1826 | call update_history_buttons() |
| 1827 | + |
| 1828 | + ! Refresh tab bar to show updated label |
| 1829 | + call refresh_tab_bar() |
| 1830 | + call update_tab_visual_states() |
| 1781 | 1831 | end subroutine breadcrumb_callback |
| 1782 | 1832 | |
| 1783 | 1833 | ! Show progress bar (now just resets to prepare for updates) |