|
@ -665,6 +665,7 @@ typedef struct { |
|
|
Usz drag_start_x; |
|
|
Usz drag_start_x; |
|
|
int win_h; |
|
|
int win_h; |
|
|
int win_w; |
|
|
int win_w; |
|
|
|
|
|
int grid_h; |
|
|
int grid_scroll_y; // not sure if i like this being int
|
|
|
int grid_scroll_y; // not sure if i like this being int
|
|
|
int grid_scroll_x; |
|
|
int grid_scroll_x; |
|
|
bool needs_remarking : 1; |
|
|
bool needs_remarking : 1; |
|
@ -673,6 +674,7 @@ typedef struct { |
|
|
bool draw_event_list : 1; |
|
|
bool draw_event_list : 1; |
|
|
bool is_mouse_down : 1; |
|
|
bool is_mouse_down : 1; |
|
|
bool is_mouse_dragging : 1; |
|
|
bool is_mouse_dragging : 1; |
|
|
|
|
|
bool is_hud_visible : 1; |
|
|
} App_state; |
|
|
} App_state; |
|
|
|
|
|
|
|
|
void app_init(App_state* a) { |
|
|
void app_init(App_state* a) { |
|
@ -699,6 +701,7 @@ void app_init(App_state* a) { |
|
|
a->midi_mode = NULL; |
|
|
a->midi_mode = NULL; |
|
|
a->win_h = 0; |
|
|
a->win_h = 0; |
|
|
a->win_w = 0; |
|
|
a->win_w = 0; |
|
|
|
|
|
a->grid_h = 0; |
|
|
a->grid_scroll_y = 0; |
|
|
a->grid_scroll_y = 0; |
|
|
a->grid_scroll_x = 0; |
|
|
a->grid_scroll_x = 0; |
|
|
a->drag_start_y = 0; |
|
|
a->drag_start_y = 0; |
|
@ -709,6 +712,7 @@ void app_init(App_state* a) { |
|
|
a->draw_event_list = false; |
|
|
a->draw_event_list = false; |
|
|
a->is_mouse_down = false; |
|
|
a->is_mouse_down = false; |
|
|
a->is_mouse_dragging = false; |
|
|
a->is_mouse_dragging = false; |
|
|
|
|
|
a->is_hud_visible = false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void app_deinit(App_state* a) { |
|
|
void app_deinit(App_state* a) { |
|
@ -930,7 +934,60 @@ void app_do_stuff(App_state* a) { |
|
|
|
|
|
|
|
|
static double ms_to_sec(double ms) { return ms / 1000.0; } |
|
|
static double ms_to_sec(double ms) { return ms / 1000.0; } |
|
|
|
|
|
|
|
|
void app_force_draw_dirty(App_state* a) { a->is_draw_dirty = true; } |
|
|
static inline Isz isz_clamp(Isz x, Isz low, Isz high) { |
|
|
|
|
|
return x < low ? low : x > high ? high : x; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// todo cleanup to use proper unsigned/signed w/ overflow check
|
|
|
|
|
|
Isz scroll_offset_on_axis_for_cursor_pos(Isz win_len, Isz cont_len, |
|
|
|
|
|
Isz cursor_pos, Isz pad, |
|
|
|
|
|
Isz cur_scroll) { |
|
|
|
|
|
if (win_len <= 0 || cont_len <= 0) |
|
|
|
|
|
return 0; |
|
|
|
|
|
if (cont_len <= win_len) |
|
|
|
|
|
return -((win_len - cont_len) / 2); |
|
|
|
|
|
if (pad * 2 >= win_len) { |
|
|
|
|
|
pad = (win_len - 1) / 2; |
|
|
|
|
|
} |
|
|
|
|
|
Isz min_vis_scroll = cursor_pos - win_len + 1 + pad; |
|
|
|
|
|
Isz max_vis_scroll = cursor_pos - pad; |
|
|
|
|
|
Isz new_scroll; |
|
|
|
|
|
if (cur_scroll < min_vis_scroll) |
|
|
|
|
|
new_scroll = min_vis_scroll; |
|
|
|
|
|
else if (cur_scroll > max_vis_scroll) |
|
|
|
|
|
new_scroll = max_vis_scroll; |
|
|
|
|
|
else |
|
|
|
|
|
new_scroll = cur_scroll; |
|
|
|
|
|
return isz_clamp(new_scroll, 0, cont_len - win_len); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void app_make_cursor_visible(App_state* a) { |
|
|
|
|
|
int grid_h = a->grid_h; |
|
|
|
|
|
int cur_scr_y = a->grid_scroll_y; |
|
|
|
|
|
int cur_scr_x = a->grid_scroll_x; |
|
|
|
|
|
int new_scr_y = (int)scroll_offset_on_axis_for_cursor_pos( |
|
|
|
|
|
grid_h, (Isz)a->field.height, (Isz)a->tui_cursor.y, 5, cur_scr_y); |
|
|
|
|
|
int new_scr_x = (int)scroll_offset_on_axis_for_cursor_pos( |
|
|
|
|
|
a->win_w, (Isz)a->field.width, (Isz)a->tui_cursor.x, 5, cur_scr_x); |
|
|
|
|
|
if (new_scr_y == cur_scr_y && new_scr_x == cur_scr_x) |
|
|
|
|
|
return; |
|
|
|
|
|
a->grid_scroll_y = new_scr_y; |
|
|
|
|
|
a->grid_scroll_x = new_scr_x; |
|
|
|
|
|
a->is_draw_dirty = true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
enum { Hud_height = 2 }; |
|
|
|
|
|
|
|
|
|
|
|
void app_set_window_size(App_state* a, int win_h, int win_w) { |
|
|
|
|
|
bool draw_hud = win_h > Hud_height + 1; |
|
|
|
|
|
int grid_h = draw_hud ? win_h - 2 : win_h; |
|
|
|
|
|
a->win_h = win_h; |
|
|
|
|
|
a->win_w = win_w; |
|
|
|
|
|
a->grid_h = grid_h; |
|
|
|
|
|
a->is_draw_dirty = true; |
|
|
|
|
|
a->is_hud_visible = draw_hud; |
|
|
|
|
|
app_make_cursor_visible(a); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
void app_draw(App_state* a, WINDOW* win) { |
|
|
void app_draw(App_state* a, WINDOW* win) { |
|
|
werase(win); |
|
|
werase(win); |
|
@ -958,22 +1015,20 @@ void app_draw(App_state* a, WINDOW* win) { |
|
|
a->piano_bits); |
|
|
a->piano_bits); |
|
|
a->needs_remarking = false; |
|
|
a->needs_remarking = false; |
|
|
} |
|
|
} |
|
|
int win_h, win_w; |
|
|
int win_h = a->win_h; |
|
|
getmaxyx(win, win_h, win_w); |
|
|
int win_w = a->win_w; |
|
|
int hud_height = 2; |
|
|
tdraw_glyphs_grid_scrolled(win, 0, 0, a->grid_h, win_w, a->field.buffer, |
|
|
bool draw_hud = win_h > hud_height + 1; |
|
|
|
|
|
int grid_h = draw_hud ? win_h - 2 : win_h; |
|
|
|
|
|
tdraw_glyphs_grid_scrolled(win, 0, 0, grid_h, win_w, a->field.buffer, |
|
|
|
|
|
a->markmap_r.buffer, a->field.height, |
|
|
a->markmap_r.buffer, a->field.height, |
|
|
a->field.width, a->grid_scroll_y, a->grid_scroll_x, |
|
|
a->field.width, a->grid_scroll_y, a->grid_scroll_x, |
|
|
a->ruler_spacing_y, a->ruler_spacing_x); |
|
|
a->ruler_spacing_y, a->ruler_spacing_x); |
|
|
tdraw_grid_cursor(win, 0, 0, grid_h, win_w, a->field.buffer, a->field.height, |
|
|
tdraw_grid_cursor(win, 0, 0, a->grid_h, win_w, a->field.buffer, |
|
|
a->field.width, a->grid_scroll_y, a->grid_scroll_x, |
|
|
a->field.height, a->field.width, a->grid_scroll_y, |
|
|
a->tui_cursor.y, a->tui_cursor.x, a->tui_cursor.h, |
|
|
a->grid_scroll_x, a->tui_cursor.y, a->tui_cursor.x, |
|
|
a->tui_cursor.w, a->input_mode, a->is_playing); |
|
|
a->tui_cursor.h, a->tui_cursor.w, a->input_mode, |
|
|
if (draw_hud) { |
|
|
a->is_playing); |
|
|
|
|
|
if (a->is_hud_visible) { |
|
|
char const* filename = a->filename ? a->filename : ""; |
|
|
char const* filename = a->filename ? a->filename : ""; |
|
|
tdraw_hud(win, win_h - hud_height, 0, hud_height, win_w, filename, |
|
|
tdraw_hud(win, win_h - Hud_height, 0, Hud_height, win_w, filename, |
|
|
a->field.height, a->field.width, a->ruler_spacing_y, |
|
|
a->field.height, a->field.width, a->ruler_spacing_y, |
|
|
a->ruler_spacing_x, a->tick_num, a->bpm, &a->tui_cursor, |
|
|
a->ruler_spacing_x, a->tick_num, a->bpm, &a->tui_cursor, |
|
|
a->input_mode); |
|
|
a->input_mode); |
|
@ -997,51 +1052,6 @@ void app_adjust_bpm(App_state* a, Isz delta_bpm) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static inline Isz isz_clamp(Isz x, Isz low, Isz high) { |
|
|
|
|
|
return x < low ? low : x > high ? high : x; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// todo cleanup to use proper unsigned/signed w/ overflow check
|
|
|
|
|
|
Isz scroll_offset_on_axis_for_cursor_pos(Isz win_len, Isz cont_len, |
|
|
|
|
|
Isz cursor_pos, Isz pad, |
|
|
|
|
|
Isz cur_scroll) { |
|
|
|
|
|
if (win_len <= 0 || cont_len <= 0) |
|
|
|
|
|
return 0; |
|
|
|
|
|
if (cont_len <= win_len) |
|
|
|
|
|
return -((win_len - cont_len) / 2); |
|
|
|
|
|
if (pad * 2 >= win_len) { |
|
|
|
|
|
pad = (win_len - 1) / 2; |
|
|
|
|
|
} |
|
|
|
|
|
Isz min_vis_scroll = cursor_pos - win_len + 1 + pad; |
|
|
|
|
|
Isz max_vis_scroll = cursor_pos - pad; |
|
|
|
|
|
Isz new_scroll; |
|
|
|
|
|
if (cur_scroll < min_vis_scroll) |
|
|
|
|
|
new_scroll = min_vis_scroll; |
|
|
|
|
|
else if (cur_scroll > max_vis_scroll) |
|
|
|
|
|
new_scroll = max_vis_scroll; |
|
|
|
|
|
else |
|
|
|
|
|
new_scroll = cur_scroll; |
|
|
|
|
|
return isz_clamp(new_scroll, 0, cont_len - win_len); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void app_make_cursor_visible(App_state* a) { |
|
|
|
|
|
int hud_height = 2; |
|
|
|
|
|
int win_h = a->win_h; |
|
|
|
|
|
bool draw_hud = win_h > hud_height + 1; |
|
|
|
|
|
int grid_h = draw_hud ? win_h - 2 : win_h; |
|
|
|
|
|
int cur_scr_y = a->grid_scroll_y; |
|
|
|
|
|
int cur_scr_x = a->grid_scroll_x; |
|
|
|
|
|
int new_scr_y = (int)scroll_offset_on_axis_for_cursor_pos( |
|
|
|
|
|
grid_h, (Isz)a->field.height, (Isz)a->tui_cursor.y, 5, cur_scr_y); |
|
|
|
|
|
int new_scr_x = (int)scroll_offset_on_axis_for_cursor_pos( |
|
|
|
|
|
a->win_w, (Isz)a->field.width, (Isz)a->tui_cursor.x, 5, cur_scr_x); |
|
|
|
|
|
if (new_scr_y == cur_scr_y && new_scr_x == cur_scr_x) |
|
|
|
|
|
return; |
|
|
|
|
|
a->grid_scroll_y = new_scr_y; |
|
|
|
|
|
a->grid_scroll_x = new_scr_x; |
|
|
|
|
|
a->is_draw_dirty = true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void app_move_cursor_relative(App_state* a, Isz delta_y, Isz delta_x) { |
|
|
void app_move_cursor_relative(App_state* a, Isz delta_y, Isz delta_x) { |
|
|
tui_cursor_move_relative(&a->tui_cursor, a->field.height, a->field.width, |
|
|
tui_cursor_move_relative(&a->tui_cursor, a->field.height, a->field.width, |
|
|
delta_y, delta_x); |
|
|
delta_y, delta_x); |
|
@ -1698,10 +1708,7 @@ int main(int argc, char** argv) { |
|
|
} |
|
|
} |
|
|
wclear(stdscr); |
|
|
wclear(stdscr); |
|
|
cont_win = derwin(stdscr, content_h, content_w, content_y, content_x); |
|
|
cont_win = derwin(stdscr, content_h, content_w, content_y, content_x); |
|
|
app_state.win_h = content_h; |
|
|
app_set_window_size(&app_state, content_h, content_w); |
|
|
app_state.win_w = content_w; |
|
|
|
|
|
app_make_cursor_visible(&app_state); |
|
|
|
|
|
app_force_draw_dirty(&app_state); |
|
|
|
|
|
} |
|
|
} |
|
|
} break; |
|
|
} break; |
|
|
case KEY_MOUSE: { |
|
|
case KEY_MOUSE: { |
|
|