Merge ListSelector with LevelPicker
parent
0c137397fd
commit
523337125b
|
@ -119,8 +119,6 @@ add_executable(nothing
|
|||
src/ui/edit_field.c
|
||||
src/ui/history.h
|
||||
src/ui/history.c
|
||||
src/ui/list_selector.h
|
||||
src/ui/list_selector.c
|
||||
src/ui/wiggly_text.h
|
||||
src/ui/wiggly_text.c
|
||||
src/ui/slider.h
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include "src/ui/console_log.c"
|
||||
#include "src/ui/edit_field.c"
|
||||
#include "src/ui/history.c"
|
||||
#include "src/ui/list_selector.c"
|
||||
#include "src/ui/wiggly_text.c"
|
||||
#include "src/ui/slider.c"
|
||||
#include "src/ui/grid.c"
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "system/nth_alloc.h"
|
||||
#include "system/stacktrace.h"
|
||||
#include "system/str.h"
|
||||
#include "ui/list_selector.h"
|
||||
#include "system/log.h"
|
||||
#include "game/level_folder.h"
|
||||
#include "ui/wiggly_text.h"
|
||||
|
@ -23,7 +22,13 @@ struct LevelPicker
|
|||
Vec2f camera_position;
|
||||
LevelFolder level_folder;
|
||||
WigglyText wiggly_text;
|
||||
ListSelector list_selector;
|
||||
|
||||
Dynarray items;
|
||||
size_t cursor;
|
||||
int selected_item;
|
||||
Vec2f position;
|
||||
Vec2f font_scale;
|
||||
float padding_bottom;
|
||||
};
|
||||
|
||||
LevelPicker *create_level_picker(const char *dirpath)
|
||||
|
@ -54,9 +59,9 @@ LevelPicker *create_level_picker(const char *dirpath)
|
|||
.color = COLOR_WHITE,
|
||||
};
|
||||
|
||||
level_picker->list_selector.items = level_picker->level_folder.filepaths;
|
||||
level_picker->list_selector.font_scale = vec(5.0f, 5.0f);
|
||||
level_picker->list_selector.padding_bottom = 50.0f;
|
||||
level_picker->items = level_picker->level_folder.filepaths;
|
||||
level_picker->font_scale = vec(5.0f, 5.0f);
|
||||
level_picker->padding_bottom = 50.0f;
|
||||
|
||||
return level_picker;
|
||||
}
|
||||
|
@ -86,7 +91,36 @@ int level_picker_render(const LevelPicker *level_picker,
|
|||
camera,
|
||||
vec(viewport.w * 0.5f - title_size.x * 0.5f, TITLE_MARGIN_TOP));
|
||||
|
||||
list_selector_render(camera, &level_picker->list_selector);
|
||||
for (size_t i = 0; i < level_picker->items.count; ++i) {
|
||||
const Vec2f current_position = vec_sum(
|
||||
level_picker->position,
|
||||
vec(0.0f, (float) i * ((float) FONT_CHAR_HEIGHT * level_picker->font_scale.y + level_picker->padding_bottom)));
|
||||
|
||||
const char *item_text = dynarray_pointer_at(&level_picker->items, i);
|
||||
|
||||
sprite_font_render_text(
|
||||
&camera->font,
|
||||
camera->renderer,
|
||||
current_position,
|
||||
level_picker->font_scale,
|
||||
rgba(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
item_text);
|
||||
|
||||
if (i == level_picker->cursor) {
|
||||
SDL_Rect boundary_box = rect_for_sdl(
|
||||
sprite_font_boundary_box(
|
||||
current_position,
|
||||
level_picker->font_scale,
|
||||
strlen(item_text)));
|
||||
if (SDL_SetRenderDrawColor(camera->renderer, 255, 255, 255, 255) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SDL_RenderDrawRect(camera->renderer, &boundary_box) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* CSS */
|
||||
|
@ -122,6 +156,33 @@ int level_picker_update(LevelPicker *level_picker,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
Vec2f level_picker_list_size(const LevelPicker *level_picker,
|
||||
Vec2f font_scale,
|
||||
float padding_bottom)
|
||||
{
|
||||
trace_assert(level_picker);
|
||||
|
||||
Vec2f result = vec(0.0f, 0.0f);
|
||||
|
||||
for (size_t i = 0; i < level_picker->items.count; ++i) {
|
||||
const char *item_text = dynarray_pointer_at(
|
||||
&level_picker->items,
|
||||
i);
|
||||
|
||||
Rect boundary_box = sprite_font_boundary_box(
|
||||
vec(0.0f, 0.0f),
|
||||
font_scale,
|
||||
strlen(item_text));
|
||||
|
||||
result.x = fmaxf(result.x, boundary_box.w);
|
||||
result.y += boundary_box.y + padding_bottom;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int level_picker_event(LevelPicker *level_picker,
|
||||
const SDL_Event *event)
|
||||
{
|
||||
|
@ -141,20 +202,100 @@ int level_picker_event(LevelPicker *level_picker,
|
|||
|
||||
const Vec2f title_size = wiggly_text_size(&level_picker->wiggly_text);
|
||||
|
||||
const Vec2f selector_size = list_selector_size(
|
||||
&level_picker->list_selector,
|
||||
const Vec2f selector_size = level_picker_list_size(
|
||||
level_picker,
|
||||
font_scale,
|
||||
padding_bottom);
|
||||
|
||||
level_picker->list_selector.position =
|
||||
level_picker->position =
|
||||
vec((float)width * 0.5f - selector_size.x * 0.5f,
|
||||
TITLE_MARGIN_TOP + title_size.y + TITLE_MARGIN_BOTTOM);
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
switch (event->key.keysym.sym) {
|
||||
case SDLK_UP:
|
||||
if (level_picker->cursor == 0) {
|
||||
level_picker->cursor = level_picker->items.count - 1;
|
||||
} else {
|
||||
level_picker->cursor--;
|
||||
}
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
level_picker->cursor++;
|
||||
if (level_picker->cursor == level_picker->items.count) {
|
||||
level_picker->cursor = 0;
|
||||
}
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
if (level_picker->cursor < level_picker->items.count) {
|
||||
level_picker->selected_item = (int) level_picker->cursor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION: {
|
||||
const Vec2f mouse_pos = vec((float) event->motion.x, (float) event->motion.y);
|
||||
Vec2f position = level_picker->position;
|
||||
|
||||
for (size_t i = 0; i < level_picker->items.count; ++i) {
|
||||
const char *item_text = dynarray_pointer_at(
|
||||
&level_picker->items,
|
||||
i);
|
||||
|
||||
Rect boundary_box = sprite_font_boundary_box(
|
||||
position,
|
||||
level_picker->font_scale,
|
||||
strlen(item_text));
|
||||
|
||||
if (rect_contains_point(boundary_box, mouse_pos)) {
|
||||
level_picker->cursor = i;
|
||||
}
|
||||
|
||||
position.y += boundary_box.h + level_picker->padding_bottom;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN: {
|
||||
switch (event->button.button) {
|
||||
case SDL_BUTTON_LEFT: {
|
||||
// check if the click position was actually inside...
|
||||
// note: make sure there's actually stuff in the list! tsoding likes
|
||||
// to remove all levels and change title to "SMOL BREAK"...
|
||||
if (level_picker->items.count == 0)
|
||||
break;
|
||||
|
||||
// note: this assumes that all list items are the same height!
|
||||
// this is probably a valid assumption as long as we use a sprite font.
|
||||
float single_item_height =
|
||||
FONT_CHAR_HEIGHT * level_picker->font_scale.y + level_picker->padding_bottom;
|
||||
|
||||
Vec2f position = level_picker->position;
|
||||
vec_add(&position, vec(0.0f, (float) level_picker->cursor * single_item_height));
|
||||
|
||||
const char *item_text =
|
||||
dynarray_pointer_at(
|
||||
&level_picker->items,
|
||||
level_picker->cursor);
|
||||
|
||||
Rect boundary_box = sprite_font_boundary_box(
|
||||
position,
|
||||
level_picker->font_scale,
|
||||
strlen(item_text));
|
||||
|
||||
const Vec2f mouse_pos = vec((float) event->motion.x, (float) event->motion.y);
|
||||
if (rect_contains_point(boundary_box, mouse_pos)) {
|
||||
level_picker->selected_item = (int) level_picker->cursor;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
return list_selector_event(&level_picker->list_selector, event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int level_picker_input(LevelPicker *level_picker,
|
||||
|
@ -171,19 +312,19 @@ const char *level_picker_selected_level(const LevelPicker *level_picker)
|
|||
{
|
||||
trace_assert(level_picker);
|
||||
|
||||
if (level_picker->list_selector.selected_item < 0) {
|
||||
if (level_picker->selected_item < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dynarray_pointer_at(
|
||||
&level_picker->level_folder.filepaths,
|
||||
(size_t)level_picker->list_selector.selected_item);
|
||||
&level_picker->items,
|
||||
(size_t)level_picker->selected_item);
|
||||
}
|
||||
|
||||
void level_picker_clean_selection(LevelPicker *level_picker)
|
||||
{
|
||||
trace_assert(level_picker);
|
||||
level_picker->list_selector.selected_item = -1;
|
||||
level_picker->selected_item = -1;
|
||||
}
|
||||
|
||||
int level_picker_enter_camera_event(LevelPicker *level_picker,
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "system/stacktrace.h"
|
||||
#include "system/lt.h"
|
||||
#include "system/nth_alloc.h"
|
||||
#include "system/str.h"
|
||||
#include "math/vec.h"
|
||||
#include "game/sprite_font.h"
|
||||
#include "system/log.h"
|
||||
|
||||
#include "./list_selector.h"
|
||||
|
||||
|
||||
int list_selector_render(const Camera *camera,
|
||||
const ListSelector *list_selector)
|
||||
{
|
||||
trace_assert(camera);
|
||||
trace_assert(list_selector);
|
||||
|
||||
for (size_t i = 0; i < list_selector->items.count; ++i) {
|
||||
const Vec2f current_position = vec_sum(
|
||||
list_selector->position,
|
||||
vec(0.0f, (float) i * ((float) FONT_CHAR_HEIGHT * list_selector->font_scale.y + list_selector->padding_bottom)));
|
||||
|
||||
const char *item_text = dynarray_pointer_at(&list_selector->items, i);
|
||||
|
||||
sprite_font_render_text(
|
||||
&camera->font,
|
||||
camera->renderer,
|
||||
current_position,
|
||||
list_selector->font_scale,
|
||||
rgba(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
item_text);
|
||||
|
||||
if (i == list_selector->cursor) {
|
||||
SDL_Rect boundary_box = rect_for_sdl(
|
||||
sprite_font_boundary_box(
|
||||
current_position,
|
||||
list_selector->font_scale,
|
||||
strlen(item_text)));
|
||||
if (SDL_SetRenderDrawColor(camera->renderer, 255, 255, 255, 255) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SDL_RenderDrawRect(camera->renderer, &boundary_box) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vec2f list_selector_size(const ListSelector *list_selector,
|
||||
Vec2f font_scale,
|
||||
float padding_bottom)
|
||||
{
|
||||
trace_assert(list_selector);
|
||||
|
||||
Vec2f result = vec(0.0f, 0.0f);
|
||||
|
||||
for (size_t i = 0; i < list_selector->items.count; ++i) {
|
||||
const char *item_text = dynarray_pointer_at(
|
||||
&list_selector->items,
|
||||
i);
|
||||
|
||||
Rect boundary_box = sprite_font_boundary_box(
|
||||
vec(0.0f, 0.0f),
|
||||
font_scale,
|
||||
strlen(item_text));
|
||||
|
||||
result.x = fmaxf(result.x, boundary_box.w);
|
||||
result.y += boundary_box.y + padding_bottom;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int list_selector_event(ListSelector *list_selector, const SDL_Event *event)
|
||||
{
|
||||
trace_assert(list_selector);
|
||||
trace_assert(event);
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_KEYDOWN:
|
||||
switch (event->key.keysym.sym) {
|
||||
case SDLK_UP:
|
||||
if (list_selector->cursor == 0) {
|
||||
list_selector->cursor = list_selector->items.count - 1;
|
||||
} else {
|
||||
list_selector->cursor--;
|
||||
}
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
list_selector->cursor++;
|
||||
if (list_selector->cursor == list_selector->items.count) {
|
||||
list_selector->cursor = 0;
|
||||
}
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
if (list_selector->cursor < list_selector->items.count) {
|
||||
list_selector->selected_item = (int) list_selector->cursor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION: {
|
||||
const Vec2f mouse_pos = vec((float) event->motion.x, (float) event->motion.y);
|
||||
Vec2f position = list_selector->position;
|
||||
|
||||
for (size_t i = 0; i < list_selector->items.count; ++i) {
|
||||
const char *item_text = dynarray_pointer_at(
|
||||
&list_selector->items,
|
||||
i);
|
||||
|
||||
Rect boundary_box = sprite_font_boundary_box(
|
||||
position,
|
||||
list_selector->font_scale,
|
||||
strlen(item_text));
|
||||
|
||||
if (rect_contains_point(boundary_box, mouse_pos)) {
|
||||
list_selector->cursor = i;
|
||||
}
|
||||
|
||||
position.y += boundary_box.h + list_selector->padding_bottom;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN: {
|
||||
switch (event->button.button) {
|
||||
case SDL_BUTTON_LEFT: {
|
||||
// check if the click position was actually inside...
|
||||
// note: make sure there's actually stuff in the list! tsoding likes
|
||||
// to remove all levels and change title to "SMOL BREAK"...
|
||||
if (list_selector->items.count == 0)
|
||||
break;
|
||||
|
||||
// note: this assumes that all list items are the same height!
|
||||
// this is probably a valid assumption as long as we use a sprite font.
|
||||
float single_item_height =
|
||||
FONT_CHAR_HEIGHT * list_selector->font_scale.y + list_selector->padding_bottom;
|
||||
|
||||
Vec2f position = list_selector->position;
|
||||
vec_add(&position, vec(0.0f, (float) list_selector->cursor * single_item_height));
|
||||
|
||||
const char *item_text =
|
||||
dynarray_pointer_at(
|
||||
&list_selector->items,
|
||||
list_selector->cursor);
|
||||
|
||||
Rect boundary_box = sprite_font_boundary_box(
|
||||
position,
|
||||
list_selector->font_scale,
|
||||
strlen(item_text));
|
||||
|
||||
const Vec2f mouse_pos = vec((float) event->motion.x, (float) event->motion.y);
|
||||
if (rect_contains_point(boundary_box, mouse_pos)) {
|
||||
list_selector->selected_item = (int) list_selector->cursor;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#ifndef LIST_SELECTOR_H_
|
||||
#define LIST_SELECTOR_H_
|
||||
|
||||
#include "game/camera.h"
|
||||
#include "dynarray.h"
|
||||
|
||||
typedef struct {
|
||||
Dynarray items;
|
||||
size_t cursor;
|
||||
int selected_item;
|
||||
Vec2f position;
|
||||
Vec2f font_scale;
|
||||
float padding_bottom;
|
||||
} ListSelector;
|
||||
|
||||
int list_selector_render(const Camera *camera,
|
||||
const ListSelector *list_selector);
|
||||
Vec2f list_selector_size(const ListSelector *list_selector,
|
||||
Vec2f font_scale,
|
||||
float padding_bottom);
|
||||
int list_selector_event(ListSelector *list_selector, const SDL_Event *event);
|
||||
|
||||
static inline
|
||||
void list_selector_clean_selection(ListSelector *list_selector)
|
||||
{
|
||||
trace_assert(list_selector);
|
||||
list_selector->selected_item = -1;
|
||||
}
|
||||
|
||||
#endif // LIST_SELECTOR_H_
|
Loading…
Reference in New Issue