Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Пример файлового менеджера на Pascal с использованием Raylib: загрузка и отображение языков

Delphi , Файловая система , Файлы

 

В этой статье мы разберем реализацию простого файлового менеджера на Object Pascal с использованием библиотеки Raylib, особое внимание уделив поддержке различных языков, включая кириллицу.

Введение в Raylib

Raylib - это простая и легкая библиотека для создания игр и мультимедийных приложений, которая поддерживает множество платформ. Она предоставляет удобный API для работы с графикой, звуком и вводом.

Структура файлового менеджера

Рассмотрим основные компоненты файлового менеджера:

program filebrowser;
{$WARN 5044 off : Symbol "$1" is not portable}
uses
  SysUtils, Raylib, math;

const
  SCREEN_WIDTH = 800;
  SCREEN_HEIGHT = 600;
  FONT_SIZE = 20;
  ITEM_HEIGHT = 30;
  MAX_VISIBLE_ITEMS = 15;
  MARGIN = 10;
  SCROLL_SPEED = 1;

var
  Files: array of string;
  ScrollOffset: Integer = 0;
  SelectedItem: Integer = 0;
  CurrentDir, ParentDir: string;
  FileListArea: TRectangle;
  RussianFont: TFont;

Загрузка шрифтов с поддержкой кириллицы

Одна из ключевых особенностей этого примера - корректное отображение текста на разных языках. Рассмотрим функцию загрузки шрифта:

function LoadRussianFont: TFont;
var
  i: Integer;
  cp: array[0..511] of Integer;
begin
  // Инициализируем массив кодовых точек
  for i := 0 to 95 do
    cp[i] := 32 + i;  // Базовые ASCII символы

  // Добавляем кириллические символы (диапазон 0x0400-0x04FF)
  for i := 0 to 255 do
    cp[96 + i] := $400 + i;

  // Загружаем шрифт с указанными кодовыми точками
  {$IFDEF UNIX}
  Result := LoadFontEx('Yulong-Regular.otf', FONT_SIZE, @cp[0], Length(cp));
  {$ELSE}
  Result := LoadFontEx('C:\Windows\Fonts\arial.ttf', FONT_SIZE, @cp[0], Length(cp));
  {$ENDIF}

  if Result.texture.id = 0 then // Если шрифт не загрузился, используем стандартный
    Result := GetFontDefault();
end;

Улучшенная версия с поддержкой множества языков

В более поздней версии автор предложил улучшенную функцию загрузки шрифтов, поддерживающую больше языков:

function LoadUnicodeFont: TFont;
var
  cp: array of Integer;
  i, count: Integer;

  procedure AddRange(start, stop: Integer);
  begin
    while start <= stop do
    begin
      if count >= Length(cp) then
        SetLength(cp, Length(cp) + 256);
      cp[count] := start;
      Inc(count);
      Inc(start);
    end;
  end;

begin
  // Создаем массив кодовых точек
  SetLength(cp, 4096);
  count := 0;

  // Базовые символы ASCII
  AddRange(32, 126);

  // Основные европейские языки
  AddRange($C0, $17F);  // Latin-1 Supplement + Latin Extended-A
  AddRange($180, $24F); // Latin Extended-B
  AddRange($370, $3FF); // Greek and Coptic
  AddRange($400, $4FF); // Cyrillic
  AddRange($500, $52F); // Cyrillic Supplement
  AddRange($1E00, $1EFF); // Latin Extended Additional

  // Специальные символы для конкретных языков
  // Африканские языки
  AddRange($1E00, $1EFF); // Latin Extended Additional (Fon, Ewe и др.)
  AddRange($2C60, $2C7F); // Latin Extended-C (Aja, Basaa)

  // Азиатские языки (кириллица и латиница)
  AddRange($A640, $A69F); // Cyrillic Extended-B (Kazakh, Chuvash)

  // Дополнительные символы
  AddRange($300, $36F);  // Combining Diacritical Marks
  AddRange($1DC0, $1DFF); // Combining Diacritical Marks Supplement

  // Уменьшаем массив до фактического размера
  SetLength(cp, count);

  // Пытаемся загрузить универсальный шрифт
  {$IFDEF UNIX}
  Result := LoadFontEx('Yulong-Regular.otf', FONT_SIZE, @cp[0], Length(cp));
  if Result.texture.id = 0 then
    Result := LoadFontEx('/usr/share/fonts/truetype/freefont/FreeSans.ttf', FONT_SIZE, @cp[0], Length(cp));
  {$ELSE}
  Result := LoadFontEx('C:\Windows\Fonts\arialuni.ttf', FONT_SIZE, @cp[0], Length(cp));
  if Result.texture.id = 0 then
    Result := LoadFontEx('C:\Windows\Fonts\arial.ttf', FONT_SIZE, @cp[0], Length(cp));
  {$ENDIF}

  if Result.texture.id = 0 then // Если шрифт не загрузился, используем стандартный
    Result := GetFontDefault();
