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
|
#include "layout.h"
#include "array.h"
#include "config.h"
void calculate_layout(client_state* state, rect_t* prompt, rect_t* input, rect_t* options, bool recalc_options, bool recalc_pages) {
if (state->prompt && *state->prompt) {
*prompt = (rect_t){ .x = 0, .y = state->line_height * state->lines, .dx = state->prompt_text_buffer.pixel_len + state->horizontal_spacing, .dy = state->line_height };
}
else {
*prompt = (rect_t){ 0 };
}
// calculate input rect
*input = (rect_t){ .x = prompt->dx, .y = state->line_height * state->lines, .dx = state->width - prompt->dx, .dy = state->line_height };
if (state->lines == 0 && array_size(state->filtered_items) != 0) {
input->dx = state->width / 3.0f - input->x;
}
// calculate options rect
*options = (rect_t){0};
if (state->lines) {
options->dx = state->width;
options->dy = input->y;
} else {
options->x = input->x + input->dx;
options->dx = state->width - options->x;
options->dy = state->line_height;
}
if (recalc_options || recalc_pages) {
// calculate starting conditions
rect_t cur;
if (state->lines) {
cur = (rect_t){ .x = options->x, .y = input->y - state->line_height, .dx = options->dx, .dy = state->line_height };
} else {
cur = (rect_t){ .x = options->x, .y = options->y, .dx = 0.0f, .dy = state->line_height };
}
// calculate base filtered item rects
array_for_all(item_display_t, display, state->filtered_items) {
if (!state->lines) {
cur.dx = display->item->pixel_len + state->horizontal_spacing;
}
display->r = cur;
if (state->lines) {
cur.y -= state->line_height;
} else {
cur.x += cur.dx;
}
}
// calculate page indices
if (recalc_pages && !fancy_scroll) {
state->current_page = 0;
array_clear(state->page_indices);
if (state->lines) {
// this won't work if we add sized images and bullshit to the option list
for (size_t i = 0; i < array_size(state->filtered_items); i += state->lines) {
array_add(state->page_indices, i);
}
} else {
array_add(state->page_indices, 0);
float page_x = options->x;
size_t i = 0;
array_for_all(item_display_t, display, state->filtered_items) {
// check if past the end of the page
if (((display->r.x + display->r.dx) - page_x) / options->dx >= 1.f) {
page_x = display->r.x;
array_add(state->page_indices, i);
}
++i;
}
}
}
if (array_size(state->filtered_items) != 0) {
if (fancy_scroll) {
// calculate fancy scroll offset
if (state->lines) {
// calc vertical scroll
float selected_bot = state->scroll_offset + state->filtered_items[state->selected_filtered_item].r.y;
float selected_top = selected_bot + state->filtered_items[state->selected_filtered_item].r.dy;
if (selected_bot < options->y) {
state->scroll_offset -= selected_bot;
}
float diff = selected_top - (options->y + options->dy);
if (diff > 0) {
state->scroll_offset -= diff;
}
} else {
// calc horizontal scroll
item_display_t* selected = &state->filtered_items[state->selected_filtered_item];
float selected_left = state->scroll_offset + selected->r.x;
float selected_right = selected_left + selected->r.dx;
float right_diff = selected_right - (options->x + options->dx);
float left_diff = selected_left - options->x;
if (selected->r.dx > options->dx) {
state->scroll_offset -= left_diff;
}
else if (right_diff > 0) {
state->scroll_offset -= right_diff;
}
else if (left_diff < 0) {
state->scroll_offset -= left_diff;
}
}
// display all filtered items (could be optimized)
state->displayed_item_start = 0;
state->displayed_item_end = array_size(state->filtered_items);
} else {
// calculate page scroll offset
// determine what page the selected item is in
size_t selected_page = 0;
size_t selected_page_index = 0;
for(size_t i = 0; i < array_size(state->page_indices); ++i) {
if (state->selected_filtered_item < state->page_indices[i]) {
break;
}
selected_page = i;
selected_page_index = state->page_indices[i];
}
// determine scroll to put that page at the start
if (state->lines) {
rect_t r = state->filtered_items[selected_page_index].r;
state->scroll_offset = (options->y + options->dy) - (r.y + r.dy);
} else {
rect_t r = state->filtered_items[selected_page_index].r;
state->scroll_offset = options->x - r.x;
}
// display filtered items on the page
state->displayed_item_start = state->page_indices[selected_page];
state->displayed_item_end = (selected_page + 1 < array_size(state->page_indices)) ? state->page_indices[selected_page + 1] : array_size(state->filtered_items);
}
// apply scroll offset
array_for_all(item_display_t, display, state->filtered_items) {
if (state->lines) {
display->r.y += state->scroll_offset;
} else {
display->r.x += state->scroll_offset;
}
}
}
else {
state->displayed_item_start = 0;
state->displayed_item_end = 0;
}
}
}
|