feat: implement grid-of-grids with 8 separate 8x8 clip grids and zoom mode
Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
This commit is contained in:
13
dispatcher.c
13
dispatcher.c
@@ -168,12 +168,12 @@ static AppState clip_trigger(AppState state, int clip_index) {
|
||||
}
|
||||
|
||||
static AppState scene_trigger(AppState state, int scene_index) {
|
||||
if (scene_index < 0 || scene_index >= MAX_SCENES) return state;
|
||||
if (scene_index < 0 || scene_index >= MAX_SCENES * 8) return state; // 8 grids
|
||||
|
||||
// Save undo info for all clips in the scene as a batch
|
||||
int batch_start = state.undo.undo_index;
|
||||
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
|
||||
int clip_idx = CLIP_INDEX(scene_index, ch);
|
||||
int clip_idx = scene_index * MAX_CHANNELS + ch;
|
||||
save_undo_state(&state, clip_idx);
|
||||
}
|
||||
// Mark all entries in this batch with the batch size
|
||||
@@ -185,7 +185,7 @@ static AppState scene_trigger(AppState state, int scene_index) {
|
||||
|
||||
// Now apply the changes
|
||||
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
|
||||
int clip_idx = CLIP_INDEX(scene_index, ch);
|
||||
int clip_idx = scene_index * MAX_CHANNELS + ch;
|
||||
state = clip_trigger(state, clip_idx);
|
||||
}
|
||||
|
||||
@@ -395,6 +395,7 @@ AppState reducer(AppState state, Action action) {
|
||||
|
||||
case ACTION_MIDI_NOTE_ON: {
|
||||
int clip_index = action.data.midi_note_on.note % MAX_CLIPS;
|
||||
save_undo_state(&state, clip_index);
|
||||
return clip_trigger(state, clip_index);
|
||||
}
|
||||
|
||||
@@ -466,7 +467,11 @@ AppState reducer(AppState state, Action action) {
|
||||
clip->buffer_size = 0;
|
||||
clip->write_position = 0;
|
||||
clip->read_position = 0;
|
||||
if (clip->buffer) memset(clip->buffer, 0, MAX_BUFFER_SIZE * sizeof(float));
|
||||
if (clip->buffer) {
|
||||
memset(clip->buffer, 0, MAX_BUFFER_SIZE * sizeof(float));
|
||||
} else {
|
||||
clip->buffer = (float *)calloc(MAX_BUFFER_SIZE, sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
// Reset Carla host
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#define MAX_SCENES 8
|
||||
#define MAX_CHANNELS 8
|
||||
#define MAX_CLIPS (MAX_SCENES * MAX_CHANNELS) // 64
|
||||
#define MAX_CLIPS 512 // 8 grids * 8 scenes * 8 channels
|
||||
#define MAX_BUFFER_SIZE 441000 // 10 seconds at 44.1kHz
|
||||
#define MAX_UNDO_HISTORY 256
|
||||
|
||||
|
||||
25
engine.c
25
engine.c
@@ -95,18 +95,21 @@ static int process_callback(jack_nframes_t nframes, void *arg) {
|
||||
rack_in[i] = 0.0f;
|
||||
|
||||
for (int s = 0; s < MAX_SCENES; s++) {
|
||||
int clip_idx = CLIP_INDEX(s, ch);
|
||||
Clip *clip = &state.clips[clip_idx];
|
||||
|
||||
if (clip->state == CLIP_RECORDING) {
|
||||
if (clip->write_position < MAX_BUFFER_SIZE) {
|
||||
clip->buffer[clip->write_position++] = audio_in[ch][i];
|
||||
// Iterate over all grids for this scene and channel
|
||||
for (int g = 0; g < 8; g++) {
|
||||
int clip_idx = g * GRID_ROWS * GRID_COLS + s * GRID_COLS + ch;
|
||||
Clip *clip = &state.clips[clip_idx];
|
||||
|
||||
if (clip->state == CLIP_RECORDING) {
|
||||
if (clip->write_position < MAX_BUFFER_SIZE) {
|
||||
clip->buffer[clip->write_position++] = audio_in[ch][i];
|
||||
}
|
||||
}
|
||||
|
||||
if (clip->state == CLIP_LOOPING && clip->buffer_size > 0) {
|
||||
rack_in[i] += clip->buffer[clip->read_position];
|
||||
clip->read_position = (clip->read_position + 1) % clip->buffer_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (clip->state == CLIP_LOOPING && clip->buffer_size > 0) {
|
||||
rack_in[i] += clip->buffer[clip->read_position];
|
||||
clip->read_position = (clip->read_position + 1) % clip->buffer_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
main.c
8
main.c
@@ -84,6 +84,14 @@ int main(int argc, char *argv[]) {
|
||||
for (int i = 0; i < MAX_CLIPS; i++) {
|
||||
initial_state.clips[i].state = CLIP_EMPTY;
|
||||
initial_state.clips[i].buffer = (float *)calloc(MAX_BUFFER_SIZE, sizeof(float));
|
||||
if (!initial_state.clips[i].buffer) {
|
||||
fprintf(stderr, "Failed to allocate buffer for clip %d\n", i);
|
||||
// Cleanup previously allocated buffers
|
||||
for (int j = 0; j < i; j++) {
|
||||
free(initial_state.clips[j].buffer);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
initial_state.clips[i].buffer_size = 0;
|
||||
initial_state.clips[i].write_position = 0;
|
||||
initial_state.clips[i].read_position = 0;
|
||||
|
||||
193
tui.c
193
tui.c
@@ -31,6 +31,10 @@ static int selected_row = 0;
|
||||
static int selected_col = 0;
|
||||
static bool show_help = false;
|
||||
|
||||
// Grid of grids state
|
||||
static int selected_grid = 0; // Which grid we're viewing (0-7)
|
||||
static bool zoom_mode = false; // Whether we're in zoom mode
|
||||
|
||||
// View modes
|
||||
typedef enum {
|
||||
VIEW_GRID,
|
||||
@@ -95,8 +99,8 @@ static int state_to_color(ClipState state) {
|
||||
}
|
||||
|
||||
// Get clip index from grid position
|
||||
static int grid_to_clip_index(int row, int col) {
|
||||
return row * GRID_COLS + col;
|
||||
static int grid_to_clip_index(int grid, int row, int col) {
|
||||
return grid * GRID_ROWS * GRID_COLS + row * GRID_COLS + col;
|
||||
}
|
||||
|
||||
// Check if a cell is in the visual selection
|
||||
@@ -131,7 +135,7 @@ static int* get_selected_clips(int *count) {
|
||||
int idx = 0;
|
||||
for (int r = min_row; r <= max_row; r++) {
|
||||
for (int c = min_col; c <= max_col; c++) {
|
||||
clips[idx++] = grid_to_clip_index(r, c);
|
||||
clips[idx++] = grid_to_clip_index(selected_grid, r, c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,20 +167,27 @@ static void yank_clips(int *clip_indices, int count) {
|
||||
static void paste_clips(void) {
|
||||
if (!yank_buffer.clip_indices || yank_buffer.count == 0) return;
|
||||
|
||||
int first_yanked_row = yank_buffer.clip_indices[0] / GRID_COLS;
|
||||
int first_yanked_col = yank_buffer.clip_indices[0] % GRID_COLS;
|
||||
int first_yanked_grid = yank_buffer.clip_indices[0] / (GRID_ROWS * GRID_COLS);
|
||||
int first_yanked_row = (yank_buffer.clip_indices[0] % (GRID_ROWS * GRID_COLS)) / GRID_COLS;
|
||||
int first_yanked_col = (yank_buffer.clip_indices[0] % (GRID_ROWS * GRID_COLS)) % GRID_COLS;
|
||||
int grid_offset = selected_grid - first_yanked_grid;
|
||||
int row_offset = selected_row - first_yanked_row;
|
||||
int col_offset = selected_col - first_yanked_col;
|
||||
|
||||
for (int i = 0; i < yank_buffer.count; i++) {
|
||||
int orig_row = yank_buffer.clip_indices[i] / GRID_COLS;
|
||||
int orig_col = yank_buffer.clip_indices[i] % GRID_COLS;
|
||||
int orig_grid = yank_buffer.clip_indices[i] / (GRID_ROWS * GRID_COLS);
|
||||
int remainder = yank_buffer.clip_indices[i] % (GRID_ROWS * GRID_COLS);
|
||||
int orig_row = remainder / GRID_COLS;
|
||||
int orig_col = remainder % GRID_COLS;
|
||||
|
||||
int new_grid = orig_grid + grid_offset;
|
||||
int new_row = orig_row + row_offset;
|
||||
int new_col = orig_col + col_offset;
|
||||
|
||||
if (new_row >= 0 && new_row < GRID_ROWS && new_col >= 0 && new_col < GRID_COLS) {
|
||||
int new_clip_idx = grid_to_clip_index(new_row, new_col);
|
||||
if (new_grid >= 0 && new_grid < NUM_GRIDS &&
|
||||
new_row >= 0 && new_row < GRID_ROWS &&
|
||||
new_col >= 0 && new_col < GRID_COLS) {
|
||||
int new_clip_idx = grid_to_clip_index(new_grid, new_row, new_col);
|
||||
// Trigger three times to cycle: empty -> recording -> looping -> stopped
|
||||
for (int j = 0; j < 3; j++) {
|
||||
Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = new_clip_idx } };
|
||||
@@ -190,7 +201,7 @@ static void paste_clips(void) {
|
||||
static void set_mark(char mark_char) {
|
||||
if (mark_char >= 'a' && mark_char <= 'z') {
|
||||
int idx = mark_char - 'a';
|
||||
marks[idx] = grid_to_clip_index(selected_row, selected_col);
|
||||
marks[idx] = grid_to_clip_index(selected_grid, selected_row, selected_col);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,9 +210,11 @@ static void go_to_mark(char mark_char) {
|
||||
if (mark_char >= 'a' && mark_char <= 'z') {
|
||||
int idx = mark_char - 'a';
|
||||
int clip_idx = marks[idx];
|
||||
if (clip_idx >= 0 && clip_idx < GRID_ROWS * GRID_COLS) {
|
||||
selected_row = clip_idx / GRID_COLS;
|
||||
selected_col = clip_idx % GRID_COLS;
|
||||
if (clip_idx >= 0 && clip_idx < NUM_GRIDS * GRID_ROWS * GRID_COLS) {
|
||||
selected_grid = clip_idx / (GRID_ROWS * GRID_COLS);
|
||||
int remainder = clip_idx % (GRID_ROWS * GRID_COLS);
|
||||
selected_row = remainder / GRID_COLS;
|
||||
selected_col = remainder % GRID_COLS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,7 +222,7 @@ static void go_to_mark(char mark_char) {
|
||||
// Play next scene (next row)
|
||||
static void play_next_scene(void) {
|
||||
int next_row = (selected_row + 1) % GRID_ROWS;
|
||||
Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = next_row } };
|
||||
Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = next_row + selected_grid * GRID_ROWS } };
|
||||
g_dispatch(action);
|
||||
selected_row = next_row;
|
||||
}
|
||||
@@ -217,7 +230,7 @@ static void play_next_scene(void) {
|
||||
// Play previous scene (previous row)
|
||||
static void play_prev_scene(void) {
|
||||
int prev_row = (selected_row - 1 + GRID_ROWS) % GRID_ROWS;
|
||||
Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = prev_row } };
|
||||
Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = prev_row + selected_grid * GRID_ROWS } };
|
||||
g_dispatch(action);
|
||||
selected_row = prev_row;
|
||||
}
|
||||
@@ -582,12 +595,12 @@ static bool handle_rack_view(int ch) {
|
||||
}
|
||||
|
||||
// Draw a single cell
|
||||
static void draw_cell(int row, int col, bool selected) {
|
||||
int clip_idx = grid_to_clip_index(row, col);
|
||||
static void draw_cell(int grid, int row, int col, bool selected) {
|
||||
int clip_idx = grid_to_clip_index(grid, row, col);
|
||||
AppState state = dispatcher_get_state();
|
||||
Clip *clip = &state.clips[clip_idx];
|
||||
|
||||
int y = row * CELL_HEIGHT + 1;
|
||||
int y = row * CELL_HEIGHT + 3; // Offset by 2 for grid selector
|
||||
int x = col * CELL_WIDTH + 1;
|
||||
|
||||
int color = state_to_color(clip->state);
|
||||
@@ -624,27 +637,77 @@ static void draw_cell(int row, int col, bool selected) {
|
||||
static void draw_grid(void) {
|
||||
clear();
|
||||
|
||||
if (zoom_mode) {
|
||||
// Draw grid selector overview
|
||||
attron(A_BOLD);
|
||||
mvprintw(0, 0, "JACK Looper - Grid Selector (h/j/k/l navigate, Enter select, z exit)");
|
||||
attroff(A_BOLD);
|
||||
|
||||
// Draw mini grids
|
||||
for (int g = 0; g < NUM_GRIDS; g++) {
|
||||
int gx = (g % 4) * 20;
|
||||
int gy = (g / 4) * 10 + 2;
|
||||
|
||||
if (g == selected_grid) {
|
||||
attron(A_REVERSE);
|
||||
}
|
||||
mvprintw(gy, gx, "Grid %d", g);
|
||||
if (g == selected_grid) {
|
||||
attroff(A_REVERSE);
|
||||
}
|
||||
|
||||
// Draw mini representation (4x4 sampling of the 8x8 grid)
|
||||
for (int r = 0; r < 4; r++) {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
int clip_idx = grid_to_clip_index(g, r * 2, c * 2);
|
||||
AppState state = dispatcher_get_state();
|
||||
Clip *clip = &state.clips[clip_idx];
|
||||
int color = state_to_color(clip->state);
|
||||
attron(COLOR_PAIR(color));
|
||||
mvaddch(gy + 1 + r, gx + 1 + c, ' ');
|
||||
attroff(COLOR_PAIR(color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal grid view
|
||||
attron(A_BOLD);
|
||||
mvprintw(0, 0, "JACK Looper - 8x8 Clip Grid");
|
||||
mvprintw(0, 0, "JACK Looper - 8x8 Clip Grid (Grid %d)", selected_grid);
|
||||
attroff(A_BOLD);
|
||||
|
||||
// Draw grid selector bar at top
|
||||
mvprintw(1, 0, "Grid: ");
|
||||
for (int g = 0; g < NUM_GRIDS; g++) {
|
||||
if (g == selected_grid) {
|
||||
attron(A_REVERSE);
|
||||
}
|
||||
mvprintw(1, 6 + g * 4, "G%d", g);
|
||||
if (g == selected_grid) {
|
||||
attroff(A_REVERSE);
|
||||
}
|
||||
}
|
||||
|
||||
AppState state = dispatcher_get_state();
|
||||
|
||||
for (int row = 0; row < GRID_ROWS; row++) {
|
||||
for (int col = 0; col < GRID_COLS; col++) {
|
||||
bool selected = (row == selected_row && col == selected_col);
|
||||
draw_cell(row, col, selected);
|
||||
draw_cell(selected_grid, row, col, selected);
|
||||
}
|
||||
}
|
||||
|
||||
int clip_idx = grid_to_clip_index(selected_row, selected_col);
|
||||
int clip_idx = grid_to_clip_index(selected_grid, selected_row, selected_col);
|
||||
Clip *clip = &state.clips[clip_idx];
|
||||
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 1, 0,
|
||||
"Selected: Clip %d | State: %s | Buffer: %zu samples",
|
||||
clip_idx, clip_state_to_string(clip->state), clip->buffer_size);
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 3, 0,
|
||||
"Selected: Clip %d | Grid %d | State: %s | Buffer: %zu samples",
|
||||
clip_idx, selected_grid, clip_state_to_string(clip->state), clip->buffer_size);
|
||||
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 2, 0,
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 4, 0,
|
||||
"Transport: %s | Clock: %s | BPM: %.1f | Quantize: %s | Threshold: %u",
|
||||
transport_state_to_string(state.transport_state),
|
||||
clock_source_to_string(state.clock_source),
|
||||
@@ -659,23 +722,24 @@ static void draw_grid(void) {
|
||||
case MODE_MOVE: mode_str = "MOVE"; break;
|
||||
default: mode_str = "?"; break;
|
||||
}
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 3, 0, "Mode: %s", mode_str);
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 5, 0, "Mode: %s", mode_str);
|
||||
|
||||
if (show_help) {
|
||||
attron(COLOR_PAIR(COLOR_HELP));
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 4, 0, "=== Help ===");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 5, 0, "h/j/k/l - Navigate grid (left/down/up/right)");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 6, 0, "t - Trigger selected clip");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 7, 0, "r - Reset selected clip");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 8, 0, "s - Trigger scene (current row)");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 9, 0, "Space - Play/Pause transport");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 10, 0, "S - Stop transport");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 11, 0, "C - Toggle clock source (Internal/MIDI)");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 12, 0, "q - Toggle quantize mode (off/beat/bar)");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 13, 0, "T - Set quantize threshold");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 14, 0, "x - Reset transport position");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 15, 0, "? - Toggle help");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 16, 0, "Esc/q - Quit");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 6, 0, "=== Help ===");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 7, 0, "h/j/k/l - Navigate grid (left/down/up/right)");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 8, 0, "t - Trigger selected clip");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 9, 0, "r - Reset selected clip");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 10, 0, "s - Trigger scene (current row)");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 11, 0, "Space - Play/Pause transport");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 12, 0, "S - Stop transport");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 13, 0, "C - Toggle clock source (Internal/MIDI)");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 14, 0, "q - Toggle quantize mode (off/beat/bar)");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 15, 0, "T - Set quantize threshold");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 16, 0, "x - Reset transport position");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 17, 0, "z - Toggle grid selector (zoom mode)");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 18, 0, "? - Toggle help");
|
||||
mvprintw(GRID_ROWS * CELL_HEIGHT + 19, 0, "Esc/q - Quit");
|
||||
attroff(COLOR_PAIR(COLOR_HELP));
|
||||
}
|
||||
|
||||
@@ -793,7 +857,7 @@ static bool handle_command_mode(void) {
|
||||
|
||||
// Handle mouse events
|
||||
static void handle_mouse_event(MEVENT *event) {
|
||||
int grid_row = (event->y - 1) / CELL_HEIGHT;
|
||||
int grid_row = (event->y - 3) / CELL_HEIGHT; // Offset for grid selector
|
||||
int grid_col = (event->x - 1) / CELL_WIDTH;
|
||||
|
||||
if (grid_row < 0 || grid_row >= GRID_ROWS || grid_col < 0 || grid_col >= GRID_COLS) {
|
||||
@@ -803,24 +867,24 @@ static void handle_mouse_event(MEVENT *event) {
|
||||
if (event->bstate & BUTTON1_CLICKED) {
|
||||
selected_row = grid_row;
|
||||
selected_col = grid_col;
|
||||
int clip_idx = grid_to_clip_index(selected_row, selected_col);
|
||||
int clip_idx = grid_to_clip_index(selected_grid, selected_row, selected_col);
|
||||
Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = clip_idx } };
|
||||
g_dispatch(action);
|
||||
} else if (event->bstate & BUTTON3_CLICKED) {
|
||||
selected_row = grid_row;
|
||||
selected_col = grid_col;
|
||||
int clip_idx = grid_to_clip_index(selected_row, selected_col);
|
||||
int clip_idx = grid_to_clip_index(selected_grid, selected_row, selected_col);
|
||||
Action action = { .type = ACTION_RESET_CLIP, .data.reset_clip = { .clip_index = clip_idx } };
|
||||
g_dispatch(action);
|
||||
} else if (event->bstate & BUTTON2_CLICKED) {
|
||||
selected_row = grid_row;
|
||||
selected_col = grid_col;
|
||||
Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = selected_row } };
|
||||
Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = selected_row + selected_grid * GRID_ROWS } };
|
||||
g_dispatch(action);
|
||||
} else if (event->bstate & BUTTON1_DOUBLE_CLICKED) {
|
||||
selected_row = grid_row;
|
||||
selected_col = grid_col;
|
||||
Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = selected_row } };
|
||||
Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = selected_row + selected_grid * GRID_ROWS } };
|
||||
g_dispatch(action);
|
||||
}
|
||||
}
|
||||
@@ -916,6 +980,35 @@ void tui_run(Engine *engine) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle zoom mode navigation
|
||||
if (zoom_mode) {
|
||||
switch (ch) {
|
||||
case 'h': case KEY_LEFT:
|
||||
selected_grid = (selected_grid - 1 + NUM_GRIDS) % NUM_GRIDS;
|
||||
break;
|
||||
case 'l': case KEY_RIGHT:
|
||||
selected_grid = (selected_grid + 1) % NUM_GRIDS;
|
||||
break;
|
||||
case 'j': case KEY_DOWN:
|
||||
selected_grid = (selected_grid + 1) % NUM_GRIDS;
|
||||
break;
|
||||
case 'k': case KEY_UP:
|
||||
selected_grid = (selected_grid - 1 + NUM_GRIDS) % NUM_GRIDS;
|
||||
break;
|
||||
case '\n': case '\r':
|
||||
// Select this grid and exit zoom mode
|
||||
zoom_mode = false;
|
||||
break;
|
||||
case 'z':
|
||||
zoom_mode = false;
|
||||
break;
|
||||
case 27: case 'Q':
|
||||
return;
|
||||
}
|
||||
draw_grid();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_mode == MODE_MOVE) {
|
||||
switch (ch) {
|
||||
case 'h': case KEY_LEFT:
|
||||
@@ -995,19 +1088,19 @@ void tui_run(Engine *engine) {
|
||||
selected_col = (selected_col + 1) % GRID_COLS;
|
||||
break;
|
||||
case 't': {
|
||||
int clip_idx = grid_to_clip_index(selected_row, selected_col);
|
||||
int clip_idx = grid_to_clip_index(selected_grid, selected_row, selected_col);
|
||||
Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = clip_idx } };
|
||||
g_dispatch(action);
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
int clip_idx = grid_to_clip_index(selected_row, selected_col);
|
||||
int clip_idx = grid_to_clip_index(selected_grid, selected_row, selected_col);
|
||||
Action action = { .type = ACTION_RESET_CLIP, .data.reset_clip = { .clip_index = clip_idx } };
|
||||
g_dispatch(action);
|
||||
break;
|
||||
}
|
||||
case 'y': {
|
||||
int clip_idx = grid_to_clip_index(selected_row, selected_col);
|
||||
int clip_idx = grid_to_clip_index(selected_grid, selected_row, selected_col);
|
||||
yank_clips(&clip_idx, 1);
|
||||
break;
|
||||
}
|
||||
@@ -1015,7 +1108,7 @@ void tui_run(Engine *engine) {
|
||||
paste_clips();
|
||||
break;
|
||||
case 's': {
|
||||
Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = selected_row } };
|
||||
Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = selected_row + selected_grid * GRID_ROWS } };
|
||||
g_dispatch(action);
|
||||
break;
|
||||
}
|
||||
@@ -1070,6 +1163,10 @@ void tui_run(Engine *engine) {
|
||||
if (should_quit) return;
|
||||
break;
|
||||
}
|
||||
case 'z': {
|
||||
zoom_mode = !zoom_mode;
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
show_help = !show_help;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user