Recent

Author Topic: A small file manager(Raylib)  (Read 673 times)

Guva

  • Full Member
  • ***
  • Posts: 178
  • 🌈 ZX-Spectrum !!!
A small file manager(Raylib)
« on: April 03, 2025, 06:34:45 pm »
An example of a file manager used by raylib. It also shows you how to download and display a language other than English.

Code: Pascal  [Select][+][-]
  1. program filebrowser;
  2. {$WARN 5044 off : Symbol "$1" is not portable}
  3. uses
  4.   SysUtils, Raylib, math;
  5.  
  6. const
  7.   SCREEN_WIDTH = 800;
  8.   SCREEN_HEIGHT = 600;
  9.   FONT_SIZE = 20;
  10.   ITEM_HEIGHT = 30;
  11.   MAX_VISIBLE_ITEMS = 15;
  12.   MARGIN = 10;
  13.   SCROLL_SPEED = 1;
  14.  
  15. var
  16.   Files: array of string;
  17.   ScrollOffset: Integer = 0;
  18.   SelectedItem: Integer = 0;
  19.   CurrentDir, ParentDir: string;
  20.   FileListArea: TRectangle;
  21.   RussianFont: TFont;
  22.  
  23. // Функция для загрузки шрифта с поддержкой кириллицы
  24. function LoadRussianFont: TFont;
  25. var
  26.   i: Integer;
  27.   cp: array[0..511] of Integer;
  28. begin
  29.   // Инициализируем массив кодовых точек
  30.   for i := 0 to 95 do
  31.     cp[i] := 32 + i;  // Базовые ASCII символы
  32.  
  33.   // Добавляем кириллические символы (диапазон 0x0400-0x04FF)
  34.   for i := 0 to 255 do
  35.     cp[96 + i] := $400 + i;
  36.  
  37.   // Загружаем шрифт с указанными кодовыми точками
  38.   {$IFDEF UNIX}
  39.   Result := LoadFontEx('Yulong-Regular.otf', FONT_SIZE, @cp[0], Length(cp));
  40.   {$ELSE}
  41.   Result := LoadFontEx('C:\Windows\Fonts\arial.ttf', FONT_SIZE, @cp[0], Length(cp));
  42.   {$ENDIF}
  43.  
  44.   if Result.texture.id = 0 then // Если шрифт не загрузился, используем стандартный
  45.     Result := GetFontDefault();
  46. end;
  47.  
  48. procedure HandleMouseWheel;
  49. var
  50.   WheelMove: Single;
  51. begin
  52.   WheelMove := GetMouseWheelMove();
  53.  
  54.   if WheelMove <> 0 then
  55.   begin
  56.     if WheelMove > 0 then
  57.       SelectedItem := Max(SelectedItem - SCROLL_SPEED, 0)
  58.     else if WheelMove < 0 then
  59.       SelectedItem := Min(SelectedItem + SCROLL_SPEED, High(Files));
  60.   end;
  61. end;
  62.  
  63. function GetUserHomeDir: string;
  64. begin
  65.   {$IFDEF UNIX}
  66.   Result := GetEnvironmentVariable('HOME');
  67.   {$ELSE}
  68.   Result := GetEnvironmentVariable('USERPROFILE');
  69.   {$ENDIF}
  70.   if Result = '' then
  71.     Result := GetCurrentDir;
  72. end;
  73.  
  74. procedure LoadDirectory(const Path: string);
  75. var
  76.   SearchRec: TSearchRec;
  77.   FileCount: Integer = 0;
  78.   IsDirectory: Boolean;
  79. begin
  80.   SetLength(Files, 0);
  81.  
  82.   if Path <> ExtractFileDrive(Path) then
  83.   begin
  84.     SetLength(Files, 1);
  85.     Files[0] := '..' + PathDelim;
  86.     FileCount := 1;
  87.   end;
  88.  
  89.   if FindFirst(IncludeTrailingPathDelimiter(Path) + '*', faDirectory, SearchRec) = 0 then
  90.   begin
  91.     repeat
  92.       IsDirectory := (SearchRec.Attr and faDirectory) <> 0;
  93.       if IsDirectory and (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
  94.       begin
  95.         SetLength(Files, FileCount + 1);
  96.         Files[FileCount] := SearchRec.Name + PathDelim;
  97.         Inc(FileCount);
  98.       end;
  99.     until FindNext(SearchRec) <> 0;
  100.     FindClose(SearchRec);
  101.   end;
  102.  
  103.   if FindFirst(IncludeTrailingPathDelimiter(Path) + '*', faAnyFile and not (faDirectory or faHidden), SearchRec) = 0 then
  104.   begin
  105.     repeat
  106.       IsDirectory := (SearchRec.Attr and faDirectory) <> 0;
  107.       if not IsDirectory then
  108.       begin
  109.         SetLength(Files, FileCount + 1);
  110.         Files[FileCount] := SearchRec.Name;
  111.         Inc(FileCount);
  112.       end;
  113.     until FindNext(SearchRec) <> 0;
  114.     FindClose(SearchRec);
  115.   end;
  116.  
  117.   CurrentDir := IncludeTrailingPathDelimiter(Path);
  118.   ScrollOffset := 0;
  119.   SelectedItem := 0;
  120. end;
  121.  
  122. procedure DrawInterface;
  123. var
  124.   i, YPos: Integer;
  125.   FileName: string;
  126.   MaxVisibleItems: Integer;
  127.   BGColor: TColor;
  128. begin
  129.   DrawTextEx(RussianFont, 'Файловый браузер',
  130.     Vector2Create(MARGIN, MARGIN), FONT_SIZE , 1, DARKGRAY);
  131.  
  132.   // Текущая директория
  133.   DrawTextEx(RussianFont, PChar('Директория: ' + CurrentDir),
  134.     Vector2Create(MARGIN, MARGIN + 40), FONT_SIZE, 1, GRAY);
  135.  
  136.   FileListArea := RectangleCreate(MARGIN, MARGIN + 80, SCREEN_WIDTH - 2*MARGIN, SCREEN_HEIGHT - 160);
  137.  
  138.  // DrawRectangleRec(FileListArea, LIGHTGRAY);
  139.   DrawRectangleLinesEx(FileListArea, 1, GRAY);
  140.  
  141.   MaxVisibleItems := Trunc(FileListArea.height / ITEM_HEIGHT);
  142.  
  143.   if SelectedItem < ScrollOffset then
  144.     ScrollOffset := SelectedItem;
  145.   if SelectedItem >= ScrollOffset + MaxVisibleItems then
  146.     ScrollOffset := SelectedItem - MaxVisibleItems + 1;
  147.  
  148.   for i := 0 to Min(MaxVisibleItems - 1, High(Files) - ScrollOffset) do
  149.   begin
  150.     YPos := Round(FileListArea.y) + i * ITEM_HEIGHT;
  151.     FileName := Files[ScrollOffset + i];
  152.  
  153.     // Чередование цветов фона (как в Total Commander)
  154.     if (ScrollOffset + i) mod 2 = 0 then
  155.       BGColor := Fade(LIGHTGRAY, 0.5)    // Первый цвет фона
  156.     else
  157.       BGColor := Fade(LIGHTGRAY, 0.3) ;  // Второй цвет фона (светло-серый)   ;
  158.  
  159.     if (ScrollOffset + i) = SelectedItem then
  160.     begin
  161.       DrawRectangle(Round(FileListArea.x), YPos, Round(FileListArea.width), ITEM_HEIGHT, BLUE);
  162.       if (FileName <> '') and (FileName[Length(FileName)] = PathDelim) then
  163.         DrawTextEx(RussianFont, PChar('['+ExcludeTrailingBackslash(FileName)+']'),
  164.           Vector2Create(Round(FileListArea.x) + 5, YPos + 5), FONT_SIZE, 1, WHITE)
  165.       else
  166.         DrawTextEx(RussianFont, PChar(FileName),
  167.           Vector2Create(Round(FileListArea.x) + 5, YPos + 5), FONT_SIZE, 1, WHITE)
  168.     end
  169.     else
  170.     begin
  171.       // Рисуем фон с чередующимся цветом
  172.       DrawRectangle(Round(FileListArea.x), YPos, Round(FileListArea.width), ITEM_HEIGHT, BGColor);
  173.  
  174.       if (FileName <> '') and (FileName[Length(FileName)] = PathDelim) then
  175.       begin
  176.         if FileName = '..' + PathDelim then
  177.           DrawTextEx(RussianFont, PChar('['+ExcludeTrailingBackslash(FileName)+']'),
  178.             Vector2Create(Round(FileListArea.x) + 5, YPos + 5), FONT_SIZE, 1, DARKBLUE)
  179.         else
  180.           DrawTextEx(RussianFont, PChar('['+ExcludeTrailingBackslash(FileName)+']'),
  181.             Vector2Create(Round(FileListArea.x) + 5, YPos + 5), FONT_SIZE, 1, DARKGREEN);
  182.       end
  183.       else
  184.         DrawTextEx(RussianFont, PChar(FileName),
  185.           Vector2Create(Round(FileListArea.x) + 5, YPos + 5), FONT_SIZE, 1, DARKGRAY);
  186.     end;
  187.   end;
  188.  
  189.   // Информация о файлах
  190.   if Length(Files) > 0 then
  191.     DrawTextEx(RussianFont, PChar(Format('%d/%d', [SelectedItem + 1, Length(Files)])),
  192.       Vector2Create(SCREEN_WIDTH - 50, SCREEN_HEIGHT - 30), FONT_SIZE, 1, LIGHTGRAY);
  193.  
  194.   DrawTextEx(RussianFont, 'Стрелки:Навигация  Enter:Открыть  Backspace:Назад',
  195.     Vector2Create(MARGIN, SCREEN_HEIGHT - 30), FONT_SIZE , 1, GRAY);
  196. end;
  197.  
  198. begin
  199.   InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, 'Файловый браузер');
  200.   SetTargetFPS(60);
  201.  
  202.   // Загружаем шрифт с поддержкой кириллицы
  203.   RussianFont := LoadRussianFont;
  204.   SetTextureFilter(RussianFont.texture, TEXTURE_FILTER_TRILINEAR);
  205.  
  206.   // Загрузка начальной директории
  207.   LoadDirectory(GetUserHomeDir);
  208.  
  209.   while not WindowShouldClose do
  210.   begin
  211.     HandleMouseWheel;
  212.  
  213.     if IsKeyPressed(KEY_DOWN) and (SelectedItem < High(Files)) then
  214.       Inc(SelectedItem);
  215.  
  216.     if IsKeyPressed(KEY_UP) and (SelectedItem > 0) then
  217.       Dec(SelectedItem);
  218.  
  219.     if IsKeyPressed(KEY_PAGE_DOWN) then
  220.       SelectedItem := Min(SelectedItem + MAX_VISIBLE_ITEMS, High(Files));
  221.  
  222.     if IsKeyPressed(KEY_PAGE_UP) then
  223.       SelectedItem := Max(SelectedItem - MAX_VISIBLE_ITEMS, 0);
  224.  
  225.     if IsKeyPressed(KEY_ENTER) and (Length(Files) > 0) then
  226.     begin
  227.       if (Files[SelectedItem] <> '') and (Files[SelectedItem][Length(Files[SelectedItem])] = PathDelim) then
  228.       begin
  229.         if Files[SelectedItem] = '..' + PathDelim then
  230.         begin
  231.           ParentDir := ExtractFileDir(ExcludeTrailingPathDelimiter(CurrentDir));
  232.           if ParentDir <> CurrentDir then
  233.             LoadDirectory(ParentDir);
  234.         end
  235.         else
  236.           LoadDirectory(CurrentDir + Files[SelectedItem]);
  237.       end
  238.       else
  239.         TraceLog(LOG_INFO, PChar('Selected: ' + CurrentDir + Files[SelectedItem]));
  240.     end;
  241.  
  242.     if IsKeyPressed(KEY_BACKSPACE) then
  243.     begin
  244.       ParentDir := ExtractFileDir(ExcludeTrailingPathDelimiter(CurrentDir));
  245.       if ParentDir <> CurrentDir then
  246.         LoadDirectory(ParentDir);
  247.     end;
  248.  
  249.     if IsKeyPressed(KEY_F5) then
  250.       LoadDirectory(CurrentDir);
  251.  
  252.     BeginDrawing();
  253.       ClearBackground(RAYWHITE);
  254.       DrawInterface;
  255.     EndDrawing();
  256.   end;
  257.  
  258.   // Освобождаем шрифт
  259.   UnloadFont(RussianFont);
  260.   CloseWindow;
  261. end.
  262.  

