๐Ÿ“ฆ RobLoach / nuklear_console

๐Ÿ“„ nuklear_console_row.h ยท 294 lines
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294#ifndef NK_CONSOLE_ROW_H__
#define NK_CONSOLE_ROW_H__

typedef struct nk_console_row_data {
    int activeChild;
    nk_bool widgets_added; /** Indicates if all the row's widgets have been added. */
} nk_console_row_data;

#if defined(__cplusplus)
extern "C" {
#endif

/**
 * Begin a new row in the console.
 *
 * @param parent The parent of which to create the new row.
 * @return The new row.
 */
NK_API nk_console* nk_console_row_begin(nk_console* parent);

/**
 * End the current row in the console.
 *
 * @param row The row to end.
 */
NK_API void nk_console_row_end(nk_console* row);
NK_API struct nk_rect nk_console_row_render(nk_console* console);

#if defined(__cplusplus)
}
#endif

#endif // NK_CONSOLE_ROW_H__

#if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY)
#ifndef NK_CONSOLE_ROW_IMPLEMENTATION_ONCE
#define NK_CONSOLE_ROW_IMPLEMENTATION_ONCE

#if defined(__cplusplus)
extern "C" {
#endif

static nk_console* nk_console_row_active_child(nk_console* row) {
    if (row == NULL || row->data == NULL) {
        return NULL;
    }

    int row_children_size = (int)cvector_size(row->children);
    if (row_children_size == 0) {
        return NULL;
    }

    nk_console_row_data* data = (nk_console_row_data*)row->data;
    if (data->activeChild < 0) {
        data->activeChild = 0;
    }
    else if (data->activeChild >= row_children_size) {
        data->activeChild = row_children_size - 1;
    }

    return row->children[data->activeChild];
}

// Find the index of the next selectable child by either going right (direction
// = 1) or left (direction = -1). Returns the index of the currently active
// child if no other selectable child is found.
static int nk_console_row_next_selectable_child(nk_console* row, int direction) {
    nk_console_row_data* data = (nk_console_row_data*)row->data;
    int numChildren = (int)cvector_size(row->children);
    for (int i = data->activeChild + direction; i >= 0 && i < numChildren; i += direction) {
        if (row->children[i]->selectable) {
            return i;
        }
    }

    return data->activeChild;
}

/**
 * Pick the nearest selectable child to the currently active child.
 *
 * @param row The row to pick the nearest selectable child from.
 * @return True or false if a new selectable child was found.
 */
static nk_bool nk_console_row_pick_nearest_selectable_child(nk_console* row) {
    // Ensure there is actually a possible active child.
    if (row == NULL || row->data == NULL || nk_console_row_active_child(row) == NULL) {
        return nk_false;
    }

    nk_console_row_data* data = (nk_console_row_data*)row->data;
    int amount = (int)cvector_size(row->children);
    for (int i = 1; i < amount; ++i) {
        if (data->activeChild + i < amount && row->children[data->activeChild + i]->selectable) {
            data->activeChild += i;
            return nk_true;
        }
        if (data->activeChild - i >= 0 && row->children[data->activeChild - i]->selectable) {
            data->activeChild -= i;
            return nk_true;
        }
    }

    return nk_false;
}

NK_API nk_console* nk_console_row_begin(nk_console* parent) {
    // Create the row data.
    nk_console_row_data* data = (nk_console_row_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_row_data));
    nk_zero(data, sizeof(nk_console_row_data));

    // Create the row.
    nk_console* row = nk_console_label(parent, NULL);
    row->data = data;
    row->alignment = NK_TEXT_ALIGN_CENTERED;
    row->type = NK_CONSOLE_ROW;
    row->render = nk_console_row_render;
    row->columns = 0;
    row->selectable = nk_false;
    return row;
}

NK_API void nk_console_row_end(nk_console* row) {
    // Make sure there is a row to end that has available children.
    nk_console* active_child = nk_console_row_active_child(row);
    if (active_child == NULL) {
        return;
    }

    // Set up the row data based on the available children.
    row->columns = 0;
    row->selectable = nk_false;
    row->height = 0;
    int numChildren = (int)cvector_size(row->children);
    for (int i = 0; i < numChildren; ++i) {
        nk_console* child = row->children[i];

        // This row is selectable if there's at least one selectable child.
        row->selectable |= child->selectable;

        // The row's height is taken from the tallest child.
        if (child->height > row->height) {
            row->height = child->height;
        }

        // Calculate the maximum amount of columns that are in the row
        row->columns += child->columns;
    }

    // Make sure we start on a selectable child by default.
    if (!active_child->selectable) {
        nk_console_row_pick_nearest_selectable_child(row);
    }

    nk_console_row_data* data = (nk_console_row_data*)row->data;
    data->widgets_added = nk_true;
}

