Merge ListSelector with LevelPicker

master
rexim 2019-12-16 00:52:01 +07:00
parent 0c137397fd
commit 523337125b
5 changed files with 155 additions and 217 deletions

View File

@ -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

View File

@ -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"

View File

@ -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,

View File

@ -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;
}

View File

@ -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_