Skip to content

Commit

Permalink
Allow to configure navigation keymaps
Browse files Browse the repository at this point in the history
Not tested much.  This is mostly so that we can swap the middle and
right mouse button.
  • Loading branch information
guillaumechereau committed Jul 11, 2024
1 parent 360a913 commit b880f34
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 14 deletions.
72 changes: 61 additions & 11 deletions src/goxel.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,13 +389,31 @@ static void goxel_init_sound()
void goxel_add_gesture(int type, int button,
int (*fn)(const gesture_t *gest, void *user))
{
goxel.gestures[goxel.gestures_count] = calloc(1, sizeof(gesture_t));
*goxel.gestures[goxel.gestures_count] = (gesture_t) {
gesture_t *gest;

gest = calloc(1, sizeof(gesture_t));
*gest = (gesture_t){
.type = type,
.button = button,
.callback = fn,
};
goxel.gestures_count++;
arrput(goxel.gestures, gest);
}

static void goxel_remove_gesture(int (*fn)(const gesture_t *gest, void *user))
{
int i;
bool keep = true;
while (keep) {
keep = false;
for (i = 0; i < arrlen(goxel.gestures); i++) {
if (goxel.gestures[i]->callback == fn) {
arrdel(goxel.gestures, i);
keep = true;
break;
}
}
}
}

static void goxel_load_recent_files(void)
Expand Down Expand Up @@ -444,25 +462,57 @@ void goxel_init(void)
}
goxel.palette = goxel.palette ?: goxel.palettes;

goxel_load_recent_files();

goxel_reset();

goxel_add_gesture(GESTURE_HOVER, 0, on_hover);
goxel_add_gesture(GESTURE_DRAG, GESTURE_LMB, on_drag_rotate);
goxel_add_gesture(GESTURE_DRAG, GESTURE_LMB, on_drag);
goxel_add_gesture(GESTURE_DRAG, GESTURE_RMB, on_pan);
goxel_add_gesture(GESTURE_DRAG, GESTURE_MMB | GESTURE_SHIFT, on_pan);
goxel_add_gesture(GESTURE_DRAG, GESTURE_MMB | GESTURE_CTRL, on_zoom);
goxel_add_gesture(GESTURE_DRAG, GESTURE_MMB, on_rotate);
}

goxel_load_recent_files();
void goxel_update_keymaps(void)
{
int i, action, input;
typedef int (*fn_t)(const gesture_t *gest, void *user);
const fn_t FNS[] = { on_pan, on_rotate, on_zoom };
keymap_t keymap;

goxel_reset();
// Default keymaps.
if (arrlen(goxel.keymaps) == 0) {
keymap.action = 0;
keymap.input = GESTURE_RMB;
arrput(goxel.keymaps, keymap);
keymap.action = 0;
keymap.input = GESTURE_MMB | GESTURE_SHIFT;
arrput(goxel.keymaps, keymap);
keymap.action = 2;
keymap.input = GESTURE_MMB | GESTURE_CTRL;
arrput(goxel.keymaps, keymap);
keymap.action = 1;
keymap.input = GESTURE_MMB;
arrput(goxel.keymaps, keymap);
}

goxel_remove_gesture(on_pan);
goxel_remove_gesture(on_rotate);
goxel_remove_gesture(on_zoom);

for (i = 0; i < arrlen(goxel.keymaps); i++) {
action = goxel.keymaps[i].action;
input = goxel.keymaps[i].input;
goxel_add_gesture(GESTURE_DRAG, input, FNS[action]);
}
}

void goxel_reset(void)
{
image_delete(goxel.image);
goxel.image = image_new();
goxel.lang = "en";

settings_load();
goxel_update_keymaps();

// Put plane horizontal at the origin.
plane_from_vectors(goxel.plane,
Expand Down Expand Up @@ -817,8 +867,8 @@ void goxel_mouse_in_view(const float viewport[4], const inputs_t *inputs,
int i;

painter_t painter = goxel.painter;
gesture_update(goxel.gestures_count, goxel.gestures,
inputs, viewport, NULL);
gesture_update(arrlen(goxel.gestures), goxel.gestures, inputs, viewport,
NULL);

for (i = 0; i < goxel.gesture3ds_count; i++) {
gest3d = &goxel.gesture3ds[i];
Expand Down
18 changes: 15 additions & 3 deletions src/goxel.h
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,11 @@ enum {
SNAP_ROUNDED = 1 << 10, // Round the result.
};

typedef struct keymap {
int action; // 0: pan, 1: rotate, 2, zoom.
int input; // union of GESTURE enum.
} keymap_t;

typedef struct goxel
{
int screen_size[2];
Expand Down Expand Up @@ -519,9 +524,8 @@ typedef struct goxel

int view_effects; // EFFECT_WIREFRAME | EFFECT_GRID | EFFECT_EDGES

// All the gestures we listen to. Up to 16.
gesture_t *gestures[16];
int gestures_count;
// Stb array of all the gestures we listen to.
gesture_t **gestures;

// All the 3d gestures we listen to.
gesture3d_t gesture3ds[32];
Expand All @@ -545,6 +549,9 @@ typedef struct goxel

const char *lang; // Current set language.

// Arr array of keymap settings (different from shortcuts).
keymap_t *keymaps;

} goxel_t;

// the global goxel instance.
Expand Down Expand Up @@ -655,6 +662,11 @@ void goxel_apply_color_filter(
*/
bool goxel_gesture3d(const gesture3d_t *gesture);

/*
* Call this if the keymaps array has changed.
*/
void goxel_update_keymaps(void);

// Section: tests

/* Function: tests_run
Expand Down
159 changes: 159 additions & 0 deletions src/gui/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "goxel.h"
#include <errno.h> // IWYU pragma: keep.

#include "../ext_src/stb/stb_ds.h"

#include "utils/ini.h"

static int shortcut_callback(action_t *action, void *user)
Expand All @@ -37,6 +39,81 @@ static int shortcut_callback(action_t *action, void *user)
return 0;
}

static void on_keymap(int idx, keymap_t *keymap)
{
int i;
char id[32];
bool selected;
const char *preview = "";

const struct {
const char *label;
int input;
} input_choices[] = {
{ "Right Mouse", GESTURE_RMB },
{ "Middle Mouse", GESTURE_MMB },
{ "Ctrl Right Mouse", GESTURE_CTRL | GESTURE_RMB },
{ "Ctrl Middle Mouse", GESTURE_CTRL | GESTURE_MMB },
{ "Shift Right Mouse", GESTURE_SHIFT | GESTURE_RMB },
{ "Shift Middle Mouse", GESTURE_SHIFT | GESTURE_MMB },
};

const char *action_choices[] = { "Pan", "Rotate", "Zoom" };

if (keymap->action < 0 || keymap->action > 2) {
return;
}

snprintf(id, sizeof(id), "keymap_%d", idx);
gui_push_id(id);

if (gui_combo_begin("##action", action_choices[keymap->action])) {
for (i = 0; i < ARRAY_SIZE(action_choices); i++) {
selected = i == keymap->action;
if (gui_combo_item(action_choices[i], selected)) {
keymap->action = input_choices[i].input;
settings_save();
}
}
gui_combo_end();
}
gui_next_column();

for (i = 0; i < ARRAY_SIZE(input_choices); i++) {
if (input_choices[i].input == keymap->input) {
preview = input_choices[i].label;
break;
}
}

if (gui_combo_begin("##input", preview)) {
for (i = 0; i < ARRAY_SIZE(input_choices); i++) {
selected = input_choices[i].input == keymap->input;
if (gui_combo_item(input_choices[i].label, selected)) {
keymap->input = input_choices[i].input;
settings_save();
}
}
gui_combo_end();
}

gui_next_column();

if (gui_button("Delete", 0, 0)) {
arrdel(goxel.keymaps, idx);
}

gui_next_column();

gui_pop_id();
}

static void on_add_keymap_button(void)
{
keymap_t keymap = { 0, GESTURE_RMB };
arrput(goxel.keymaps, keymap);
settings_save();
}

int gui_settings_popup(void *data)
{
Expand Down Expand Up @@ -92,12 +169,50 @@ int gui_settings_popup(void *data)
gui_columns(1);
} gui_section_end();

if (gui_section_begin("Keymaps", GUI_SECTION_COLLAPSABLE_CLOSED)) {
gui_columns(3);
gui_separator();
for (i = 0; i < arrlen(goxel.keymaps); i++) {
on_keymap(i, &goxel.keymaps[i]);
}
gui_separator();
gui_columns(1);
if (gui_button(_(ADD), 0, 0)) {
on_add_keymap_button();
}
}
gui_section_end();

gui_popup_bottom_begin();
ret = gui_button(_(OK), 0, 0);
gui_popup_bottom_end();
return ret;
}

static void add_keymap(const char *name, const char *value)
{
keymap_t keymap = {
.action = -1,
.input = 0,
};

if (strcmp(name, "pan") == 0) keymap.action = 0;
if (strcmp(name, "rotate") == 0) keymap.action = 1;
if (strcmp(name, "zoom") == 0) keymap.action = 2;

if (strcasestr(value, "right mouse")) keymap.input |= GESTURE_RMB;
if (strcasestr(value, "middle mouse")) keymap.input |= GESTURE_MMB;
if (strcasestr(value, "shift")) keymap.input |= GESTURE_SHIFT;
if (strcasestr(value, "ctrl")) keymap.input |= GESTURE_CTRL;

if (keymap.action == -1 || keymap.input == 0) {
LOG_W("Cannot parse keymap %s = %s", name, value);
return;
}

arrput(goxel.keymaps, keymap);
}

static int settings_ini_handler(void *user, const char *section,
const char *name, const char *value,
int lineno)
Expand All @@ -120,6 +235,9 @@ static int settings_ini_handler(void *user, const char *section,
LOG_W("Cannot set shortcut for unknown action '%s'", name);
}
}
if (strcmp(section, "keymaps") == 0) {
add_keymap(name, value);
}
return 0;
}

Expand All @@ -128,6 +246,7 @@ void settings_load(void)
char path[1024];
snprintf(path, sizeof(path), "%s/settings.ini", sys_get_user_dir());
LOG_I("Read settings file: %s", path);
arrfree(goxel.keymaps);
ini_parse(path, settings_ini_handler, NULL);
actions_check_shortcuts();
}
Expand All @@ -140,6 +259,41 @@ static int shortcut_save_callback(action_t *a, void *user)
return 0;
}

static void save_keymaps(FILE *file)
{
int i, action, input;

fprintf(file, "[keymaps]\n");
for (i = 0; i < arrlen(goxel.keymaps); i++) {
action = goxel.keymaps[i].action;
input = goxel.keymaps[i].input;
switch (action) {
case 0:
fprintf(file, "pan=");
break;
case 1:
fprintf(file, "rotate=");
break;
case 2:
fprintf(file, "zoom=");
break;
}
if (input & GESTURE_CTRL) {
fprintf(file, "Ctrl ");
}
if (input & GESTURE_SHIFT) {
fprintf(file, "Shift ");
}
if (input & GESTURE_MMB) {
fprintf(file, "Middle Mouse");
}
if (input & GESTURE_RMB) {
fprintf(file, "Right Mouse");
}
fprintf(file, "\n");
}
}

void settings_save(void)
{
char path[1024];
Expand All @@ -156,9 +310,14 @@ void settings_save(void)
fprintf(file, "[ui]\n");
fprintf(file, "theme=%s\n", theme_get()->name);
fprintf(file, "language=%s\n", goxel.lang);
fprintf(file, "\n");

fprintf(file, "[shortcuts]\n");
actions_iter(shortcut_save_callback, file);

fprintf(file, "\n");
save_keymaps(file);
fprintf(file, "\n");

fclose(file);
}

0 comments on commit b880f34

Please sign in to comment.