@@ -1,5 +1,4 @@ |
| 1 | | -! UI display for Fortress navigator (adapted for fac - Phase 1 simplified) |
| 2 | | -! Original source: fortress/src/ui/display.f90 |
| 1 | +! UI display for Fortress navigator (simplified for fac) |
| 3 | 2 | |
| 4 | 3 | module fortress_display_module |
| 5 | 4 | use iso_fortran_env, only: output_unit |
@@ -7,9 +6,9 @@ module fortress_display_module |
| 7 | 6 | implicit none |
| 8 | 7 | private |
| 9 | 8 | |
| 10 | | - public :: draw_fortress_interface, get_file_color |
| 9 | + public :: draw_fortress_interface |
| 11 | 10 | |
| 12 | | - ! ANSI escape codes (matching fortress terminal_control) |
| 11 | + ! ANSI escape codes |
| 13 | 12 | character(len=*), parameter :: ESC = char(27) |
| 14 | 13 | character(len=*), parameter :: BOLD = ESC // "[1m" |
| 15 | 14 | character(len=*), parameter :: DIM = ESC // "[2m" |
@@ -17,10 +16,8 @@ module fortress_display_module |
| 17 | 16 | character(len=*), parameter :: RESET = ESC // "[0m" |
| 18 | 17 | character(len=*), parameter :: BLUE = ESC // "[34m" |
| 19 | 18 | character(len=*), parameter :: GREEN = ESC // "[32m" |
| 20 | | - character(len=*), parameter :: RED = ESC // "[31m" |
| 21 | 19 | character(len=*), parameter :: GREY = ESC // "[90m" |
| 22 | 20 | character(len=*), parameter :: WHITE = ESC // "[37m" |
| 23 | | - character(len=*), parameter :: YELLOW = ESC // "[33m" |
| 24 | 21 | |
| 25 | 22 | contains |
| 26 | 23 | |
@@ -33,128 +30,92 @@ contains |
| 33 | 30 | character(len=*), dimension(*), intent(in) :: current_files, parent_files |
| 34 | 31 | logical, dimension(*), intent(in) :: current_is_dir, parent_is_dir |
| 35 | 32 | logical, dimension(*), intent(in) :: current_is_exec, parent_is_exec |
| 36 | | - integer :: left_w, i, parent_idx, current_idx, vis_h, display_len, inner_w |
| 37 | | - character(len=256) :: fname |
| 38 | | - character(len=20) :: color_code |
| 33 | + integer :: left_w, i, j, parent_idx, current_idx, vis_h |
| 34 | + character(len=256) :: parent_name, current_name |
| 35 | + character(len=256) :: line |
| 39 | 36 | |
| 37 | + ! Calculate layout |
| 40 | 38 | left_w = c * 3 / 10 |
| 41 | | - vis_h = r - 4 ! Visible height (top border + header + footer + bottom border) |
| 42 | | - inner_w = c - 4 ! Inner width (minus border chars and padding) |
| 39 | + vis_h = r - 3 |
| 43 | 40 | |
| 44 | | - ! Clear screen and move to top |
| 45 | | - write(output_unit, '(a)', advance='no') ESC // "[H" // ESC // "[J" |
| 46 | | - flush(output_unit) |
| 47 | | - |
| 48 | | - ! Top border with rounded corners |
| 49 | | - write(output_unit, '(a)') DIM // "╭─" // repeat("─", inner_w) // "─╮" // RESET |
| 41 | + ! Clear screen and hide cursor |
| 42 | + write(output_unit, '(a)', advance='no') ESC // "[?25l" // ESC // "[2J" // ESC // "[H" |
| 50 | 43 | |
| 51 | | - ! Header - path with border |
| 52 | | - write(output_unit, '(a)', advance='no') DIM // "│ " // RESET |
| 53 | | - write(output_unit, '(a)', advance='no') BOLD // trim(current_dir) // RESET |
| 54 | | - write(output_unit, '(a)', advance='no') repeat(" ", max(0, inner_w - len_trim(current_dir))) |
| 55 | | - write(output_unit, '(a)') DIM // " │" // RESET |
| 44 | + ! Header |
| 45 | + write(output_unit, '(a)') BOLD // "FORTRESS" // RESET // " - " // trim(current_dir) |
| 46 | + write(output_unit, '(a)') "" |
| 56 | 47 | |
| 57 | | - ! Files (render based on scroll offsets) with border |
| 48 | + ! Render each line - write complete lines at once |
| 58 | 49 | do i = 1, vis_h |
| 59 | 50 | parent_idx = i + parent_scroll_offset |
| 60 | 51 | current_idx = i + scroll_offset |
| 61 | 52 | |
| 62 | | - ! Left border |
| 63 | | - write(output_unit, '(a)', advance='no') DIM // "│ " // RESET |
| 53 | + ! Start with cursor at beginning of line |
| 54 | + write(output_unit, '(a)', advance='no') ESC // "[1G" |
| 64 | 55 | |
| 65 | | - ! Parent pane (left 30%) |
| 56 | + ! === Parent pane (left 30%) === |
| 66 | 57 | if (parent_idx >= 1 .and. parent_idx <= parent_count) then |
| 67 | | - fname = parent_files(parent_idx) |
| 58 | + ! Clean the filename - remove any control characters |
| 59 | + parent_name = trim(adjustl(parent_files(parent_idx))) |
| 68 | 60 | |
| 69 | | - ! Add trailing slash for directories |
| 70 | | - if (parent_is_dir(parent_idx) .and. parent_files(parent_idx) /= "." .and. parent_files(parent_idx) /= "..") then |
| 71 | | - fname = trim(fname) // "/" |
| 61 | + ! Remove any newlines or carriage returns |
| 62 | + j = scan(parent_name, char(10)//char(13)) |
| 63 | + if (j > 0) then |
| 64 | + parent_name = parent_name(1:j-1) |
| 72 | 65 | end if |
| 73 | 66 | |
| 74 | | - ! Get color for parent file |
| 75 | | - color_code = get_file_color(parent_files(parent_idx), parent_is_dir(parent_idx), parent_is_exec(parent_idx)) |
| 67 | + if (parent_is_dir(parent_idx)) parent_name = trim(parent_name) // "/" |
| 76 | 68 | |
| 77 | | - ! Calculate visual width |
| 78 | | - display_len = min(len_trim(fname), left_w) |
| 69 | + ! Truncate if too long |
| 70 | + if (len_trim(parent_name) > left_w) then |
| 71 | + parent_name = parent_name(1:left_w) |
| 72 | + end if |
| 79 | 73 | |
| 80 | | - ! Highlight if selected in parent pane |
| 74 | + ! Write parent item with color |
| 81 | 75 | if (parent_idx == parent_selected) then |
| 82 | | - write(output_unit, '(a)', advance='no') DIM // BOLD // trim(color_code) // & |
| 83 | | - fname(1:min(len_trim(fname), left_w)) // RESET |
| 76 | + write(output_unit, '(a)', advance='no') DIM // BOLD // BLUE // trim(parent_name) // RESET |
| 84 | 77 | else |
| 85 | | - write(output_unit, '(a)', advance='no') DIM // trim(color_code) // & |
| 86 | | - fname(1:min(len_trim(fname), left_w)) // RESET |
| 78 | + write(output_unit, '(a)', advance='no') DIM // GREY // trim(parent_name) // RESET |
| 87 | 79 | end if |
| 88 | | - write(output_unit, '(a)', advance='no') repeat(" ", max(0, left_w - display_len)) |
| 89 | | - else |
| 90 | | - write(output_unit, '(a)', advance='no') repeat(" ", left_w) |
| 91 | 80 | end if |
| 92 | 81 | |
| 93 | | - ! RESET before separator |
| 94 | | - write(output_unit, '(a)', advance='no') RESET |
| 95 | | - |
| 96 | | - ! Separator |
| 97 | | - write(output_unit, '(a)', advance='no') " │ " |
| 82 | + ! Use explicit cursor positioning for separator - build the position string |
| 83 | + write(line, '(a,i0,a)') ESC // "[", left_w + 1, "G" |
| 84 | + write(output_unit, '(a)', advance='no') trim(line) // " │ " |
| 98 | 85 | |
| 99 | | - ! Current pane (right 70%) |
| 86 | + ! === Current pane (right) === |
| 100 | 87 | if (current_idx >= 1 .and. current_idx <= current_count) then |
| 101 | | - fname = current_files(current_idx) |
| 88 | + ! Clean the filename - remove any control characters |
| 89 | + current_name = trim(adjustl(current_files(current_idx))) |
| 102 | 90 | |
| 103 | | - ! Add trailing slash for directories |
| 104 | | - if (current_is_dir(current_idx) .and. current_files(current_idx) /= "." .and. current_files(current_idx) /= "..") then |
| 105 | | - fname = trim(fname) // "/" |
| 91 | + ! Remove any newlines or carriage returns by finding first occurrence |
| 92 | + j = scan(current_name, char(10)//char(13)) |
| 93 | + if (j > 0) then |
| 94 | + current_name = current_name(1:j-1) |
| 106 | 95 | end if |
| 107 | 96 | |
| 108 | | - ! Get color for current file |
| 109 | | - color_code = get_file_color(current_files(current_idx), current_is_dir(current_idx), current_is_exec(current_idx)) |
| 97 | + if (current_is_dir(current_idx)) current_name = trim(current_name) // "/" |
| 110 | 98 | |
| 111 | | - ! Highlight selected item |
| 112 | 99 | if (current_idx == selected) then |
| 113 | | - write(output_unit, '(a)', advance='no') BOLD // UNDERLINE // trim(color_code) // trim(fname) // RESET |
| 100 | + write(output_unit, '(a)') BOLD // UNDERLINE // WHITE // trim(current_name) // RESET |
| 101 | + else if (current_is_dir(current_idx)) then |
| 102 | + write(output_unit, '(a)') BLUE // trim(current_name) // RESET |
| 103 | + else if (current_is_exec(current_idx)) then |
| 104 | + write(output_unit, '(a)') GREEN // trim(current_name) // RESET |
| 114 | 105 | else |
| 115 | | - write(output_unit, '(a)', advance='no') trim(color_code) // trim(fname) // RESET |
| 106 | + write(output_unit, '(a)') trim(current_name) // RESET |
| 116 | 107 | end if |
| 117 | | - |
| 118 | | - ! Pad to right border |
| 119 | | - write(output_unit, '(a)', advance='no') repeat(" ", max(0, inner_w - left_w - 3 - len_trim(fname))) |
| 120 | 108 | else |
| 121 | | - write(output_unit, '(a)', advance='no') repeat(" ", max(0, inner_w - left_w - 3)) |
| 109 | + write(output_unit, '(a)') "" |
| 122 | 110 | end if |
| 123 | | - |
| 124 | | - ! Right border |
| 125 | | - write(output_unit, '(a)') DIM // " │" // RESET |
| 126 | 111 | end do |
| 127 | 112 | |
| 128 | | - ! Bottom border with ESC hint |
| 129 | | - write(output_unit, '(a)', advance='no') DIM // "╰─" // RESET |
| 130 | | - write(output_unit, '(a)', advance='no') DIM // " ESC to exit " // RESET |
| 131 | | - write(output_unit, '(a)') DIM // repeat("─", max(0, inner_w - 13)) // "─╯" // RESET |
| 113 | + ! Footer |
| 114 | + write(output_unit, '(a)') DIM // "arrows:nav enter:open esc:quit" // RESET |
| 132 | 115 | |
| 116 | + ! Show cursor again |
| 117 | + write(output_unit, '(a)', advance='no') ESC // "[?25h" |
| 133 | 118 | flush(output_unit) |
| 134 | 119 | end subroutine draw_fortress_interface |
| 135 | 120 | |
| 136 | | - function get_file_color(filename, is_dir, is_exec) result(color) |
| 137 | | - character(len=*), intent(in) :: filename |
| 138 | | - logical, intent(in) :: is_dir, is_exec |
| 139 | | - character(len=20) :: color |
| 140 | | - |
| 141 | | - ! Directories: Blue and bold |
| 142 | | - if (is_dir) then |
| 143 | | - color = BOLD // BLUE |
| 144 | | - ! Dotfiles: Grey |
| 145 | | - else if (len_trim(filename) > 0) then |
| 146 | | - if (filename(1:1) == '.') then |
| 147 | | - color = GREY |
| 148 | | - ! Executable files: Green |
| 149 | | - else if (is_exec) then |
| 150 | | - color = GREEN |
| 151 | | - ! All other files: White |
| 152 | | - else |
| 153 | | - color = WHITE |
| 154 | | - end if |
| 155 | | - else |
| 156 | | - color = WHITE |
| 157 | | - end if |
| 158 | | - end function get_file_color |
| 159 | | - |
| 160 | 121 | end module fortress_display_module |