| 1 | module ui_display |
| 2 | use iso_fortran_env, only: output_unit |
| 3 | use terminal_control |
| 4 | use git_ops, only: write_git_indicators |
| 5 | use filesystem_ops, only: MAX_PATH, MAX_FILES |
| 6 | implicit none |
| 7 | private |
| 8 | |
| 9 | public :: draw_interface, get_file_color |
| 10 | |
| 11 | contains |
| 12 | |
| 13 | subroutine draw_interface(r, c, current_dir, current_files, current_is_dir, current_is_exec, & |
| 14 | current_is_staged, current_is_unstaged, current_is_untracked, current_count, & |
| 15 | parent_files, parent_is_dir, parent_is_exec, parent_count, & |
| 16 | selected, parent_selected, scroll_offset, parent_scroll_offset, & |
| 17 | in_git_repo, repo_name, branch_name) |
| 18 | integer, intent(in) :: r, c, current_count, parent_count, selected, parent_selected |
| 19 | integer, intent(in) :: scroll_offset, parent_scroll_offset |
| 20 | character(len=*), intent(in) :: current_dir, repo_name, branch_name |
| 21 | character(len=*), dimension(*), intent(in) :: current_files, parent_files |
| 22 | logical, dimension(*), intent(in) :: current_is_dir, parent_is_dir |
| 23 | logical, dimension(*), intent(in) :: current_is_exec, parent_is_exec |
| 24 | logical, dimension(*), intent(in) :: current_is_staged, current_is_unstaged, current_is_untracked |
| 25 | logical, intent(in) :: in_git_repo |
| 26 | integer :: left_w, i, parent_idx, current_idx, vis_h |
| 27 | character(len=256) :: fname |
| 28 | character(len=20) :: color_code |
| 29 | |
| 30 | left_w = c * 3 / 10 |
| 31 | vis_h = r - 3 ! Visible height |
| 32 | |
| 33 | ! Header |
| 34 | write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) |
| 35 | |
| 36 | ! Files (render based on scroll offsets) |
| 37 | do i = 1, vis_h |
| 38 | parent_idx = i + parent_scroll_offset |
| 39 | current_idx = i + scroll_offset |
| 40 | |
| 41 | ! Parent pane |
| 42 | if (parent_idx >= 1 .and. parent_idx <= parent_count) then |
| 43 | fname = parent_files(parent_idx) |
| 44 | if (parent_is_dir(parent_idx) .and. fname /= "." .and. fname /= "..") then |
| 45 | fname = trim(fname) // "/" |
| 46 | end if |
| 47 | |
| 48 | ! Get color for parent file |
| 49 | color_code = get_file_color(parent_files(parent_idx), parent_is_dir(parent_idx), parent_is_exec(parent_idx)) |
| 50 | |
| 51 | if (parent_idx == parent_selected) then |
| 52 | write(output_unit, '(a)', advance='no') DIM // BOLD // trim(color_code) // & |
| 53 | fname(1:min(len_trim(fname),left_w)) // RESET |
| 54 | else |
| 55 | write(output_unit, '(a)', advance='no') DIM // trim(color_code) // & |
| 56 | fname(1:min(len_trim(fname),left_w)) // RESET |
| 57 | end if |
| 58 | write(output_unit, '(a)', advance='no') repeat(" ", max(0, left_w - len_trim(fname))) |
| 59 | else |
| 60 | write(output_unit, '(a)', advance='no') repeat(" ", left_w) |
| 61 | end if |
| 62 | |
| 63 | ! Separator |
| 64 | write(output_unit, '(a)', advance='no') " │ " |
| 65 | |
| 66 | ! Current pane |
| 67 | if (current_idx >= 1 .and. current_idx <= current_count) then |
| 68 | fname = current_files(current_idx) |
| 69 | if (current_is_dir(current_idx) .and. fname /= "." .and. fname /= "..") then |
| 70 | fname = trim(fname) // "/" |
| 71 | end if |
| 72 | |
| 73 | ! Get color for current file |
| 74 | color_code = get_file_color(current_files(current_idx), current_is_dir(current_idx), current_is_exec(current_idx)) |
| 75 | |
| 76 | if (current_idx == selected) then |
| 77 | write(output_unit, '(a)', advance='no') REVERSE // trim(color_code) // trim(fname) |
| 78 | ! Add git indicators if in repo |
| 79 | if (in_git_repo) then |
| 80 | call write_git_indicators(current_is_staged(current_idx), & |
| 81 | current_is_unstaged(current_idx), & |
| 82 | current_is_untracked(current_idx), .true.) |
| 83 | end if |
| 84 | write(output_unit, '(a)') RESET |
| 85 | else |
| 86 | write(output_unit, '(a)', advance='no') trim(color_code) // trim(fname) |
| 87 | ! Add git indicators if in repo |
| 88 | if (in_git_repo) then |
| 89 | call write_git_indicators(current_is_staged(current_idx), & |
| 90 | current_is_unstaged(current_idx), & |
| 91 | current_is_untracked(current_idx), .false.) |
| 92 | end if |
| 93 | write(output_unit, '(a)') RESET |
| 94 | end if |
| 95 | else |
| 96 | write(output_unit, *) |
| 97 | end if |
| 98 | end do |
| 99 | |
| 100 | ! Footer |
| 101 | if (in_git_repo) then |
| 102 | write(output_unit, '(a)') DIM // trim(repo_name) // ":" // trim(branch_name) // " | " // RESET // & |
| 103 | DIM // "↑↓:nav →:enter ←:back f:find A:add U:unstage M:commit c:cd q:quit" // RESET |
| 104 | else |
| 105 | write(output_unit, '(a)') DIM // "↑↓:nav →:enter ←:back f:find c:cd q:quit" // RESET |
| 106 | end if |
| 107 | end subroutine draw_interface |
| 108 | |
| 109 | function get_file_color(filename, is_dir, is_exec) result(color) |
| 110 | character(len=*), intent(in) :: filename |
| 111 | logical, intent(in) :: is_dir, is_exec |
| 112 | character(len=20) :: color |
| 113 | |
| 114 | ! Directories: Blue and bold |
| 115 | if (is_dir) then |
| 116 | color = BOLD // BLUE |
| 117 | ! Dotfiles: Grey |
| 118 | else if (filename(1:1) == '.') then |
| 119 | color = GREY |
| 120 | ! Executable files: Green |
| 121 | else if (is_exec) then |
| 122 | color = GREEN |
| 123 | ! All other files: White |
| 124 | else |
| 125 | color = WHITE |
| 126 | end if |
| 127 | end function get_file_color |
| 128 | |
| 129 | end module ui_display |
| 130 |