@@ -13,6 +13,7 @@ module command_palette_module |
| 13 | 13 | |
| 14 | 14 | integer, parameter :: MAX_COMMANDS = 100 |
| 15 | 15 | integer, parameter :: MAX_VISIBLE = 10 |
| 16 | + integer, parameter :: PALETTE_WIDTH = 60 ! Fixed width for centered palette |
| 16 | 17 | |
| 17 | 18 | type :: command_t |
| 18 | 19 | character(len=:), allocatable :: name |
@@ -291,37 +292,65 @@ contains |
| 291 | 292 | end select |
| 292 | 293 | end subroutine command_palette_handle_key |
| 293 | 294 | |
| 294 | | - subroutine render_command_palette(palette, screen_rows) |
| 295 | + subroutine render_command_palette(palette, screen_rows, screen_cols) |
| 295 | 296 | type(command_palette_t), intent(in) :: palette |
| 296 | | - integer, intent(in) :: screen_rows |
| 297 | | - integer :: i, visible_start, visible_end, row |
| 297 | + integer, intent(in) :: screen_rows, screen_cols |
| 298 | + integer :: i, visible_start, visible_end, row, start_col, start_row |
| 299 | + integer :: content_width, display_width |
| 298 | 300 | character(len=256) :: line, category_tag |
| 299 | 301 | type(command_t) :: cmd |
| 300 | | - |
| 301 | | - ! Clear and draw header |
| 302 | | - call terminal_move_cursor(screen_rows - MAX_VISIBLE - 2, 1) |
| 303 | | - call terminal_write(repeat(' ', 80)) |
| 304 | | - call terminal_move_cursor(screen_rows - MAX_VISIBLE - 2, 1) |
| 305 | | - call terminal_write('Command Palette (type to search, Esc to cancel)') |
| 306 | | - |
| 307 | | - ! Draw search query |
| 308 | | - call terminal_move_cursor(screen_rows - MAX_VISIBLE - 1, 1) |
| 309 | | - call terminal_write(repeat(' ', 80)) |
| 310 | | - call terminal_move_cursor(screen_rows - MAX_VISIBLE - 1, 1) |
| 311 | | - call terminal_write('> ' // trim(palette%search_query)) |
| 302 | + character(len=:), allocatable :: border_top, border_bottom |
| 303 | + ! ANSI escape codes |
| 304 | + character(len=*), parameter :: ESC = char(27) |
| 305 | + character(len=*), parameter :: CYAN = ESC // '[36m' |
| 306 | + character(len=*), parameter :: YELLOW = ESC // '[33m' |
| 307 | + character(len=*), parameter :: INVERSE = ESC // '[7m' |
| 308 | + character(len=*), parameter :: RESET = ESC // '[0m' |
| 309 | + |
| 310 | + ! Calculate centering - top-center like VSCode |
| 311 | + content_width = min(PALETTE_WIDTH, screen_cols - 4) |
| 312 | + start_col = max(1, (screen_cols - content_width) / 2) |
| 313 | + start_row = 2 ! Start near top (below tab bar if present) |
| 314 | + |
| 315 | + ! Build border strings |
| 316 | + border_top = '┌' // repeat('─', content_width - 2) // '┐' |
| 317 | + border_bottom = '└' // repeat('─', content_width - 2) // '┘' |
| 318 | + |
| 319 | + ! Draw top border |
| 320 | + call terminal_move_cursor(start_row, start_col) |
| 321 | + call terminal_write(border_top) |
| 322 | + |
| 323 | + ! Draw header line with cyan title |
| 324 | + row = start_row + 1 |
| 325 | + call terminal_move_cursor(row, start_col) |
| 326 | + call terminal_write('│' // CYAN // ' Command Palette' // RESET) |
| 327 | + display_width = 17 ! " Command Palette" length |
| 328 | + call terminal_write(repeat(' ', content_width - display_width - 2) // '│') |
| 329 | + |
| 330 | + ! Draw search query line with yellow prompt |
| 331 | + row = row + 1 |
| 332 | + call terminal_move_cursor(row, start_col) |
| 333 | + call terminal_write('│' // YELLOW // ' > ' // RESET) |
| 334 | + call terminal_write(trim(palette%search_query)) |
| 335 | + display_width = 3 + len_trim(palette%search_query) |
| 336 | + call terminal_write(repeat(' ', content_width - display_width - 2) // '│') |
| 337 | + |
| 338 | + ! Draw separator |
| 339 | + row = row + 1 |
| 340 | + call terminal_move_cursor(row, start_col) |
| 341 | + call terminal_write('├' // repeat('─', content_width - 2) // '┤') |
| 312 | 342 | |
| 313 | 343 | ! Calculate visible range |
| 314 | 344 | visible_start = palette%scroll_offset + 1 |
| 315 | 345 | visible_end = min(visible_start + MAX_VISIBLE - 1, palette%num_filtered) |
| 316 | 346 | |
| 317 | 347 | ! Draw commands |
| 318 | | - row = screen_rows - MAX_VISIBLE |
| 319 | 348 | do i = visible_start, visible_end |
| 349 | + row = row + 1 |
| 320 | 350 | cmd = palette%filtered_commands(i) |
| 321 | 351 | |
| 322 | | - call terminal_move_cursor(row, 1) |
| 323 | | - call terminal_write(repeat(' ', 80)) |
| 324 | | - call terminal_move_cursor(row, 1) |
| 352 | + call terminal_move_cursor(row, start_col) |
| 353 | + call terminal_write('│') |
| 325 | 354 | |
| 326 | 355 | ! Build line with category, name, and shortcut |
| 327 | 356 | if (len_trim(cmd%category) > 0) then |
@@ -330,37 +359,46 @@ contains |
| 330 | 359 | category_tag = '' |
| 331 | 360 | end if |
| 332 | 361 | |
| 333 | | - if (i == palette%selected_index) then |
| 334 | | - ! Highlight selected item |
| 335 | | - write(line, '(A,A,A)') '> ', trim(category_tag), trim(cmd%name) |
| 336 | | - else |
| 337 | | - write(line, '(A,A,A)') ' ', trim(category_tag), trim(cmd%name) |
| 338 | | - end if |
| 362 | + write(line, '(A,A,A)') ' ', trim(category_tag), trim(cmd%name) |
| 339 | 363 | |
| 340 | 364 | ! Add shortcut if available |
| 341 | 365 | if (len_trim(cmd%shortcut) > 0) then |
| 342 | | - write(line, '(A,A,A)') trim(line), ' (', trim(cmd%shortcut) // ')' |
| 366 | + write(line, '(A,A,A)') trim(line), ' ', trim(cmd%shortcut) |
| 343 | 367 | end if |
| 344 | 368 | |
| 345 | | - call terminal_write(trim(line)) |
| 346 | | - row = row + 1 |
| 369 | + ! Truncate if too long |
| 370 | + display_width = min(len_trim(line), content_width - 3) |
| 371 | + |
| 372 | + if (i == palette%selected_index) then |
| 373 | + ! Highlight selected item with inverse colors |
| 374 | + call terminal_write(INVERSE // line(1:display_width)) |
| 375 | + call terminal_write(repeat(' ', content_width - display_width - 2) // RESET // '│') |
| 376 | + else |
| 377 | + call terminal_write(line(1:display_width)) |
| 378 | + call terminal_write(repeat(' ', content_width - display_width - 2) // '│') |
| 379 | + end if |
| 347 | 380 | end do |
| 348 | 381 | |
| 349 | | - ! Clear remaining lines |
| 350 | | - do while (row <= screen_rows) |
| 351 | | - call terminal_move_cursor(row, 1) |
| 352 | | - call terminal_write(repeat(' ', 80)) |
| 382 | + ! Fill remaining visible slots with empty rows |
| 383 | + do i = visible_end + 1, visible_start + MAX_VISIBLE - 1 |
| 353 | 384 | row = row + 1 |
| 385 | + call terminal_move_cursor(row, start_col) |
| 386 | + call terminal_write('│' // repeat(' ', content_width - 2) // '│') |
| 354 | 387 | end do |
| 355 | 388 | |
| 356 | | - ! Position cursor at end of search query |
| 357 | | - call terminal_move_cursor(screen_rows - MAX_VISIBLE - 1, 3 + palette%search_pos) |
| 389 | + ! Draw bottom border |
| 390 | + row = row + 1 |
| 391 | + call terminal_move_cursor(row, start_col) |
| 392 | + call terminal_write(border_bottom) |
| 393 | + |
| 394 | + ! Position cursor at end of search query (inside the box) |
| 395 | + call terminal_move_cursor(start_row + 2, start_col + 4 + palette%search_pos) |
| 358 | 396 | end subroutine render_command_palette |
| 359 | 397 | |
| 360 | | - function show_command_palette_interactive(palette, screen_rows) result(selected_cmd_id) |
| 398 | + function show_command_palette_interactive(palette, screen_rows, screen_cols) result(selected_cmd_id) |
| 361 | 399 | use input_handler_module, only: get_key_input |
| 362 | 400 | type(command_palette_t), intent(inout) :: palette |
| 363 | | - integer, intent(in) :: screen_rows |
| 401 | + integer, intent(in) :: screen_rows, screen_cols |
| 364 | 402 | character(len=:), allocatable :: selected_cmd_id |
| 365 | 403 | character(len=32) :: key_input |
| 366 | 404 | integer :: ch, status |
@@ -368,7 +406,7 @@ contains |
| 368 | 406 | type(command_t) :: cmd |
| 369 | 407 | |
| 370 | 408 | call show_command_palette(palette) |
| 371 | | - call render_command_palette(palette, screen_rows) |
| 409 | + call render_command_palette(palette, screen_rows, screen_cols) |
| 372 | 410 | |
| 373 | 411 | do |
| 374 | 412 | call get_key_input(key_input, status) |
@@ -406,7 +444,7 @@ contains |
| 406 | 444 | end if |
| 407 | 445 | end if |
| 408 | 446 | |
| 409 | | - call render_command_palette(palette, screen_rows) |
| 447 | + call render_command_palette(palette, screen_rows, screen_cols) |
| 410 | 448 | end do |
| 411 | 449 | end function show_command_palette_interactive |
| 412 | 450 | |