end;

Работа с файловой системой

Функция загрузки содержимого директории:

procedure LoadDirectory(const Path: string);
var
  SearchRec: TSearchRec;
  FileCount: Integer = 0;
  IsDirectory: Boolean;
begin
  SetLength(Files, 0);

  if Path <> ExtractFileDrive(Path) then
  begin
    SetLength(Files, 1);
    Files[0] := '..' + PathDelim;
    FileCount := 1;
  end;

  if FindFirst(IncludeTrailingPathDelimiter(Path) + '*', faDirectory, SearchRec) = 0 then
  begin
    repeat
      IsDirectory := (SearchRec.Attr and faDirectory) <> 0;
      if IsDirectory and (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
      begin
        SetLength(Files, FileCount + 1);
        Files[FileCount] := SearchRec.Name + PathDelim;
        Inc(FileCount);
      end;
    until FindNext(SearchRec) <> 0;
    FindClose(SearchRec);
  end;

  if FindFirst(IncludeTrailingPathDelimiter(Path) + '*', faAnyFile and not (faDirectory or faHidden), SearchRec) = 0 then
  begin
    repeat
      IsDirectory := (SearchRec.Attr and faDirectory) <> 0;
      if not IsDirectory then
      begin
        SetLength(Files, FileCount + 1);
        Files[FileCount] := SearchRec.Name;
        Inc(FileCount);
      end;
    until FindNext(SearchRec) <> 0;
    FindClose(SearchRec);
  end;

  CurrentDir := IncludeTrailingPathDelimiter(Path);
  ScrollOffset := 0;
  SelectedItem := 0;
end;

Отрисовка интерфейса

Процедура отрисовки пользовательского интерфейса:

procedure DrawInterface;
var
  i, YPos: Integer;
  FileName: string;
  MaxVisibleItems: Integer;
  BGColor: TColor;
begin
  DrawTextEx(RussianFont, 'Файловый браузер',
    Vector2Create(MARGIN, MARGIN), FONT_SIZE , 1, DARKGRAY);

  // Текущая директория
  DrawTextEx(RussianFont, PChar('Директория: ' + CurrentDir),
    Vector2Create(MARGIN, MARGIN + 40), FONT_SIZE, 1, GRAY);

  FileListArea := RectangleCreate(MARGIN, MARGIN + 80, SCREEN_WIDTH - 2*MARGIN, SCREEN_HEIGHT - 160);
  DrawRectangleLinesEx(FileListArea, 1, GRAY);

  MaxVisibleItems := Trunc(FileListArea.height / ITEM_HEIGHT);

  if SelectedItem < ScrollOffset then
    ScrollOffset := SelectedItem;
  if SelectedItem >= ScrollOffset + MaxVisibleItems then
    ScrollOffset := SelectedItem - MaxVisibleItems + 1;

  for i := 0 to Min(MaxVisibleItems - 1, High(Files) - ScrollOffset) do
  begin
    YPos := Round(FileListArea.y) + i * ITEM_HEIGHT;
    FileName := Files[ScrollOffset + i];

    // Чередование цветов фона
    if (ScrollOffset + i) mod 2 = 0 then
      BGColor := Fade(LIGHTGRAY, 0.5)
    else
      BGColor := Fade(LIGHTGRAY, 0.3);

    if (ScrollOffset + i) = SelectedItem then
    begin
      DrawRectangle(Round(FileListArea.x), YPos, Round(FileListArea.width), ITEM_HEIGHT, BLUE);
      if (FileName <> '') and (FileName[Length(FileName)] = PathDelim) then
        DrawTextEx(RussianFont, PChar('['+ExcludeTrailingBackslash(FileName)+']'),
          Vector2Create(Round(FileListArea.x) + 5, YPos + 5), FONT_SIZE, 1, WHITE)
      else
        DrawTextEx(RussianFont, PChar(FileName),
          Vector2Create(Round(FileListArea.x) + 5, YPos + 5), FONT_SIZE, 1, WHITE)
    end
    else
    begin
      DrawRectangle(Round(FileListArea.x), YPos, Round(FileListArea.width), ITEM_HEIGHT, BGColor);

      if (FileName <> '') and (FileName[Length(FileName)] = PathDelim) then
      begin
        if FileName = '..' + PathDelim then
          DrawTextEx(RussianFont, PChar('['+ExcludeTrailingBackslash(FileName)+']'),
            Vector2Create(Round(FileListArea.x) + 5, YPos + 5), FONT_SIZE, 1, DARKBLUE)
        else
          DrawTextEx(RussianFont, PChar('['+ExcludeTrailingBackslash(FileName)+']'),
            Vector2Create(Round(FileListArea.x) + 5, YPos + 5), FONT_SIZE, 1, DARKGREEN);
      end
      else
        DrawTextEx(RussianFont, PChar(FileName),
          Vector2Create(Round(FileListArea.x) + 5, YPos + 5), FONT_SIZE, 1, DARKGRAY);
    end;
  end;

  // Информация о файлах
  if Length(Files) > 0 then
    DrawTextEx(RussianFont, PChar(Format('%d/%d', [SelectedItem + 1, Length(Files)])),
      Vector2Create(SCREEN_WIDTH - 50, SCREEN_HEIGHT - 30), FONT_SIZE, 1, LIGHTGRAY);

  DrawTextEx(RussianFont, 'Стрелки:Навигация  Enter:Открыть  Backspace:Назад',
    Vector2Create(MARGIN, SCREEN_HEIGHT - 30), FONT_SIZE , 1, GRAY);
end;

Основной цикл программы

Главный цикл обработки событий и управления программой:

begin
  InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, 'Файловый браузер');
  SetTargetFPS(60);

  // Загружаем шрифт с поддержкой кириллицы
  RussianFont := LoadRussianFont;
  SetTextureFilter(RussianFont.texture, TEXTURE_FILTER_TRILINEAR);

  // Загрузка начальной директории
  LoadDirectory(GetUserHomeDir);

  while not WindowShouldClose do
  begin
    HandleMouseWheel;

    if IsKeyPressed(KEY_DOWN) and (SelectedItem < High(Files)) then
      Inc(SelectedItem);

    if IsKeyPressed(KEY_UP) and (SelectedItem > 0) then
      Dec(SelectedItem);

    if IsKeyPressed(KEY_PAGE_DOWN) then
      SelectedItem := Min(SelectedItem + MAX_VISIBLE_ITEMS, High(Files));

    if IsKeyPressed(KEY_PAGE_UP) then
      SelectedItem := Max(SelectedItem - MAX_VISIBLE_ITEMS, 0);

    if IsKeyPressed(KEY_ENTER) and (Length(Files) > 0) then
    begin
      if (Files[SelectedItem] <> '') and (Files[SelectedItem][Length(Files[SelectedItem])] = PathDelim) then
      begin
        if Files[SelectedItem] = '..' + PathDelim then
        begin
          ParentDir := ExtractFileDir(ExcludeTrailingPathDelimiter(CurrentDir));
          if ParentDir <> CurrentDir then
            LoadDirectory(ParentDir);
        end
        else
          LoadDirectory(CurrentDir + Files[SelectedItem]);
      end
      else
        TraceLog(LOG_INFO, PChar('Selected: ' + CurrentDir + Files[SelectedItem]));
    end;

    if IsKeyPressed(KEY_BACKSPACE) then
    begin
      ParentDir := ExtractFileDir(ExcludeTrailingPathDelimiter(CurrentDir));
      if ParentDir <> CurrentDir then
        LoadDirectory(ParentDir);
    end;

    if IsKeyPressed(KEY_F5) then
      LoadDirectory(CurrentDir);

    BeginDrawing();
      ClearBackground(RAYWHITE);
      DrawInterface;
    EndDrawing();
  end;

  // Освобождаем шрифт
  UnloadFont(RussianFont);
  CloseWindow;
end.

Альтернативные решения

  1. Использование Free Pascal Components (FPC): Вместо Raylib можно использовать стандартные компоненты Lazarus или другие библиотеки, такие как LCL или fpGUI.

  2. Кроссплатформенные решения: Для более сложных файловых менеджеров можно рассмотреть использование:
    GTK или Qt через привязки к Pascal
    Electron с использованием Pas2JS

  3. Дополнительные функции:
    Добавление вкладок для работы с несколькими директориями
    Поддержка Drag&Drop
    Встроенный просмотрщик файлов

Заключение

Представленный пример демонстрирует, как создать простой, но функциональный файловый менеджер на Pascal с использованием библиотеки Raylib. Особое внимание уделено поддержке различных языков, что делает приложение более универсальным. Код может служить хорошей основой для более сложных проектов.

Создано по материалам из источника по ссылке.

Пример файлового менеджера на Pascal с использованием Raylib, демонстрирующий загрузку и отображение языков.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Файлы ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-06-04 06:10:17/0.0062768459320068/0