#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <raylib.h>
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define FONT_SIZE 20
#define ITEM_HEIGHT 30
#define MAX_VISIBLE_ITEMS 15
#define MARGIN 10
#define SCROLL_SPEED 1
typedef struct {
char **items;
int count;
} FileList;
FileList files = {0};
int scroll_offset = 0;
int selected_item = 0;
char current_dir[1024] = {0};
char parent_dir[1024] = {0};
Rectangle file_list_area;
Font russian_font;
// Функция для загрузки шрифта с поддержкой множества языков
Font LoadUnicodeFont() {
int *codepoints = NULL;
int count = 0;
int capacity = 0;
// Функция для добавления диапазона символов
#define ADD_RANGE(start, end) \
for (int i = start; i <= end; i++) { \
if (count >= capacity) { \
capacity = (capacity == 0) ? 256 : capacity * 2; \
codepoints = realloc(codepoints, capacity * sizeof(int)); \
} \
codepoints[count++] = i; \
}
// Базовые символы ASCII
ADD_RANGE(32, 126);
// Основные европейские языки
ADD_RANGE(0xC0, 0x17F); // Latin-1 Supplement + Latin Extended-A
ADD_RANGE(0x180, 0x24F); // Latin Extended-B
ADD_RANGE(0x370, 0x3FF); // Greek and Coptic
ADD_RANGE(0x400, 0x4FF); // Cyrillic
ADD_RANGE(0x500, 0x52F); // Cyrillic Supplement
ADD_RANGE(0x1E00, 0x1EFF); // Latin Extended Additional
// Специальные символы для конкретных языков
ADD_RANGE(0x1E00, 0x1EFF); // Latin Extended Additional (Fon, Ewe и др.)
ADD_RANGE(0x2C60, 0x2C7F); // Latin Extended-C (Aja, Basaa)
// Азиатские языки (кириллица и латиница)
ADD_RANGE(0xA640, 0xA69F); // Cyrillic Extended-B (Kazakh, Chuvash)
// Дополнительные символы
ADD_RANGE(0x300, 0x36F); // Combining Diacritical Marks
ADD_RANGE(0x1DC0, 0x1DFF); // Combining Diacritical Marks Supplement
#undef ADD_RANGE
Font font = {0};
// Пытаемся загрузить универсальный шрифт
#ifdef __linux__
font = LoadFontEx("Yulong-Regular.otf", FONT_SIZE, codepoints, count);
if (font.texture.id == 0) {
font = LoadFontEx("/usr/share/fonts/truetype/freefont/FreeSans.ttf", FONT_SIZE, codepoints, count);
}
#else
font = LoadFontEx("C:\\Windows\\Fonts\\arialuni.ttf", FONT_SIZE, codepoints, count);
if (font.texture.id == 0) {
font = LoadFontEx("C:\\Windows\\Fonts\\arial.ttf", FONT_SIZE, codepoints, count);
}
#endif
if (font.texture.id == 0) {
font = GetFontDefault();
}
return font;
}
void HandleMouseWheel() {
float wheel_move = GetMouseWheelMove();
if (wheel_move != 0) {
if (wheel_move > 0) {
selected_item = (selected_item - SCROLL_SPEED > 0) ? selected_item - SCROLL_SPEED : 0;
} else if (wheel_move < 0) {
selected_item = (selected_item + SCROLL_SPEED < files.count - 1) ? selected_item + SCROLL_SPEED : files.count - 1;
}
}
}
const char* GetUserHomeDir() {
const char *home
= getenv("HOME"); if (home == NULL) {
}
if (home == NULL) {
home = ".";
}
return home;
}
void FreeFileList() {
for (int i = 0; i < files.count; i++) {
}
files.items = NULL;
files.count = 0;
}
void LoadDirectory(const char *path) {
FreeFileList();
// Сохраняем текущую директорию
strncpy(current_dir
, path
, sizeof(current_dir
) - 1); current_dir[sizeof(current_dir) - 1] = '\0';
// Добавляем родительскую директорию, если это не корень
files.
items = malloc(sizeof(char*)); files.items[0] = strdup("../");
files.count = 1;
}
DIR *dir = opendir(path);
if (dir != NULL) {
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
// Пропускаем специальные директории
if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0) { continue;
}
// Проверяем, является ли это директорией
char full_path[1024];
snprintf(full_path
, sizeof(full_path
), "%s/%s", path
, entry
->d_name
);
struct stat statbuf;
if (stat(full_path, &statbuf) == 0) {
if (S_ISDIR(statbuf.st_mode)) {
// Это директория
sprintf(item
, "%s/", entry
->d_name
); files.
items = realloc(files.
items, (files.
count + 1) * sizeof(char*)); files.items[files.count++] = item;
}
}
}
closedir(dir);
}
// Теперь добавляем файлы
dir = opendir(path);
if (dir != NULL) {
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
// Пропускаем специальные директории
if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0) { continue;
}
// Проверяем, является ли это файлом
char full_path[1024];
snprintf(full_path
, sizeof(full_path
), "%s/%s", path
, entry
->d_name
);
struct stat statbuf;
if (stat(full_path, &statbuf) == 0) {
if (!S_ISDIR(statbuf.st_mode)) {
// Это файл
files.
items = realloc(files.
items, (files.
count + 1) * sizeof(char*)); files.items[files.count++] = strdup(entry->d_name);
}
}
}
closedir(dir);
}
scroll_offset = 0;
selected_item = 0;
}
void DrawInterface() {
// Заголовок
DrawTextEx(russian_font, "Файловый браузер", (Vector2){MARGIN, MARGIN}, FONT_SIZE, 1, DARKGRAY);
// Текущая директория
char dir_text[2048];
snprintf(dir_text
, sizeof(dir_text
), "Директория: %s", current_dir
); DrawTextEx(russian_font, dir_text, (Vector2){MARGIN, MARGIN + 40}, FONT_SIZE, 1, GRAY);
// Область списка файлов
file_list_area = (Rectangle){MARGIN, MARGIN + 80, SCREEN_WIDTH - 2*MARGIN, SCREEN_HEIGHT - 160};
DrawRectangleLinesEx(file_list_area, 1, GRAY);
int max_visible_items = (int)(file_list_area.height / ITEM_HEIGHT);
// Корректируем scroll offset
if (selected_item < scroll_offset) {
scroll_offset = selected_item;
}
if (selected_item >= scroll_offset + max_visible_items) {
scroll_offset = selected_item - max_visible_items + 1;
}
// Рисуем видимые элементы
for (int i = 0; i < max_visible_items && (scroll_offset + i) < files.count; i++) {
int y_pos = (int)file_list_area.y + i * ITEM_HEIGHT;
const char *filename = files.items[scroll_offset + i];
// Чередование цветов фона
Color bg_color = ((scroll_offset + i) % 2 == 0) ?
Fade(LIGHTGRAY, 0.5f) : Fade(LIGHTGRAY, 0.3f);
if ((scroll_offset + i) == selected_item) {
// Выбранный элемент
DrawRectangle((int)file_list_area.x, y_pos, (int)file_list_area.width, ITEM_HEIGHT, BLUE);
char display_text[1024];
if (filename
[strlen(filename
)-1] == '/') { if (strcmp(filename
, "../") == 0) { snprintf(display_text
, sizeof(display_text
), "[..]"); } else {
snprintf(display_text
, sizeof(display_text
), "[%.*s]", (int)strlen(filename
)-1, filename
); }
DrawTextEx(russian_font, display_text, (Vector2){(int)file_list_area.x + 5, y_pos + 5}, FONT_SIZE, 1, WHITE);
} else {
DrawTextEx(russian_font, filename, (Vector2){(int)file_list_area.x + 5, y_pos + 5}, FONT_SIZE, 1, WHITE);
}
} else {
// Невыбранный элемент
DrawRectangle((int)file_list_area.x, y_pos, (int)file_list_area.width, ITEM_HEIGHT, bg_color);
char display_text[1024];
if (filename
[strlen(filename
)-1] == '/') { if (strcmp(filename
, "../") == 0) { snprintf(display_text
, sizeof(display_text
), "[..]"); DrawTextEx(russian_font, display_text, (Vector2){(int)file_list_area.x + 5, y_pos + 5}, FONT_SIZE, 1, DARKBLUE);
} else {
snprintf(display_text
, sizeof(display_text
), "[%.*s]", (int)strlen(filename
)-1, filename
); DrawTextEx(russian_font, display_text, (Vector2){(int)file_list_area.x + 5, y_pos + 5}, FONT_SIZE, 1, DARKGREEN);
}
} else {
DrawTextEx(russian_font, filename, (Vector2){(int)file_list_area.x + 5, y_pos + 5}, FONT_SIZE, 1, DARKGRAY);
}
}
}
// Информация о файлах
if (files.count > 0) {
char file_info[64];
snprintf(file_info
, sizeof(file_info
), "%d/%d", selected_item
+ 1, files.
count); DrawTextEx(russian_font, file_info, (Vector2){SCREEN_WIDTH - 50, SCREEN_HEIGHT - 30}, FONT_SIZE, 1, LIGHTGRAY);
}
// Подсказки
DrawTextEx(russian_font, "Стрелки:Навигация Enter:Открыть Backspace:Назад",
(Vector2){MARGIN, SCREEN_HEIGHT - 30}, FONT_SIZE, 1, GRAY);
}
int main() {
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Файловый браузер");
SetTargetFPS(60);
// Загружаем шрифт с поддержкой кириллицы
russian_font = LoadUnicodeFont();
SetTextureFilter(russian_font.texture, TEXTURE_FILTER_TRILINEAR);
// Загрузка начальной директории
LoadDirectory(GetUserHomeDir());
while (!WindowShouldClose()) {
HandleMouseWheel();
// Обработка клавиш навигации
if (IsKeyPressed(KEY_DOWN) && selected_item < files.count - 1) {
selected_item++;
}
if (IsKeyPressed(KEY_UP) && selected_item > 0) {
selected_item--;
}
if (IsKeyPressed(KEY_PAGE_DOWN)) {
selected_item = (selected_item + MAX_VISIBLE_ITEMS < files.count - 1) ?
selected_item + MAX_VISIBLE_ITEMS : files.count - 1;
}
if (IsKeyPressed(KEY_PAGE_UP)) {
selected_item = (selected_item - MAX_VISIBLE_ITEMS > 0) ?
selected_item - MAX_VISIBLE_ITEMS : 0;
}
// Обработка Enter (открытие файла/директории)
if (IsKeyPressed(KEY_ENTER) && files.count > 0) {
const char *selected = files.items[selected_item];
if (selected
[strlen(selected
)-1] == '/') { if (strcmp(selected
, "../") == 0) { // Переход в родительскую директорию
char *last_slash
= strrchr(current_dir
, '/'); if (last_slash != NULL) {
*last_slash = '\0';
LoadDirectory(current_dir);
}
} else {
// Переход в поддиректорию
char new_path[2048];
snprintf(new_path
, sizeof(new_path
), "%s/%.*s", current_dir
, (int)strlen(selected
)-1, selected
); LoadDirectory(new_path);
}
} else {
// Выбран файл
TraceLog(LOG_INFO, TextFormat("Selected: %s/%s", current_dir, selected));
}
}
// Обработка Backspace (назад)
if (IsKeyPressed(KEY_BACKSPACE)) {
char *last_slash
= strrchr(current_dir
, '/'); if (last_slash != NULL) {
*last_slash = '\0';
LoadDirectory(current_dir);
}
}
// Обновление по F5
if (IsKeyPressed(KEY_F5)) {
LoadDirectory(current_dir);
}
// Отрисовка
BeginDrawing();
ClearBackground(RAYWHITE);
DrawInterface();
EndDrawing();
}
// Освобождение ресурсов
FreeFileList();
UnloadFont(russian_font);
CloseWindow();
return 0;
}