Code: Pascal  [Select][+][-]
  1. // Функция для загрузки шрифта с поддержкой множества языков
  2. function LoadUnicodeFont: TFont;
  3. var
  4.   cp: array of Integer;
  5.   i, count: Integer;
  6.  
  7.   procedure AddRange(start, stop: Integer);
  8.   begin
  9.     while start <= stop do
  10.     begin
  11.       if count >= Length(cp) then
  12.         SetLength(cp, Length(cp) + 256);
  13.       cp[count] := start;
  14.       Inc(count);
  15.       Inc(start);
  16.     end;
  17.   end;
  18.  
  19. begin
  20.   // Создаем массив кодовых точек
  21.   SetLength(cp, 4096);
  22.   count := 0;
  23.  
  24.   // Базовые символы ASCII
  25.   AddRange(32, 126);
  26.  
  27.   // Основные европейские языки
  28.   AddRange($C0, $17F);  // Latin-1 Supplement + Latin Extended-A
  29.   AddRange($180, $24F); // Latin Extended-B
  30.   AddRange($370, $3FF); // Greek and Coptic
  31.   AddRange($400, $4FF); // Cyrillic
  32.   AddRange($500, $52F); // Cyrillic Supplement
  33.   AddRange($1E00, $1EFF); // Latin Extended Additional
  34.  
  35.   // Специальные символы для конкретных языков
  36.   // Африканские языки
  37.   AddRange($1E00, $1EFF); // Latin Extended Additional (Fon, Ewe и др.)
  38.   AddRange($2C60, $2C7F); // Latin Extended-C (Aja, Basaa)
  39.  
  40.   // Азиатские языки (кириллица и латиница)
  41.   AddRange($A640, $A69F); // Cyrillic Extended-B (Kazakh, Chuvash)
  42.  
  43.   // Дополнительные символы
  44.   AddRange($300, $36F);  // Combining Diacritical Marks
  45.   AddRange($1DC0, $1DFF); // Combining Diacritical Marks Supplement
  46.  
  47.   // Уменьшаем массив до фактического размера
  48.   SetLength(cp, count);
  49.  
  50.   // Пытаемся загрузить универсальный шрифт
  51.   {$IFDEF UNIX}
  52.   // Попробуем несколько шрифтов для Linux
  53.   Result := LoadFontEx('Yulong-Regular.otf', FONT_SIZE, @cp[0], Length(cp));
  54.   if Result.texture.id = 0 then
  55.     Result := LoadFontEx('/usr/share/fonts/truetype/freefont/FreeSans.ttf', FONT_SIZE, @cp[0], Length(cp));
  56.   {$ELSE}
  57.   // Для Windows используем Arial Unicode MS или обычный Arial
  58.   Result := LoadFontEx('C:\Windows\Fonts\arialuni.ttf', FONT_SIZE, @cp[0], Length(cp));
  59.   if Result.texture.id = 0 then
  60.     Result := LoadFontEx('C:\Windows\Fonts\arial.ttf', FONT_SIZE, @cp[0], Length(cp));
  61.   {$ENDIF}
  62.  
  63.   if Result.texture.id = 0 then // Если шрифт не загрузился, используем стандартный
  64.     Result := GetFontDefault();
  65. end;    
  66.  
