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