static void nk_console_row_check_left_right(nk_console* row, nk_console* top) {
    nk_console_row_data* data = (nk_console_row_data*)row->data;
    nk_console_top_data* top_data = (nk_console_top_data*)top->data;
    if (top_data->input_processed) {
        return;
    }

    // Left
    if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LEFT)) {
        data->activeChild = nk_console_row_next_selectable_child(row, -1);
        top_data->input_processed = nk_true;
    }
    // Right
    else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RIGHT)) {
        data->activeChild = nk_console_row_next_selectable_child(row, 1);
        top_data->input_processed = nk_true;
    }
}

NK_API struct nk_rect nk_console_row_render(nk_console* console) {
    if (console == NULL || console->data == NULL) {
        return nk_rect(0, 0, 0, 0);
    }

    nk_console_row_data* data = (nk_console_row_data*)console->data;
    nk_console* top = nk_console_get_top(console);
    nk_console_top_data* top_data = (nk_console_top_data*)top->data;

    // Rows use the advanced layout system to render their children.
    nk_layout_row_begin(console->ctx, NK_DYNAMIC, (float)console->height, console->columns);

    struct nk_rect widget_bounds = nk_layout_widget_bounds(console->ctx);

    // Consume mouse movement before children have a chance to.
    int numChildren = (int)cvector_size(console->children);
    struct nk_input* input = &console->ctx->input;
    if (console->selectable && top_data->input_processed == nk_false &&
        widget_bounds.w > 0 && nk_input_is_mouse_moved(input) &&
        nk_input_is_mouse_hovering_rect(input, widget_bounds)) {
        // First, make sure that the active widget is the row.
        nk_console_set_active_widget(console);

        // Find the child that the mouse is hovering over.
        if (console->columns > 0) {
            float x_percent = (input->mouse.pos.x - widget_bounds.x) / widget_bounds.w;
            float column_width_percent = 0.0f;
            for (int i = 0; i < numChildren; i++) {
                int widget_columns = console->children[i]->columns;
                if (widget_columns <= 0) {
                    widget_columns = 1;
                }
                column_width_percent += (float)widget_columns / (float)console->columns;
                if (x_percent < column_width_percent) {
                    data->activeChild = i;
                    break;
                }
            }
        }

        // Ensure the new active child is actually selectable.
        if (!nk_console_row_active_child(console)->selectable) {
            nk_console_row_pick_nearest_selectable_child(console);
        }
    }

    // Consume directional input before children have a chance to.
    if (nk_console_is_active_widget(console)) {
        nk_console_row_check_left_right(console, top);
        nk_console_check_up_down(console, widget_bounds);
        nk_console* active = nk_console_get_active_widget(console);

        // Attempt to accurately move vertically if the new widget is also a row.
        if (active != NULL && active != console && active->type == NK_CONSOLE_ROW && active->data != NULL) {
            nk_console_row_data* active_data = (nk_console_row_data*)active->data;

            // Find the desired destination based on the current row child widths.
            float desired_destination_percent = 0.0f;
            for (int i = 0; i < data->activeChild; i++) {
                desired_destination_percent += (float)console->children[i]->columns / (float)console->columns;
            }

            // Determine the new active child based on the desired destination percent.
            float x = 0.0f;
            int active_children_size = (int)cvector_size(active->children);
            for (active_data->activeChild = 0; active_data->activeChild < active_children_size; active_data->activeChild++) {
                x += (float)active->children[active_data->activeChild]->columns / (float)active->columns;
                if (x > desired_destination_percent) {
                    break;
                }
            }

            // Ensure we switched to a selectable child.
            nk_console* new_active_child = nk_console_row_active_child(active);
            if (new_active_child != NULL && !new_active_child->selectable) {
                nk_console_row_pick_nearest_selectable_child(active);
            }
        }
    }

    // Set the active widget to the active child of the row.
    if (!console->disabled && nk_console_is_active_widget(console) && numChildren > 0) {
        console->activeWidget = nk_console_row_active_child(console);
    }

    // Render all the children
    for (int i = 0; i < numChildren; ++i) {
        nk_console* child = console->children[i];
        if (child->render != NULL && child->visible == nk_true) {
            // If the row is disabled, then temporarily disable the child when rendering.
            if (console->disabled) {
                nk_bool child_disabled = child->disabled;
                child->disabled = nk_true;
                child->render(child);
                child->disabled = child_disabled;
            }
            else {
                child->render(child);
            }
        }
    }

    console->activeWidget = NULL;

    // Finished rendering the row, so complete the row layout.
    nk_layout_row_end(console->ctx);

    return widget_bounds;
}

#if defined(__cplusplus)
}
#endif

#endif // NK_CONSOLE_ROW_IMPLEMENTATION_ONCE
#endif // NK_CONSOLE_IMPLEMENTATION