ded

Dramatic EDitor
Index Commits Files Refs README LICENSE
src/file_browser.c (6329B)
   1 #include <string.h>
   2 #include "file_browser.h"
   3 #include "sv.h"
   4 
   5 static int file_cmp(const void *ap, const void *bp)
   6 {
   7     const char *a = *(const char**)ap;
   8     const char *b = *(const char**)bp;
   9     return strcmp(a, b);
  10 }
  11 
  12 Errno fb_open_dir(File_Browser *fb, const char *dir_path)
  13 {
  14     fb->files.count = 0;
  15     fb->cursor = 0;
  16     Errno err = read_entire_dir(dir_path, &fb->files);
  17     if (err != 0) {
  18         return err;
  19     }
  20     qsort(fb->files.items, fb->files.count, sizeof(*fb->files.items), file_cmp);
  21 
  22     fb->dir_path.count = 0;
  23     sb_append_cstr(&fb->dir_path, dir_path);
  24     sb_append_null(&fb->dir_path);
  25 
  26     return 0;
  27 }
  28 
  29 #define PATH_SEP "/"
  30 #define PATH_EMPTY ""
  31 #define PATH_DOT "."
  32 #define PATH_DOTDOT ".."
  33 
  34 typedef struct {
  35     String_View *items;
  36     size_t count;
  37     size_t capacity;
  38 } Comps;
  39 
  40 void normpath(String_View path, String_Builder *result)
  41 {
  42     size_t original_sb_size = result->count;
  43 
  44     if (path.count == 0) {
  45         sb_append_cstr(result, PATH_DOT);
  46         return;
  47     }
  48 
  49     int initial_slashes = 0;
  50     while (path.count > 0 && *path.data == *PATH_SEP) {
  51         initial_slashes += 1;
  52         sv_chop_left(&path, 1);
  53     }
  54     if (initial_slashes > 2) {
  55         initial_slashes = 1;
  56     }
  57 
  58     Comps new_comps = {0};
  59 
  60     while (path.count > 0) {
  61         String_View comp = sv_chop_by_delim(&path, '/');
  62         if (comp.count == 0 || sv_eq(comp, SV(PATH_DOT))) {
  63             continue;
  64         }
  65         if (!sv_eq(comp, SV(PATH_DOTDOT))) {
  66             da_append(&new_comps, comp);
  67             continue;
  68         }
  69         if (initial_slashes == 0 && new_comps.count == 0) {
  70             da_append(&new_comps, comp);
  71             continue;
  72         }
  73         if (new_comps.count > 0 && sv_eq(da_last(&new_comps), SV(PATH_DOTDOT))) {
  74             da_append(&new_comps, comp);
  75             continue;
  76         }
  77         if (new_comps.count > 0) {
  78             new_comps.count -= 1;
  79             continue;
  80         }
  81     }
  82 
  83     for (int i = 0; i < initial_slashes; ++i) {
  84         sb_append_cstr(result, PATH_SEP);
  85     }
  86 
  87     for (size_t i = 0; i < new_comps.count; ++i) {
  88         if (i > 0) sb_append_cstr(result, PATH_SEP);
  89         sb_append_buf(result, new_comps.items[i].data, new_comps.items[i].count);
  90     }
  91 
  92     if (original_sb_size == result->count) {
  93         sb_append_cstr(result, PATH_DOT);
  94     }
  95 
  96     free(new_comps.items);
  97 }
  98 
  99 Errno fb_change_dir(File_Browser *fb)
 100 {
 101     assert(fb->dir_path.count > 0 && "You need to call fb_open_dir() before fb_change_dir()");
 102     assert(fb->dir_path.items[fb->dir_path.count - 1] == '\0');
 103 
 104     if (fb->cursor >= fb->files.count) return 0;
 105 
 106     const char *dir_name = fb->files.items[fb->cursor];
 107 
 108     fb->dir_path.count -= 1;
 109 
 110     // TODO: fb->dir_path grows indefinitely if we hit the root
 111     sb_append_cstr(&fb->dir_path, "/");
 112     sb_append_cstr(&fb->dir_path, dir_name);
 113 
 114     String_Builder result = {0};
 115     normpath(sb_to_sv(fb->dir_path), &result);
 116     da_move(&fb->dir_path, result);
 117     sb_append_null(&fb->dir_path);
 118 
 119     printf("Changed dir to %s\n", fb->dir_path.items);
 120 
 121     fb->files.count = 0;
 122     fb->cursor = 0;
 123     Errno err = read_entire_dir(fb->dir_path.items, &fb->files);
 124 
 125     if (err != 0) {
 126         return err;
 127     }
 128     qsort(fb->files.items, fb->files.count, sizeof(*fb->files.items), file_cmp);
 129 
 130     return 0;
 131 }
 132 
 133 void fb_render(const File_Browser *fb, SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer *sr)
 134 {
 135     Vec2f cursor_pos = vec2f(0, -(float)fb->cursor * FREE_GLYPH_FONT_SIZE);
 136 
 137     int w, h;
 138     SDL_GetWindowSize(window, &w, &h);
 139 
 140     float max_line_len = 0.0f;
 141 
 142     sr->resolution = vec2f(w, h);
 143     sr->time = (float) SDL_GetTicks() / 1000.0f;
 144 
 145     simple_renderer_set_shader(sr, SHADER_FOR_COLOR);
 146     if (fb->cursor < fb->files.count) {
 147         const Vec2f begin = vec2f(0, -((float)fb->cursor + CURSOR_OFFSET) * FREE_GLYPH_FONT_SIZE);
 148         Vec2f end = begin;
 149         free_glyph_atlas_measure_line_sized(
 150             atlas, fb->files.items[fb->cursor], strlen(fb->files.items[fb->cursor]),
 151             &end);
 152         simple_renderer_solid_rect(sr, begin, vec2f(end.x - begin.x, FREE_GLYPH_FONT_SIZE), vec4f(.25, .25, .25, 1));
 153     }
 154     simple_renderer_flush(sr);
 155 
 156     simple_renderer_set_shader(sr, SHADER_FOR_EPICNESS);
 157     for (size_t row = 0; row < fb->files.count; ++row) {
 158         const Vec2f begin = vec2f(0, -(float)row * FREE_GLYPH_FONT_SIZE);
 159         Vec2f end = begin;
 160         free_glyph_atlas_render_line_sized(
 161             atlas, sr, fb->files.items[row], strlen(fb->files.items[row]),
 162             &end,
 163             vec4fs(0));
 164         // TODO: the max_line_len should be calculated based on what's visible on the screen right now
 165         float line_len = fabsf(end.x - begin.x);
 166         if (line_len > max_line_len) {
 167             max_line_len = line_len;
 168         }
 169     }
 170 
 171     simple_renderer_flush(sr);
 172 
 173     // Update camera
 174     {
 175         if (max_line_len > 1000.0f) {
 176             max_line_len = 1000.0f;
 177         }
 178 
 179         float target_scale = w/3/(max_line_len*0.75); // TODO: division by 0
 180 
 181         Vec2f target = cursor_pos;
 182         float offset = 0.0f;
 183 
 184         if (target_scale > 3.0f) {
 185             target_scale = 3.0f;
 186         } else {
 187             offset = cursor_pos.x - w/3/sr->camera_scale;
 188             if (offset < 0.0f) offset = 0.0f;
 189             target = vec2f(w/3/sr->camera_scale + offset, cursor_pos.y);
 190         }
 191 
 192         sr->camera_vel = vec2f_mul(
 193                              vec2f_sub(target, sr->camera_pos),
 194                              vec2fs(2.0f));
 195         sr->camera_scale_vel = (target_scale - sr->camera_scale) * 2.0f;
 196 
 197         sr->camera_pos = vec2f_add(sr->camera_pos, vec2f_mul(sr->camera_vel, vec2fs(DELTA_TIME)));
 198         sr->camera_scale = sr->camera_scale + sr->camera_scale_vel * DELTA_TIME;
 199     }
 200 }
 201 
 202 const char *fb_file_path(File_Browser *fb)
 203 {
 204     assert(fb->dir_path.count > 0 && "You need to call fb_open_dir() before fb_file_path()");
 205     assert(fb->dir_path.items[fb->dir_path.count - 1] == '\0');
 206 
 207     if (fb->cursor >= fb->files.count) return NULL;
 208 
 209     fb->file_path.count = 0;
 210     sb_append_buf(&fb->file_path, fb->dir_path.items, fb->dir_path.count - 1);
 211     sb_append_buf(&fb->file_path, "/", 1);
 212     sb_append_cstr(&fb->file_path, fb->files.items[fb->cursor]);
 213     sb_append_null(&fb->file_path);
 214 
 215     return fb->file_path.items;
 216 }