« Last Edit: April 03, 2025, 08:20:04 pm by Guva »

Guva

  • Full Member
  • ***
  • Posts: 178
  • 🌈 ZX-Spectrum !!!
Re: A small file manager(Raylib)
« Reply #1 on: April 04, 2025, 05:32:21 am »
implementation in c lang. I don't know why, but it might be useful to someone. :)
Code: C  [Select][+][-]
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <dirent.h>
  5. #include <sys/stat.h>
  6. #include <unistd.h>
  7. #include <raylib.h>
  8.  
  9. #define SCREEN_WIDTH 800
  10. #define SCREEN_HEIGHT 600
  11. #define FONT_SIZE 20
  12. #define ITEM_HEIGHT 30
  13. #define MAX_VISIBLE_ITEMS 15
  14. #define MARGIN 10
  15. #define SCROLL_SPEED 1
  16.  
  17. typedef struct {
  18.     char **items;
  19.     int count;
  20. } FileList;
  21.  
  22. FileList files = {0};
  23. int scroll_offset = 0;
  24. int selected_item = 0;
  25. char current_dir[1024] = {0};
  26. char parent_dir[1024] = {0};
  27. Rectangle file_list_area;
  28. Font russian_font;
  29.  
  30. // Функция для загрузки шрифта с поддержкой множества языков
  31. Font LoadUnicodeFont() {
  32.     int *codepoints = NULL;
  33.     int count = 0;
  34.     int capacity = 0;
  35.  
  36.     // Функция для добавления диапазона символов
  37.     #define ADD_RANGE(start, end) \
  38.         for (int i = start; i <= end; i++) { \
  39.             if (count >= capacity) { \
  40.                 capacity = (capacity == 0) ? 256 : capacity * 2; \
  41.                 codepoints = realloc(codepoints, capacity * sizeof(int)); \
  42.             } \
  43.             codepoints[count++] = i; \
  44.         }
  45.  
  46.     // Базовые символы ASCII
  47.     ADD_RANGE(32, 126);
  48.  
  49.     // Основные европейские языки
  50.     ADD_RANGE(0xC0, 0x17F);  // Latin-1 Supplement + Latin Extended-A
  51.     ADD_RANGE(0x180, 0x24F); // Latin Extended-B
  52.     ADD_RANGE(0x370, 0x3FF); // Greek and Coptic
  53.     ADD_RANGE(0x400, 0x4FF); // Cyrillic
  54.     ADD_RANGE(0x500, 0x52F); // Cyrillic Supplement
  55.     ADD_RANGE(0x1E00, 0x1EFF); // Latin Extended Additional
  56.  
  57.     // Специальные символы для конкретных языков
  58.     ADD_RANGE(0x1E00, 0x1EFF); // Latin Extended Additional (Fon, Ewe и др.)
  59.     ADD_RANGE(0x2C60, 0x2C7F); // Latin Extended-C (Aja, Basaa)
  60.  
  61.     // Азиатские языки (кириллица и латиница)
  62.     ADD_RANGE(0xA640, 0xA69F); // Cyrillic Extended-B (Kazakh, Chuvash)
  63.  
  64.     // Дополнительные символы
  65.     ADD_RANGE(0x300, 0x36F);  // Combining Diacritical Marks
  66.     ADD_RANGE(0x1DC0, 0x1DFF); // Combining Diacritical Marks Supplement
  67.  
  68.     #undef ADD_RANGE
  69.  
  70.     Font font = {0};
  71.  
  72.     // Пытаемся загрузить универсальный шрифт
  73.     #ifdef __linux__
  74.     font = LoadFontEx("Yulong-Regular.otf", FONT_SIZE, codepoints, count);
  75.     if (font.texture.id == 0) {
  76.         font = LoadFontEx("/usr/share/fonts/truetype/freefont/FreeSans.ttf", FONT_SIZE, codepoints, count);
  77.     }
  78.     #else
  79.     font = LoadFontEx("C:\\Windows\\Fonts\\arialuni.ttf", FONT_SIZE, codepoints, count);
  80.     if (font.texture.id == 0) {
  81.         font = LoadFontEx("C:\\Windows\\Fonts\\arial.ttf", FONT_SIZE, codepoints, count);
  82.     }
  83.     #endif
  84.  
  85.     free(codepoints);
  86.  
  87.     if (font.texture.id == 0) {
  88.         font = GetFontDefault();
  89.     }
  90.  
  91.     return font;
  92. }
  93.  
  94. void HandleMouseWheel() {
  95.     float wheel_move = GetMouseWheelMove();
  96.  
  97.     if (wheel_move != 0) {
  98.         if (wheel_move > 0) {
  99.             selected_item = (selected_item - SCROLL_SPEED > 0) ? selected_item - SCROLL_SPEED : 0;
  100.         } else if (wheel_move < 0) {
  101.             selected_item = (selected_item + SCROLL_SPEED < files.count - 1) ? selected_item + SCROLL_SPEED : files.count - 1;
  102.         }
  103.     }
  104. }
  105.  
  106. const char* GetUserHomeDir() {
  107.     const char *home = getenv("HOME");
  108.     if (home == NULL) {
  109.         home = getenv("USERPROFILE");
  110.     }
  111.     if (home == NULL) {
  112.         home = ".";
  113.     }
  114.     return home;
  115. }
  116.  
  117. void FreeFileList() {
  118.     for (int i = 0; i < files.count; i++) {
  119.         free(files.items[i]);
  120.     }
  121.     free(files.items);
  122.     files.items = NULL;
  123.     files.count = 0;
  124. }
  125.  
  126. void LoadDirectory(const char *path) {
  127.     FreeFileList();
  128.  
  129.     // Сохраняем текущую директорию
  130.     strncpy(current_dir, path, sizeof(current_dir) - 1);
  131.     current_dir[sizeof(current_dir) - 1] = '\0';
  132.  
  133.     // Добавляем родительскую директорию, если это не корень
  134.     if (strcmp(path, "/") != 0 && strcmp(path, "C:\\") != 0) {
  135.         files.items = malloc(sizeof(char*));
  136.         files.items[0] = strdup("../");
  137.         files.count = 1;
  138.     }
  139.  
  140.     DIR *dir = opendir(path);
  141.     if (dir != NULL) {
  142.         struct dirent *entry;
  143.         while ((entry = readdir(dir)) != NULL) {
  144.             // Пропускаем специальные директории
  145.             if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
  146.                 continue;
  147.             }
  148.  
  149.             // Проверяем, является ли это директорией
  150.             char full_path[1024];
  151.             snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name);
  152.  
  153.             struct stat statbuf;
  154.             if (stat(full_path, &statbuf) == 0) {
  155.                 if (S_ISDIR(statbuf.st_mode)) {
  156.                     // Это директория
  157.                     char *item = malloc(strlen(entry->d_name) + 2);
  158.                     sprintf(item, "%s/", entry->d_name);
  159.                     files.items = realloc(files.items, (files.count + 1) * sizeof(char*));
  160.                     files.items[files.count++] = item;
  161.                 }
  162.             }
  163.         }
  164.         closedir(dir);
  165.     }
  166.  
  167.     // Теперь добавляем файлы
  168.     dir = opendir(path);
  169.     if (dir != NULL) {
  170.         struct dirent *entry;
  171.         while ((entry = readdir(dir)) != NULL) {
  172.             // Пропускаем специальные директории
  173.             if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
  174.                 continue;
  175.             }
  176.  
  177.             // Проверяем, является ли это файлом
  178.             char full_path[1024];
  179.             snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name);
  180.  
  181.             struct stat statbuf;
  182.             if (stat(full_path, &statbuf) == 0) {
  183.                 if (!S_ISDIR(statbuf.st_mode)) {
  184.                     // Это файл
  185.                     files.items = realloc(files.items, (files.count + 1) * sizeof(char*));
  186.                     files.items[files.count++] = strdup(entry->d_name);
  187.                 }
  188.             }
  189.         }
  190.         closedir(dir);
  191.     }
  192.  
  193.     scroll_offset = 0;
  194.     selected_item = 0;
  195. }
  196.  
  197. void DrawInterface() {
  198.     // Заголовок
  199.     DrawTextEx(russian_font, "Файловый браузер", (Vector2){MARGIN, MARGIN}, FONT_SIZE, 1, DARKGRAY);
  200.  
  201.     // Текущая директория
  202.     char dir_text[2048];
  203.     snprintf(dir_text, sizeof(dir_text), "Директория: %s", current_dir);
  204.     DrawTextEx(russian_font, dir_text, (Vector2){MARGIN, MARGIN + 40}, FONT_SIZE, 1, GRAY);
  205.  
  206.     // Область списка файлов
  207.     file_list_area = (Rectangle){MARGIN, MARGIN + 80, SCREEN_WIDTH - 2*MARGIN, SCREEN_HEIGHT - 160};
  208.     DrawRectangleLinesEx(file_list_area, 1, GRAY);
  209.  
  210.     int max_visible_items = (int)(file_list_area.height / ITEM_HEIGHT);
  211.  
  212.     // Корректируем scroll offset
  213.     if (selected_item < scroll_offset) {
  214.         scroll_offset = selected_item;
  215.     }
  216.     if (selected_item >= scroll_offset + max_visible_items) {
  217.         scroll_offset = selected_item - max_visible_items + 1;
  218.     }
  219.  
  220.     // Рисуем видимые элементы
  221.     for (int i = 0; i < max_visible_items && (scroll_offset + i) < files.count; i++) {
  222.         int y_pos = (int)file_list_area.y + i * ITEM_HEIGHT;
  223.         const char *filename = files.items[scroll_offset + i];
  224.  
  225.         // Чередование цветов фона
  226.         Color bg_color = ((scroll_offset + i) % 2 == 0) ?
  227.             Fade(LIGHTGRAY, 0.5f) : Fade(LIGHTGRAY, 0.3f);
  228.  
  229.         if ((scroll_offset + i) == selected_item) {
  230.             // Выбранный элемент
  231.             DrawRectangle((int)file_list_area.x, y_pos, (int)file_list_area.width, ITEM_HEIGHT, BLUE);
  232.  
  233.             char display_text[1024];
  234.             if (filename[strlen(filename)-1] == '/') {
  235.                 if (strcmp(filename, "../") == 0) {
  236.                     snprintf(display_text, sizeof(display_text), "[..]");
  237.                 } else {
  238.                     snprintf(display_text, sizeof(display_text), "[%.*s]", (int)strlen(filename)-1, filename);
  239.                 }
  240.                 DrawTextEx(russian_font, display_text, (Vector2){(int)file_list_area.x + 5, y_pos + 5}, FONT_SIZE, 1, WHITE);
  241.             } else {
  242.                 DrawTextEx(russian_font, filename, (Vector2){(int)file_list_area.x + 5, y_pos + 5}, FONT_SIZE, 1, WHITE);
  243.             }
  244.         } else {
  245.             // Невыбранный элемент
  246.             DrawRectangle((int)file_list_area.x, y_pos, (int)file_list_area.width, ITEM_HEIGHT, bg_color);
  247.  
  248.             char display_text[1024];
  249.             if (filename[strlen(filename)-1] == '/') {
  250.                 if (strcmp(filename, "../") == 0) {
  251.                     snprintf(display_text, sizeof(display_text), "[..]");
  252.                     DrawTextEx(russian_font, display_text, (Vector2){(int)file_list_area.x + 5, y_pos + 5}, FONT_SIZE, 1, DARKBLUE);
  253.                 } else {
  254.                     snprintf(display_text, sizeof(display_text), "[%.*s]", (int)strlen(filename)-1, filename);
  255.                     DrawTextEx(russian_font, display_text, (Vector2){(int)file_list_area.x + 5, y_pos + 5}, FONT_SIZE, 1, DARKGREEN);
  256.                 }
  257.             } else {
  258.                 DrawTextEx(russian_font, filename, (Vector2){(int)file_list_area.x + 5, y_pos + 5}, FONT_SIZE, 1, DARKGRAY);
  259.             }
  260.         }
  261.     }
  262.  
  263.     // Информация о файлах
  264.     if (files.count > 0) {
  265.         char file_info[64];
  266.         snprintf(file_info, sizeof(file_info), "%d/%d", selected_item + 1, files.count);
  267.         DrawTextEx(russian_font, file_info, (Vector2){SCREEN_WIDTH - 50, SCREEN_HEIGHT - 30}, FONT_SIZE, 1, LIGHTGRAY);
  268.     }
  269.  
  270.     // Подсказки
  271.     DrawTextEx(russian_font, "Стрелки:Навигация  Enter:Открыть  Backspace:Назад",
  272.         (Vector2){MARGIN, SCREEN_HEIGHT - 30}, FONT_SIZE, 1, GRAY);
  273. }
  274.  
  275. int main() {
  276.     InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Файловый браузер");
  277.     SetTargetFPS(60);
  278.  
  279.     // Загружаем шрифт с поддержкой кириллицы
  280.     russian_font = LoadUnicodeFont();
  281.     SetTextureFilter(russian_font.texture, TEXTURE_FILTER_TRILINEAR);
  282.  
  283.     // Загрузка начальной директории
  284.     LoadDirectory(GetUserHomeDir());
  285.  
  286.     while (!WindowShouldClose()) {
  287.         HandleMouseWheel();
  288.  
  289.         // Обработка клавиш навигации
  290.         if (IsKeyPressed(KEY_DOWN) && selected_item < files.count - 1) {
  291.             selected_item++;
  292.         }
  293.  
  294.         if (IsKeyPressed(KEY_UP) && selected_item > 0) {
  295.             selected_item--;
  296.         }
  297.  
  298.         if (IsKeyPressed(KEY_PAGE_DOWN)) {
  299.             selected_item = (selected_item + MAX_VISIBLE_ITEMS < files.count - 1) ?
  300.                 selected_item + MAX_VISIBLE_ITEMS : files.count - 1;
  301.         }
  302.  
  303.         if (IsKeyPressed(KEY_PAGE_UP)) {
  304.             selected_item = (selected_item - MAX_VISIBLE_ITEMS > 0) ?
  305.                 selected_item - MAX_VISIBLE_ITEMS : 0;
  306.         }
  307.  
  308.         // Обработка Enter (открытие файла/директории)
  309.         if (IsKeyPressed(KEY_ENTER) && files.count > 0) {
  310.             const char *selected = files.items[selected_item];
  311.             if (selected[strlen(selected)-1] == '/') {
  312.                 if (strcmp(selected, "../") == 0) {
  313.                     // Переход в родительскую директорию
  314.                     char *last_slash = strrchr(current_dir, '/');
  315.                     if (last_slash != NULL) {
  316.                         *last_slash = '\0';
  317.                         if (strlen(current_dir) == 0) strcpy(current_dir, "/");
  318.                         LoadDirectory(current_dir);
  319.                     }
  320.                 } else {
  321.                     // Переход в поддиректорию
  322.                     char new_path[2048];
  323.                     snprintf(new_path, sizeof(new_path), "%s/%.*s", current_dir, (int)strlen(selected)-1, selected);
  324.                     LoadDirectory(new_path);
  325.                 }
  326.             } else {
  327.                 // Выбран файл
  328.                 TraceLog(LOG_INFO, TextFormat("Selected: %s/%s", current_dir, selected));
  329.             }
  330.         }
  331.  
  332.         // Обработка Backspace (назад)
  333.         if (IsKeyPressed(KEY_BACKSPACE)) {
  334.             char *last_slash = strrchr(current_dir, '/');
  335.             if (last_slash != NULL) {
  336.                 *last_slash = '\0';
  337.                 if (strlen(current_dir) == 0) strcpy(current_dir, "/");
  338.                 LoadDirectory(current_dir);
  339.             }
  340.         }
  341.  
  342.         // Обновление по F5
  343.         if (IsKeyPressed(KEY_F5)) {
  344.             LoadDirectory(current_dir);
  345.         }
  346.  
  347.         // Отрисовка
  348.         BeginDrawing();
  349.             ClearBackground(RAYWHITE);
  350.             DrawInterface();
  351.         EndDrawing();
  352.     }
  353.  
  354.     // Освобождение ресурсов
  355.     FreeFileList();
  356.     UnloadFont(russian_font);
  357.     CloseWindow();
  358.  
  359.     return 0;
  360. }
  361.  

Lulu

  • Sr. Member
  • ****
  • Posts: 299
Re: A small file manager(Raylib)
« Reply #2 on: April 07, 2025, 03:03:13 pm »
Hi Guva,
the way you construct the charset is interesting.
Code: Pascal  [Select][+][-]
  1.   AddRange($C0, $17F);  // Latin-1 Supplement + Latin Extended-A
  2.   AddRange($180, $24F); // Latin Extended-B
  3.   AddRange($370, $3FF); // Greek and Coptic
  4.   AddRange($400, $4FF); // Cyrillic
  5.   AddRange($500, $52F); // Cyrillic Supplement
  6.   AddRange($1E00, $1EFF); // Latin Extended Additional

Thanks for sharing!
wishing you a nice life!
GitHub repositories https://github.com/Lulu04

 

TinyPortal © 2005-2018