jfileselect: add a "save as" option

This commit is contained in:
Lephenixnoir 2022-06-24 00:38:05 +01:00
parent 699576eb33
commit 32ef1536d7
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
4 changed files with 111 additions and 12 deletions

View file

@ -7,6 +7,7 @@
#include <justui/defs.h> #include <justui/defs.h>
#include <justui/jwidget.h> #include <justui/jwidget.h>
#include <justui/jinput.h>
#include <gint/display.h> #include <gint/display.h>
#include <dirent.h> #include <dirent.h>
@ -34,6 +35,12 @@ typedef struct {
/* Full path to file last selected with EXE */ /* Full path to file last selected with EXE */
char *selected_file; char *selected_file;
/* "Save As" input field */
jinput *saveas_input;
/* Whether the "Save As" option is shown */
bool saveas;
/* Whether we are currently using the input field */
bool input_mode;
/* Current cursor position (0 .. folder_entries-1) */ /* Current cursor position (0 .. folder_entries-1) */
int16_t cursor; int16_t cursor;
@ -62,6 +69,13 @@ extern uint16_t JFILESELECT_CANCELED;
any events in this state; a path must first be set before use. */ any events in this state; a path must first be set before use. */
jfileselect *jfileselect_create(void *parent); jfileselect *jfileselect_create(void *parent);
/* jfileselect_set_saveas(): Select whether a "Save as" option is presented
If true, the browser will show a "<Create a new file here>" entry in every
directory. The result of jfileselect_selected_file(), in this case, might be
a file that does not yet exist. */
void jfileselect_set_saveas(jfileselect *fs, bool save_as);
/* jfileselect_browse(): Browse a folder /* jfileselect_browse(): Browse a folder
This function loads the specified folder and allows the user to select a This function loads the specified folder and allows the user to select a

View file

@ -28,7 +28,7 @@ struct fileinfo {
char *name; char *name;
/* File size in bytes if file, number of entries if folder */ /* File size in bytes if file, number of entries if folder */
int size; int size;
/* Type from [struct dirent] */ /* Type from [struct dirent], -1 for the "Save As" entry */
int type; int type;
}; };
@ -41,11 +41,21 @@ jfileselect *jfileselect_create(void *parent)
jwidget_init(&fs->widget, jfileselect_type_id, parent); jwidget_init(&fs->widget, jfileselect_type_id, parent);
jinput *input = jinput_create("Filename: ", 32, fs);
if(!input) {
free(fs);
return NULL;
}
jwidget_set_floating(input, true);
jwidget_set_visible(input, false);
fs->path = NULL; fs->path = NULL;
fs->entries = NULL; fs->entries = NULL;
fs->entry_count = 0; fs->entry_count = 0;
fs->selected_file = NULL; fs->selected_file = NULL;
fs->saveas_input = input;
fs->saveas = false;
fs->cursor = -1; fs->cursor = -1;
fs->scroll = 0; fs->scroll = 0;
@ -77,6 +87,30 @@ static void set_finfo(jfileselect *fs, struct fileinfo *finfo, int n)
fs->entry_count = n; fs->entry_count = n;
} }
void jfileselect_set_saveas(jfileselect *fs, bool save_as)
{
fs->saveas = save_as;
}
//---
// Input utilities
//---
static void start_input(jfileselect *fs)
{
fs->input_mode = true;
jinput_clear(fs->saveas_input);
jwidget_event(fs->saveas_input, (jevent){ .type = JWIDGET_FOCUS_IN });
fs->widget.update = true;
}
static void stop_input(jfileselect *fs)
{
fs->input_mode = false;
jwidget_event(fs->saveas_input, (jevent){ .type = JWIDGET_FOCUS_OUT });
fs->widget.update = true;
}
//--- //---
// Getters and setters // Getters and setters
//--- //---
@ -161,12 +195,19 @@ static int count_accepted_entries(DIR *dp)
static int compare_entries(void const *i1_0, void const *i2_0) static int compare_entries(void const *i1_0, void const *i2_0)
{ {
struct fileinfo const *i1 = i1_0, *i2 = i2_0; struct fileinfo const *i1 = i1_0, *i2 = i2_0;
int d1 = (i1->type == DT_DIR);
int d2 = (i2->type == DT_DIR);
/* Group directories first */ /* Group directories first */
int d1 = (i1->type == DT_DIR);
int d2 = (i2->type == DT_DIR);
if(d1 != d2) if(d1 != d2)
return d2 - d1; return d2 - d1;
/* Then the "Save As" entry */
int sa1 = (i1->type == -1);
int sa2 = (i2->type == -1);
if(sa1 != sa2)
return sa2 - sa1;
/* Then group by name */ /* Then group by name */
return strcmp(i1->name, i2->name); return strcmp(i1->name, i2->name);
} }
@ -181,7 +222,7 @@ static bool load_folder_switch(jfileselect *fs, char *path)
return false; return false;
/* Count entries */ /* Count entries */
int n = count_accepted_entries(dp); int n = count_accepted_entries(dp) + fs->saveas;
/* Allocate memory for the fileinfo structures */ /* Allocate memory for the fileinfo structures */
struct fileinfo *finfo = malloc(n * sizeof *finfo); struct fileinfo *finfo = malloc(n * sizeof *finfo);
@ -227,6 +268,13 @@ static bool load_folder_switch(jfileselect *fs, char *path)
i++; i++;
} }
/* Add the saveas entry */
if(fs->saveas) {
finfo[n-1].name = strdup("<Create a new file here>");
finfo[n-1].type = -1;
finfo[n-1].size = -1;
}
qsort(finfo, n, sizeof *finfo, compare_entries); qsort(finfo, n, sizeof *finfo, compare_entries);
closedir(dp); closedir(dp);
@ -258,6 +306,7 @@ bool jfileselect_browse(jfileselect *fs, char const *path)
fs->cursor = 0; fs->cursor = 0;
fs->scroll = 0; fs->scroll = 0;
stop_input(fs);
return true; return true;
} }
@ -288,6 +337,7 @@ static void jfileselect_poly_layout(void *fs0)
{ {
jfileselect *fs = fs0; jfileselect *fs = fs0;
count_visible_lines(fs); count_visible_lines(fs);
fs->saveas_input->widget.w = jwidget_content_width(fs) - 4;
} }
static void jfileselect_poly_render(void *fs0, int x, int y) static void jfileselect_poly_render(void *fs0, int x, int y)
@ -306,14 +356,22 @@ static void jfileselect_poly_render(void *fs0, int x, int y)
struct fileinfo *info = &finfo[fs->scroll + i]; struct fileinfo *info = &finfo[fs->scroll + i];
bool isfolder = (info->type == DT_DIR); bool isfolder = (info->type == DT_DIR);
int line_y = y + line_height * i;
if(selected)
drect(x, line_y, x + cw - 1, line_y + line_height - 1, C_BLACK);
/* Round `line_spacing / 2` down so there is more spacing below */ /* Round `line_spacing / 2` down so there is more spacing below */
int line_y = y + line_height * i;
int text_y = line_y + (fs->line_spacing + 0) / 2; int text_y = line_y + (fs->line_spacing + 0) / 2;
int fg = selected ? C_WHITE : C_BLACK; int fg = selected ? C_WHITE : C_BLACK;
if(selected && fs->input_mode) {
/* Little bit of a hack */
fs->saveas_input->widget.visible = true;
jwidget_render(fs->saveas_input, x+2, text_y);
fs->saveas_input->widget.visible = false;
continue;
}
if(selected)
drect(x, line_y, x + cw - 1, line_y + line_height - 1, C_BLACK);
dprint(x+2, text_y, fg, "%s%s", info->name, isfolder ? "/" : ""); dprint(x+2, text_y, fg, "%s%s", info->name, isfolder ? "/" : "");
if(fs->show_file_size && info->size >= 0) { if(fs->show_file_size && info->size >= 0) {
dprint_opt(x + cw - 3, text_y, fg, C_NONE, DTEXT_RIGHT, DTEXT_TOP, dprint_opt(x + cw - 3, text_y, fg, C_NONE, DTEXT_RIGHT, DTEXT_TOP,
@ -330,6 +388,30 @@ static bool jfileselect_poly_event(void *fs0, jevent e)
if(!fs->path || !fs->entries) if(!fs->path || !fs->entries)
return false; return false;
if(e.type == JINPUT_CANCELED && e.source == fs->saveas_input) {
stop_input(fs);
return true;
}
else if(e.type == JINPUT_VALIDATED && e.source == fs->saveas_input) {
stop_input(fs);
fs->selected_file = path_down(fs->path,jinput_value(fs->saveas_input));
if(fs->selected_file) {
jwidget_emit(fs,(jevent){ .type = JFILESELECT_VALIDATED });
return true;
}
else return false;
}
/* Send all events to the input when in input mode (without requiring
access to the jscene to actually move the focus */
else if(fs->input_mode) {
bool b = jwidget_event(fs->saveas_input, e);
if(b)
fs->widget.update = true;
/* We do capture all key events if not used by the input, so F-keys are
disabled/etc */
return b || e.type == JWIDGET_KEY;
}
if(e.type == JWIDGET_KEY) { if(e.type == JWIDGET_KEY) {
key_event_t ev = e.key; key_event_t ev = e.key;
if(ev.type != KEYEV_DOWN && ev.type != KEYEV_HOLD) if(ev.type != KEYEV_DOWN && ev.type != KEYEV_HOLD)
@ -376,6 +458,7 @@ static bool jfileselect_poly_event(void *fs0, jevent e)
else if(key == KEY_EXE) { else if(key == KEY_EXE) {
struct fileinfo *finfo = fs->entries; struct fileinfo *finfo = fs->entries;
struct fileinfo *i = &finfo[fs->cursor]; struct fileinfo *i = &finfo[fs->cursor];
if(i->type == DT_DIR) { if(i->type == DT_DIR) {
char *child = path_down(fs->path, i->name); char *child = path_down(fs->path, i->name);
if(child) { if(child) {
@ -385,6 +468,10 @@ static bool jfileselect_poly_event(void *fs0, jevent e)
return true; return true;
} }
} }
else if(fs->saveas && i->type == -1) {
start_input(fs);
return true;
}
else { else {
fs->selected_file = path_down(fs->path, i->name); fs->selected_file = path_down(fs->path, i->name);
if(fs->selected_file) { if(fs->selected_file) {

View file

@ -99,8 +99,7 @@ void jscene_queue_event(jscene *s, jevent e)
{ {
/* Prevent filling and overflowing the queue */ /* Prevent filling and overflowing the queue */
int next = (s->queue_next + 1) % JSCENE_QUEUE_SIZE; int next = (s->queue_next + 1) % JSCENE_QUEUE_SIZE;
if(next == s->queue_first) if(next == s->queue_first) {
{
s->lost_events++; s->lost_events++;
return; return;
} }

View file

@ -656,8 +656,7 @@ bool jwidget_event(void *w0, jevent e)
{ {
J_CAST(w) J_CAST(w)
jwidget_poly const *poly = widget_types[w->type]; jwidget_poly const *poly = widget_types[w->type];
if(poly->event) return poly->event(w, e); return poly->event && poly->event(w, e);
return false;
} }
void jwidget_emit(void *w0, jevent e) void jwidget_emit(void *w0, jevent e)