diff --git a/include/app/app.h b/include/app/app.h index d1be796..5692f66 100644 --- a/include/app/app.h +++ b/include/app/app.h @@ -6,8 +6,13 @@ #include "utils.h" #include "window/window.h" +enum Type { IMAGE, + TEXT, + RECT }; + namespace app { struct Element { + Type type; Vec2 position = {0, 0}; float rotation = 0; Vec2 scale = {1, 1}; @@ -15,6 +20,7 @@ struct Element { bool click_through = false; Ruby::Block on_click; + Element(Type type) : type(type) {} virtual ~Element() = default; virtual void render(Window &window, uint64_t abs_time) = 0; virtual Vec2 size(Window *window) = 0; @@ -26,14 +32,14 @@ struct Element { }; struct EImage : Element { - int animation_id = -1; + uint64_t animation_id = UINT64_MAX; - EImage(int animation_id) : animation_id(animation_id) { + EImage(uint64_t animation_id) : Element(Type::IMAGE), animation_id(animation_id) { animation_pool.use(animation_id); } ~EImage() { - if (animation_id != -1) + if (animation_id != UINT64_MAX) animation_pool.release(animation_id); } @@ -56,30 +62,32 @@ struct EImage : Element { struct EText : Element { Localization::Key key; - int font_id = -1; + uint64_t font_id = UINT64_MAX; ~EText() { - if (text_id != -1) + if (text_id != UINT64_MAX) text_pool.release(text_id); - if (font_id != -1) + if (font_id != UINT64_MAX) font_pool.release(font_id); } Color color = {255, 255, 255, 255}; - EText(Localization::Key key, int font_id, Color color) - : key(key), font_id(font_id), color(color) { + EText(Localization::Key key, uint64_t font_id, Color color) + : Element(Type::TEXT), key(key), font_id(font_id), color(color) { font_pool.use(font_id); } Vec2 size(Window *window) override { + if (text_id == UINT64_MAX) + return {0, 0}; return window->get_text_size(text_id); }; void render(Window &window, uint64_t) override { - if (last_language != Localization::current_language || last_key != key || text_id == -1) { + if (last_language != Localization::current_language || last_key != key || text_id == UINT64_MAX) { std::string localized_str = Localization::get(key); - if (text_id != -1) + if (text_id != UINT64_MAX) text_pool.release(text_id); text_id = window.create_text(font_id, localized_str.c_str(), localized_str.size(), color); last_language = Localization::current_language; @@ -89,9 +97,9 @@ struct EText : Element { } private: - int text_id = -1; - Localization::LanguageCode last_language = -1; - Localization::Key last_key = -1; + uint64_t text_id = UINT64_MAX; + Localization::LanguageCode last_language = UINT_MAX; + Localization::Key last_key = UINT_MAX; }; struct ERect : Element { @@ -101,7 +109,7 @@ struct ERect : Element { return shape; }; - ERect(Vec2 shape, Color color) : shape(shape), color(color) {} + ERect(Vec2 shape, Color color) : Element(Type::RECT), shape(shape), color(color) {} void render(Window &window, uint64_t) override { window.draw_rect(Rect::from_size(position, shape), color, z, rotation); @@ -111,15 +119,15 @@ struct ERect : Element { inline Pool element_pool; struct Scene { - std::vector element_ids; + std::vector element_ids; Ruby::Block update; ~Scene() { - for (const int &id : element_ids) + for (const uint64_t &id : element_ids) element_pool.release(id); } - void add_element(int element_id) { + void add_element(uint64_t element_id) { auto it = std::lower_bound( element_ids.begin(), element_ids.end(), @@ -134,7 +142,7 @@ struct Scene { element_ids.insert(it, element_id); } - void delete_element(int element_id) { + void delete_element(uint64_t element_id) { auto it = std::find(element_ids.begin(), element_ids.end(), element_id); if (it != element_ids.end()) { element_pool.release(element_id); @@ -149,23 +157,33 @@ struct Scene { } } - void click(Window *window, Vec2 position) { + void click(Vec2 position) { + std::vector clicked_elements = elements_at(position); + for (const uint64_t &id : clicked_elements) + element_pool[id]->click(); + } + + std::vector elements_at(Vec2 position) { + std::vector result; + for (auto it = element_ids.rbegin(); it != element_ids.rend(); ++it) { Element *e = element_pool[*it]; - Vec2 size = e->size(window); + Vec2 size = e->size(&window); Rect rect = Rect::from_size(e->position, size * e->scale); if (rect.contains(position)) { - e->click(); + result.push_back(*it); if (e->click_through) continue; else break; } } + + return result; } void render(Window &window, uint64_t abs_time) { - for (const int &e : element_ids) + for (const uint64_t &e : element_ids) element_pool[e]->render(window, abs_time); } }; @@ -173,10 +191,11 @@ struct Scene { inline Pool scene_pool; struct App { - int current_scene_id = 0; + uint64_t current_scene_id = 0; bool running = true; + Vec2 mouse_position = {-1, -1}; - void switch_to_scene(int scene_id) { + void switch_to_scene(uint64_t scene_id) { scene_pool.release(current_scene_id); scene_pool.use(scene_id); current_scene_id = scene_id; @@ -213,7 +232,13 @@ struct App { running = false; break; case Event::MOUSE_BUTTON_DOWN: - scene->click(&window, e.mouse_button.position); + scene->click(e.mouse_button.position); + break; + case Event::MOUSE_MOVE: + mouse_position = e.mouse_move.position; + break; + case Event::MOUSE_LEAVE: + mouse_position = {-1, -1}; break; default: break; diff --git a/include/binding/definitions.h b/include/binding/definitions.h index 9e3155d..4504cf1 100644 --- a/include/binding/definitions.h +++ b/include/binding/definitions.h @@ -4,10 +4,9 @@ #include "app/app.h" #warning "Define ==, eql?, hash for each class" -#warning "Define and bind elements_at in Scene (same as click method already defined but without callback)" namespace definitions { -inline mrb_value wrap(mrb_state *mrb, RClass *cls, mrb_data_type *type, int id) { +inline mrb_value wrap(mrb_state *mrb, RClass *cls, const mrb_data_type *type, uint64_t id) { RBasic *obj_b = mrb_obj_alloc(mrb, MRB_TT_DATA, cls); mrb_value obj = mrb_obj_value(obj_b); @@ -21,7 +20,7 @@ inline mrb_value wrap(mrb_state *mrb, RClass *cls, mrb_data_type *type, int id) static void Font_free(mrb_state *mrb, void *ptr) { UNUSED(mrb); - int id = ((Wrapper *)ptr)->id; + uint64_t id = ((Wrapper *)ptr)->id; font_pool.release(id); delete (Wrapper *)ptr; } @@ -58,7 +57,7 @@ static mrb_value Font_init(mrb_state *mrb, mrb_value self) { return self; } -inline int get_id_Font(mrb_state *mrb, mrb_value self) { +inline uint64_t get_id_Font(mrb_state *mrb, mrb_value self) { Wrapper *wrapper; Data_Get_Struct(mrb, self, &Font_type, wrapper); return wrapper->id; @@ -66,7 +65,7 @@ inline int get_id_Font(mrb_state *mrb, mrb_value self) { static void Image_free(mrb_state *mrb, void *ptr) { UNUSED(mrb); - int id = ((Wrapper *)ptr)->id; + uint64_t id = ((Wrapper *)ptr)->id; image_pool.release(id); delete (Wrapper *)ptr; } @@ -98,7 +97,7 @@ static mrb_value Image_init(mrb_state *mrb, mrb_value self) { return self; } -inline int get_id_Image(mrb_state *mrb, mrb_value self) { +inline uint64_t get_id_Image(mrb_state *mrb, mrb_value self) { Wrapper *wrapper; Data_Get_Struct(mrb, self, &Image_type, wrapper); return wrapper->id; @@ -106,7 +105,7 @@ inline int get_id_Image(mrb_state *mrb, mrb_value self) { static void Animation_free(mrb_state *mrb, void *ptr) { UNUSED(mrb); - int id = ((Wrapper *)ptr)->id; + uint64_t id = ((Wrapper *)ptr)->id; animation_pool.release(id); delete (Wrapper *)ptr; } @@ -132,7 +131,7 @@ static mrb_value Animation_init(mrb_state *mrb, mrb_value self) { if (!mrb_nil_p(kwargs)) HASH_FLOAT(kwargs, fps, fps); - std::vector frame_ids; + std::vector frame_ids; for (mrb_int i = 0; i < frames_len; i++) { mrb_value frame_val = frames_val[i]; if (!mrb_obj_is_kind_of(mrb, frame_val, mrb_class_get(mrb, "Image"))) { @@ -143,7 +142,7 @@ static mrb_value Animation_init(mrb_state *mrb, mrb_value self) { ); return mrb_nil_value(); } - int frame_id = get_id_Image(mrb, frame_val); + uint64_t frame_id = get_id_Image(mrb, frame_val); image_pool.use(frame_id); frame_ids.push_back(frame_id); } @@ -156,7 +155,7 @@ static mrb_value Animation_init(mrb_state *mrb, mrb_value self) { return self; } -inline int get_id_Animation(mrb_state *mrb, mrb_value self) { +inline uint64_t get_id_Animation(mrb_state *mrb, mrb_value self) { Wrapper *wrapper; Data_Get_Struct(mrb, self, &Animation_type, wrapper); return wrapper->id; @@ -164,7 +163,7 @@ inline int get_id_Animation(mrb_state *mrb, mrb_value self) { static void ElementText_free(mrb_state *mrb, void *ptr) { UNUSED(mrb); - int id = ((Wrapper *)ptr)->id; + uint64_t id = ((Wrapper *)ptr)->id; app::element_pool.release(id); delete (Wrapper *)ptr; } @@ -208,7 +207,7 @@ static mrb_value ElementText_init(mrb_state *mrb, mrb_value self) { Vec2 position = {x, y}; - int id = get_id_Font(mrb, font_val); + uint64_t id = get_id_Font(mrb, font_val); font_pool.use(id); app::EText *element = new app::EText(key_id, id, {255, 255, 255, 255}); @@ -223,7 +222,7 @@ static mrb_value ElementText_init(mrb_state *mrb, mrb_value self) { return self; } -inline int get_id_ElementText(mrb_state *mrb, mrb_value self) { +inline uint64_t get_id_ElementText(mrb_state *mrb, mrb_value self) { Wrapper *wrapper; Data_Get_Struct(mrb, self, &ElementText_type, wrapper); return wrapper->id; @@ -232,7 +231,7 @@ inline int get_id_ElementText(mrb_state *mrb, mrb_value self) { static mrb_value element_text_on_click(mrb_state *mrb, mrb_value self) { mrb_value block; mrb_get_args(mrb, "&", &block); - int id = get_id_ElementText(mrb, self); + uint64_t id = get_id_ElementText(mrb, self); app::Element *element = app::element_pool[id]; element->on_click.set_proc(block); return self; @@ -240,7 +239,7 @@ static mrb_value element_text_on_click(mrb_state *mrb, mrb_value self) { static void ElementRect_free(mrb_state *mrb, void *ptr) { UNUSED(mrb); - int id = ((Wrapper *)ptr)->id; + uint64_t id = ((Wrapper *)ptr)->id; app::element_pool.release(id); delete (Wrapper *)ptr; } @@ -271,8 +270,11 @@ static mrb_value ElementRect_init(mrb_state *mrb, mrb_value self) { } float z = 0; - if (!mrb_nil_p(kwargs)) + bool click_through = false; + if (!mrb_nil_p(kwargs)) { HASH_FLOAT(kwargs, z, z); + HASH_BOOL(kwargs, click_through, click_through); + } Vec2 position = {x, y}; Vec2 shape = {w, h}; @@ -286,6 +288,7 @@ static mrb_value ElementRect_init(mrb_state *mrb, mrb_value self) { ); element->position = position; element->z = z; + element->click_through = click_through; s->id = app::element_pool.acquire(element); app::element_pool.use(s->id); @@ -294,7 +297,7 @@ static mrb_value ElementRect_init(mrb_state *mrb, mrb_value self) { return self; } -inline int get_id_ElementRect(mrb_state *mrb, mrb_value self) { +inline uint64_t get_id_ElementRect(mrb_state *mrb, mrb_value self) { Wrapper *wrapper; Data_Get_Struct(mrb, self, &ElementRect_type, wrapper); return wrapper->id; @@ -303,7 +306,7 @@ inline int get_id_ElementRect(mrb_state *mrb, mrb_value self) { static mrb_value element_rect_on_click(mrb_state *mrb, mrb_value self) { mrb_value block; mrb_get_args(mrb, "&", &block); - int id = get_id_ElementRect(mrb, self); + uint64_t id = get_id_ElementRect(mrb, self); app::Element *element = app::element_pool[id]; element->on_click.set_proc(block); return self; @@ -312,7 +315,7 @@ static mrb_value element_rect_on_click(mrb_state *mrb, mrb_value self) { static mrb_value element_rect_color_set(mrb_state *mrb, mrb_value self) { mrb_value color_val; mrb_get_args(mrb, "o", &color_val); - int id = get_id_ElementRect(mrb, self); + uint64_t id = get_id_ElementRect(mrb, self); app::ERect *element = (app::ERect *)app::element_pool[id]; element->color = { (uint8_t)((mrb_fixnum(color_val) >> 24) & 0xFF), @@ -324,7 +327,7 @@ static mrb_value element_rect_color_set(mrb_state *mrb, mrb_value self) { } static mrb_value element_rect_color_get(mrb_state *mrb, mrb_value self) { - int id = get_id_ElementRect(mrb, self); + uint64_t id = get_id_ElementRect(mrb, self); app::ERect *element = (app::ERect *)app::element_pool[id]; uint32_t color = ((uint32_t)element->color.r << 24) | ((uint32_t)element->color.g << 16) @@ -334,7 +337,7 @@ static mrb_value element_rect_color_get(mrb_state *mrb, mrb_value self) { static void ElementImage_free(mrb_state *mrb, void *ptr) { UNUSED(mrb); - int id = ((Wrapper *)ptr)->id; + uint64_t id = ((Wrapper *)ptr)->id; app::element_pool.release(id); delete (Wrapper *)ptr; } @@ -380,7 +383,7 @@ static mrb_value ElementImage_init(mrb_state *mrb, mrb_value self) { HASH_BOOL(kwargs, click_through, click_through); } - int id = get_id_Animation(mrb, animation_val); + uint64_t id = get_id_Animation(mrb, animation_val); app::EImage *element = new app::EImage(id); element->position = {x, y}; @@ -394,7 +397,7 @@ static mrb_value ElementImage_init(mrb_state *mrb, mrb_value self) { return self; } -inline int get_id_ElementImage(mrb_state *mrb, mrb_value self) { +inline uint64_t get_id_ElementImage(mrb_state *mrb, mrb_value self) { Wrapper *wrapper; Data_Get_Struct(mrb, self, &ElementImage_type, wrapper); return wrapper->id; @@ -403,7 +406,7 @@ inline int get_id_ElementImage(mrb_state *mrb, mrb_value self) { static mrb_value element_image_on_click(mrb_state *mrb, mrb_value self) { mrb_value block; mrb_get_args(mrb, "&", &block); - int id = get_id_ElementImage(mrb, self); + uint64_t id = get_id_ElementImage(mrb, self); app::Element *element = app::element_pool[id]; element->on_click.set_proc(block); return self; @@ -411,7 +414,7 @@ static mrb_value element_image_on_click(mrb_state *mrb, mrb_value self) { static void Scene_free(mrb_state *mrb, void *ptr) { UNUSED(mrb); - int id = ((Wrapper *)ptr)->id; + uint64_t id = ((Wrapper *)ptr)->id; app::scene_pool.release(id); delete (Wrapper *)ptr; } @@ -434,7 +437,7 @@ static mrb_value Scene_init(mrb_state *mrb, mrb_value self) { return self; } -inline int get_id_Scene(mrb_state *mrb, mrb_value self) { +inline uint64_t get_id_Scene(mrb_state *mrb, mrb_value self) { Wrapper *wrapper; Data_Get_Struct(mrb, self, &Scene_type, wrapper); return wrapper->id; @@ -457,10 +460,10 @@ static mrb_value scene_add_element(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } - int id = get_id_Scene(mrb, self); + uint64_t id = get_id_Scene(mrb, self); app::Scene *scene = app::scene_pool[id]; - int element_id; + uint64_t element_id; if (mrb_obj_is_kind_of(mrb, element_val, mrb_class_get(mrb, "ElementText"))) { element_id = get_id_ElementText(mrb, element_val); } else if ( @@ -502,10 +505,10 @@ static mrb_value scene_delete_element(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } - int id = get_id_Scene(mrb, self); + uint64_t id = get_id_Scene(mrb, self); app::Scene *scene = app::scene_pool[id]; - int element_id; + uint64_t element_id; if (mrb_obj_is_kind_of(mrb, element_val, mrb_class_get(mrb, "ElementText"))) { element_id = get_id_ElementText(mrb, element_val); } else if ( @@ -534,13 +537,50 @@ static mrb_value scene_on_update(mrb_state *mrb, mrb_value self) { mrb_value block; mrb_get_args(mrb, "&", &block); - int id = get_id_Scene(mrb, self); + uint64_t id = get_id_Scene(mrb, self); app::Scene *scene = app::scene_pool[id]; scene->update.set_proc(block); return self; } +static mrb_value scene_elements_at(mrb_state *mrb, mrb_value self) { + mrb_value position_val; + mrb_get_args(mrb, "o", &position_val); + + float x, y; + HASH_FLOAT(position_val, x, x); + HASH_FLOAT(position_val, y, y); + Vec2 position = {x, y}; + + uint64_t id = get_id_Scene(mrb, self); + app::Scene *scene = app::scene_pool[id]; + + std::vector element_ids = scene->elements_at(position); + + mrb_value result = mrb_ary_new_capa(mrb, element_ids.size()); + for (uint64_t element_id : element_ids) { + app::Element *element = app::element_pool[element_id]; + if (element->type == Type::TEXT) { + RClass *ElementText_class = mrb_class_get(mrb, "ElementText"); + mrb_value obj = wrap(mrb, ElementText_class, &ElementText_type, element_id); + mrb_ary_push(mrb, result, obj); + } else if (element->type == Type::RECT) { + RClass *ElementRect_class = mrb_class_get(mrb, "ElementRect"); + mrb_value obj = wrap(mrb, ElementRect_class, &ElementRect_type, element_id); + mrb_ary_push(mrb, result, obj); + } else if (element->type == Type::IMAGE) { + RClass *ElementImage_class = mrb_class_get(mrb, "ElementImage"); + mrb_value obj = wrap(mrb, ElementImage_class, &ElementImage_type, element_id); + mrb_ary_push(mrb, result, obj); + } else { + continue; + } + } + + return result; +} + static mrb_value app_run(mrb_state *mrb, mrb_value) { mrb_int width, height; char *title = nullptr; @@ -560,7 +600,7 @@ static mrb_value app_start(mrb_state *mrb, mrb_value) { return mrb_nil_value(); } - int id = get_id_Scene(mrb, scene_val); + uint64_t id = get_id_Scene(mrb, scene_val); app_->switch_to_scene(id); app_->loop(); @@ -577,7 +617,7 @@ static mrb_value app_switch_to(mrb_state *mrb, mrb_value) { return mrb_nil_value(); } - int id = get_id_Scene(mrb, scene_val); + uint64_t id = get_id_Scene(mrb, scene_val); app_->switch_to_scene(id); return mrb_nil_value(); @@ -615,6 +655,14 @@ static mrb_value app_language_set(mrb_state *mrb, mrb_value) { return mrb_nil_value(); } +static mrb_value app_mouse_position(mrb_state *mrb, mrb_value) { + Vec2 mouse_pos = app_->mouse_position; + mrb_value result = mrb_hash_new(mrb); + HASH_SET(result, x, mrb_float_value(mrb, mouse_pos.x)); + HASH_SET(result, y, mrb_float_value(mrb, mouse_pos.y)); + return result; +} + inline void setup() { DEF_CLASS(Font, MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1)); @@ -638,6 +686,7 @@ inline void setup() { ADD_METHOD(Scene, "delete", scene_delete_element, MRB_ARGS_REQ(1)); ADD_METHOD(Scene, "remove", scene_delete_element, MRB_ARGS_REQ(1)); ADD_METHOD(Scene, "on_update", scene_on_update, MRB_ARGS_BLOCK()); + ADD_METHOD(Scene, "elements_at", scene_elements_at, MRB_ARGS_REQ(1)); DEF_MODULE(App); ADD_MODULE_METHOD(App, "run", app_run, MRB_ARGS_REQ(3)); @@ -645,6 +694,7 @@ inline void setup() { ADD_MODULE_METHOD(App, "switch_to", app_switch_to, MRB_ARGS_REQ(1)); ADD_MODULE_METHOD(App, "localize", app_localize, MRB_ARGS_REQ(3)); ADD_MODULE_METHOD(App, "language=", app_language_set, MRB_ARGS_REQ(1)); + ADD_MODULE_METHOD(App, "mouse_position", app_mouse_position, MRB_ARGS_NONE()); } } // namespace definitions diff --git a/include/binding/ruby.h b/include/binding/ruby.h index d8411e6..1aa7bd8 100644 --- a/include/binding/ruby.h +++ b/include/binding/ruby.h @@ -9,6 +9,8 @@ #define HASH_GET(h, key) mrb_hash_get(Ruby::mrb, h, SYM(key)) +#define HASH_SET(h, key, value) mrb_hash_set(Ruby::mrb, h, SYM(key), value) + #define HASH_FLOAT(h, key, out) \ do { \ mrb_value v = HASH_GET(h, key); \ @@ -28,7 +30,7 @@ } while (0) struct Wrapper { - int id; + uint64_t id; }; #define ADD_METHOD(NAME, METHOD, FUNC, ARGS) \ diff --git a/include/pch.h b/include/pch.h index 1a4161d..c780a6b 100644 --- a/include/pch.h +++ b/include/pch.h @@ -29,6 +29,7 @@ #include #include +#include #include #include diff --git a/include/utils.h b/include/utils.h index 67f6baa..6857558 100644 --- a/include/utils.h +++ b/include/utils.h @@ -154,12 +154,13 @@ struct Rect { template struct Pool { - T *operator[](int index) { + T *operator[](uint64_t index) { if (!is_valid(index)) { - fprintf(stderr, "Invalid pool index: %d\n", index); + fprintf(stderr, "Invalid pool index: %llu\n", (unsigned long long)index); return nullptr; } - return slots[index].ptr; + uint32_t id = (uint32_t)(index & 0xFFFFFFFFULL); + return slots[id].ptr; } const std::vector &all() { @@ -171,43 +172,51 @@ struct Pool { return all_items; } - int acquire(T *item) { - int index; + uint64_t acquire(T *item) { + uint32_t id; if (!free_indices.empty()) { - index = free_indices.back(); + id = free_indices.back(); free_indices.pop_back(); } else { - index = slots.size(); + id = slots.size(); slots.push_back({}); } - slots[index].ptr = item; - slots[index].refcount = 1; - slots[index].alive = true; + slots[id].ptr = item; + slots[id].refcount = 1; + slots[id].alive = true; + slots[id].generation++; + uint64_t index = ((uint64_t)slots[id].generation << 32) | id; return index; } - void use(int index) { - if (is_valid(index)) - slots[index].refcount++; + void use(uint64_t index) { + if (is_valid(index)) { + uint64_t id = (uint32_t)(index & 0xFFFFFFFFULL); + slots[id].refcount++; + } } - void release(int index) { + void release(uint64_t index) { if (!is_valid(index)) return; - auto &slot = slots[index]; + uint32_t id = (uint32_t)(index & 0xFFFFFFFFULL); + auto &slot = slots[id]; slot.refcount--; if (slot.refcount <= 0) { delete slot.ptr; slot.ptr = nullptr; slot.alive = false; - free_indices.push_back(index); + free_indices.push_back(id); } } - bool is_valid(int index) const { - return index >= 0 - && index < (int)slots.size() - && slots[index].alive; + bool is_valid(uint64_t index) const { + uint32_t id = (uint32_t)(index & 0xFFFFFFFFULL); + uint32_t gen = (uint32_t)(index >> 32); + return id >= 0 + && id < slots.size() + && slots[id].alive + && slots[id].generation == gen; } private: @@ -215,9 +224,10 @@ private: T *ptr = nullptr; int refcount = 0; bool alive = false; + uint32_t generation = 0; }; std::vector slots; - std::vector free_indices; + std::vector free_indices; }; #endif \ No newline at end of file diff --git a/include/window/window.h b/include/window/window.h index c85446a..adf7d1c 100644 --- a/include/window/window.h +++ b/include/window/window.h @@ -48,6 +48,7 @@ struct Event { KEY_DOWN, TEXT_INPUT, SCROLL, + MOUSE_LEAVE, MOUSE_BUTTON_UP, MOUSE_BUTTON_DOWN, MOUSE_MOVE @@ -80,13 +81,13 @@ struct Event { }; struct Animation { - std::vector frames; + std::vector frames; float frame_rate; bool loop; int reference_count = 0; ~Animation() { - for (int frame_id : frames) + for (uint64_t frame_id : frames) image_pool.release(frame_id); } }; @@ -103,18 +104,18 @@ public: ~Window(); // Rendering functions - int load_image(const char *path, float alpha = 1.0f, bool pixel_art = false); - Vec2 get_image_size(int image_id); + uint64_t load_image(const char *path, float alpha = 1.0f, bool pixel_art = false); + Vec2 get_image_size(uint64_t image_id); - int load_font(const char *path, int font_size, bool bold = false, bool italic = false, bool underline = false, bool pixel_art = false); + uint64_t load_font(const char *path, int font_size, bool bold = false, bool italic = false, bool underline = false, bool pixel_art = false); // text is copied internally, so it can be free'd by the caller after the text is created. - int create_text(int font_id, const char *text, uint32_t length, Color color); - Vec2 get_text_size(int text_id); + uint64_t create_text(uint64_t font_id, const char *text, uint32_t length, Color color); + Vec2 get_text_size(uint64_t text_id); - bool write(int text_id, Vec2 position, float z, float angle = 0.0f, Vec2 scale = {1.0f, 1.0f}); + bool write(uint64_t text_id, Vec2 position, float z, float angle = 0.0f, Vec2 scale = {1.0f, 1.0f}); - bool draw(int image_id, Vec2 position, float z, float angle = 0.0f, Vec2 scale = {1.0f, 1.0f}); + bool draw(uint64_t image_id, Vec2 position, float z, float angle = 0.0f, Vec2 scale = {1.0f, 1.0f}); bool draw_rect(Rect rect, Color color, float z, float angle = 0.0f); @@ -172,12 +173,12 @@ private: union { struct { - int image_id; + uint64_t image_id; Vec2 position; } draw_image; struct { - int text_id; + uint64_t text_id; Vec2 position; } draw_text; diff --git a/samples/button.rb b/samples/button.rb index 0f03802..56ccf60 100644 --- a/samples/button.rb +++ b/samples/button.rb @@ -16,7 +16,7 @@ text = ElementText.new :btn_text, default_font, position: {x: 50, y: 50}, z: 1, rect = ElementRect.new({x: 50, y: 50, w: 100, h: 25}, 0xFF0000FF, z: 0) -image_element = ElementImage.new bg_animation, position: {x: 0, y: 0}, z: -1 +image_element = ElementImage.new bg_animation, position: {x: 0, y: 0}, z: -1, click_through: true rect.on_click do rect.color = rect.color == 0xFF0000FF ? @@ -73,15 +73,17 @@ main_scene.on_update do |delta_time| # commented out to avoid spamming the console, but you can uncomment to see the delta time being printed every frame # runs at 60fps, so you should see around 16-17ms being printed - # simple logic to handle hover - pos = App.mouse_position - next if pos.nil? # mouse position can be nil if the window is not focused, so we skip the hover logic in that case - elements = main_scene.elements_at(*pos.values) - if elements.include?(rect) - rect.color = 0x0000FFFF - else # also handles nil case, if the mouse is not over any element, we reset the color - rect.color = 0xFF0000FF + el = main_scene.elements_at(App.mouse_position) + for elem in el + if elem == image_element + puts "Hovering over image element" + elsif elem == rect + puts "Hovering over rect element" + elsif elem == text + puts "Hovering over text element" + end end + end main_scene << text diff --git a/src/window/window.cc b/src/window/window.cc index 72b77ed..5179b39 100644 --- a/src/window/window.cc +++ b/src/window/window.cc @@ -56,7 +56,7 @@ Window::~Window() { SDL_Quit(); } -int Window::load_image(const char *path, float alpha, bool pixel_art) { +uint64_t Window::load_image(const char *path, float alpha, bool pixel_art) { SDL_Texture *texture = IMG_LoadTexture(renderer, path); if (pixel_art) { @@ -65,7 +65,7 @@ int Window::load_image(const char *path, float alpha, bool pixel_art) { if (texture == nullptr) { fprintf(stderr, "Failed to load image: %s\n", SDL_GetError()); - return UINT32_MAX; + return UINT64_MAX; } SDL_SetTextureAlphaMod(texture, (uint8_t)(alpha * 255)); @@ -74,26 +74,26 @@ int Window::load_image(const char *path, float alpha, bool pixel_art) { SDL_GetTextureSize(texture, &w, &h); Image *img = new Image{texture, {w, h}}; - int id = image_pool.acquire(img); + uint64_t id = image_pool.acquire(img); image_pool.use(id); return id; } -Vec2 Window::get_image_size(int image_id) { +Vec2 Window::get_image_size(uint64_t image_id) { if (!image_pool.is_valid(image_id)) { - fprintf(stderr, "Invalid image ID: %d\n", image_id); + fprintf(stderr, "Invalid image ID: %llu\n", (unsigned long long)image_id); return {0, 0}; } return image_pool[image_id]->size; } -int Window::load_font(const char *path, int font_size, bool bold, bool italic, bool underline, bool pixel_art) { +uint64_t Window::load_font(const char *path, int font_size, bool bold, bool italic, bool underline, bool pixel_art) { TTF_Font *font = TTF_OpenFont(path, font_size); if (font == nullptr) { fprintf(stderr, "Failed to load font: %s\n", SDL_GetError()); - return UINT32_MAX; + return UINT64_MAX; } int style = TTF_STYLE_NORMAL; @@ -107,16 +107,16 @@ int Window::load_font(const char *path, int font_size, bool bold, bool italic, b TTF_SetFontStyle(font, style); Font *f = new Font{font, pixel_art}; - int id = font_pool.acquire(f); + uint64_t id = font_pool.acquire(f); font_pool.use(id); return id; } -int Window::create_text(int font_id, const char *text, uint32_t length, Color color) { +uint64_t Window::create_text(uint64_t font_id, const char *text, uint32_t length, Color color) { if (!font_pool.is_valid(font_id)) { - fprintf(stderr, "Invalid font ID: %d\n", font_id); - return UINT32_MAX; + fprintf(stderr, "Invalid font ID: %llu\n", (unsigned long long)font_id); + return UINT64_MAX; } SDL_Color sdl_color = {color.r, color.g, color.b, color.a}; @@ -124,7 +124,7 @@ int Window::create_text(int font_id, const char *text, uint32_t length, Color co if (surface == nullptr) { fprintf(stderr, "Failed to create text surface: %s\n", SDL_GetError()); - return UINT32_MAX; + return UINT64_MAX; } SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); @@ -132,7 +132,7 @@ int Window::create_text(int font_id, const char *text, uint32_t length, Color co if (texture == nullptr) { fprintf(stderr, "Failed to create text texture: %s\n", SDL_GetError()); - return UINT32_MAX; + return UINT64_MAX; } if (font_pool[font_id]->pixel_art) @@ -142,23 +142,24 @@ int Window::create_text(int font_id, const char *text, uint32_t length, Color co SDL_GetTextureSize(texture, &w, &h); Text *t = new Text{texture, {w, h}}; - int id = text_pool.acquire(t); + uint64_t id = text_pool.acquire(t); text_pool.use(id); return id; } -Vec2 Window::get_text_size(int text_id) { +Vec2 Window::get_text_size(uint64_t text_id) { if (!text_pool.is_valid(text_id)) { - fprintf(stderr, "Invalid text ID: %d\n", text_id); + fprintf(stderr, "(Size) Invalid text ID: %llu\n", (unsigned long long)text_id); return {0, 0}; + throw std::runtime_error("Invalid text ID"); } return text_pool[text_id]->size; } -bool Window::write(int text_id, Vec2 position, float z, float angle, Vec2 scale) { +bool Window::write(uint64_t text_id, Vec2 position, float z, float angle, Vec2 scale) { if (!text_pool.is_valid(text_id)) { - fprintf(stderr, "Invalid text ID: %d\n", text_id); + fprintf(stderr, "(Write) Invalid text ID: %llu\n", (unsigned long long)text_id); return false; } @@ -176,9 +177,9 @@ bool Window::write(int text_id, Vec2 position, float z, float angle, Vec2 return true; }; -bool Window::draw(int image_id, Vec2 position, float z, float angle, Vec2 scale) { +bool Window::draw(uint64_t image_id, Vec2 position, float z, float angle, Vec2 scale) { if (!image_pool.is_valid(image_id)) { - fprintf(stderr, "Invalid image ID: %d\n", image_id); + fprintf(stderr, "Invalid image ID: %llu\n", (unsigned long long)image_id); return false; } @@ -382,6 +383,10 @@ bool Window::poll(Event &event) { event.text_input.text[event.text_input.length] = '\0'; break; + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + event.type = Event::MOUSE_LEAVE; + break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: { event.type = Event::MOUSE_BUTTON_DOWN; event.mouse_button.button = sdl_event.button.button; @@ -389,7 +394,7 @@ bool Window::poll(Event &event) { if (mouse_in_window({sdl_event.button.x, sdl_event.button.y}, virtual_position)) event.mouse_button.position = virtual_position; else - return false; + event.type = Event::MOUSE_LEAVE; } break; case SDL_EVENT_MOUSE_BUTTON_UP: { @@ -399,17 +404,17 @@ bool Window::poll(Event &event) { if (mouse_in_window({sdl_event.button.x, sdl_event.button.y}, virtual_position)) event.mouse_button.position = virtual_position; else - return false; + event.type = Event::MOUSE_LEAVE; } break; case SDL_EVENT_MOUSE_MOTION: { event.type = Event::MOUSE_MOVE; event.mouse_move.delta = {sdl_event.motion.xrel, sdl_event.motion.yrel}; Vec2 virtual_position; - if (mouse_in_window({sdl_event.button.x, sdl_event.button.y}, virtual_position)) - event.mouse_button.position = virtual_position; + if (mouse_in_window({sdl_event.motion.x, sdl_event.motion.y}, virtual_position)) + event.mouse_move.position = virtual_position; else - return false; + event.type = Event::MOUSE_LEAVE; } break; case SDL_EVENT_MOUSE_WHEEL: